aboutsummaryrefslogtreecommitdiffstats
path: root/src/java
diff options
context:
space:
mode:
Diffstat (limited to 'src/java')
-rw-r--r--src/java/META-INF/services/org.apache.fop.render.pdf.PDFImageHandler5
-rw-r--r--src/java/META-INF/services/org.apache.xmlgraphics.image.loader.spi.ImageConverter2
-rw-r--r--src/java/META-INF/services/org.apache.xmlgraphics.image.loader.spi.ImageLoaderFactory2
-rw-r--r--src/java/META-INF/services/org.apache.xmlgraphics.image.loader.spi.ImagePreloader2
-rw-r--r--src/java/org/apache/fop/apps/FOURIResolver.java21
-rw-r--r--src/java/org/apache/fop/apps/FOUserAgent.java31
-rw-r--r--src/java/org/apache/fop/apps/FopFactory.java24
-rw-r--r--src/java/org/apache/fop/area/AreaTreeHandler.java22
-rw-r--r--src/java/org/apache/fop/area/AreaTreeParser.java33
-rw-r--r--src/java/org/apache/fop/area/Trait.java19
-rw-r--r--src/java/org/apache/fop/cli/CommandLineOptions.java48
-rw-r--r--src/java/org/apache/fop/cli/ImageInputHandler.java60
-rw-r--r--src/java/org/apache/fop/cli/InputHandler.java33
-rw-r--r--src/java/org/apache/fop/cli/image2fo.xsl45
-rw-r--r--src/java/org/apache/fop/fo/FOTreeBuilder.java15
-rw-r--r--src/java/org/apache/fop/fo/flow/ExternalGraphic.java43
-rw-r--r--src/java/org/apache/fop/fo/pagination/Root.java14
-rwxr-xr-xsrc/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java35
-rw-r--r--src/java/org/apache/fop/image/loader/batik/BatikUtil.java41
-rw-r--r--src/java/org/apache/fop/image/loader/batik/ImageConverterSVG2G2D.java120
-rw-r--r--src/java/org/apache/fop/image/loader/batik/ImageConverterWMF2G2D.java107
-rw-r--r--src/java/org/apache/fop/image/loader/batik/ImageLoaderFactorySVG.java63
-rw-r--r--src/java/org/apache/fop/image/loader/batik/ImageLoaderFactoryWMF.java62
-rw-r--r--src/java/org/apache/fop/image/loader/batik/ImageLoaderSVG.java78
-rw-r--r--src/java/org/apache/fop/image/loader/batik/ImageLoaderWMF.java70
-rw-r--r--src/java/org/apache/fop/image/loader/batik/ImageWMF.java69
-rw-r--r--src/java/org/apache/fop/image/loader/batik/PreloaderSVG.java182
-rw-r--r--src/java/org/apache/fop/image/loader/batik/PreloaderWMF.java142
-rw-r--r--src/java/org/apache/fop/image/loader/batik/package.html26
-rw-r--r--src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java105
-rw-r--r--src/java/org/apache/fop/layoutmgr/TraitSetter.java17
-rw-r--r--src/java/org/apache/fop/layoutmgr/inline/ImageLayout.java190
-rw-r--r--src/java/org/apache/fop/pdf/AlphaRasterImage.java181
-rw-r--r--src/java/org/apache/fop/pdf/BitmapImage.java27
-rw-r--r--src/java/org/apache/fop/pdf/FlateFilter.java8
-rw-r--r--src/java/org/apache/fop/pdf/PDFFactory.java3
-rw-r--r--src/java/org/apache/fop/pdf/PDFFilterList.java5
-rw-r--r--src/java/org/apache/fop/pdf/PDFICCStream.java4
-rw-r--r--src/java/org/apache/fop/pdf/PDFImage.java14
-rw-r--r--src/java/org/apache/fop/pdf/PDFImageXObject.java21
-rw-r--r--src/java/org/apache/fop/pdf/PDFObject.java26
-rw-r--r--src/java/org/apache/fop/pdf/PDFReference.java3
-rw-r--r--src/java/org/apache/fop/pdf/PDFText.java26
-rw-r--r--src/java/org/apache/fop/render/AbstractGenericSVGHandler.java26
-rw-r--r--src/java/org/apache/fop/render/AbstractGraphics2DAdapter.java6
-rw-r--r--src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java88
-rw-r--r--src/java/org/apache/fop/render/Graphics2DAdapter.java2
-rw-r--r--src/java/org/apache/fop/render/Graphics2DImagePainter.java21
-rw-r--r--src/java/org/apache/fop/render/afp/AFPGraphics2DAdapter.java3
-rw-r--r--src/java/org/apache/fop/render/afp/AFPRenderer.java184
-rw-r--r--src/java/org/apache/fop/render/java2d/Java2DGraphics2DAdapter.java32
-rw-r--r--src/java/org/apache/fop/render/java2d/Java2DRenderer.java141
-rw-r--r--src/java/org/apache/fop/render/pcl/PCLGraphics2DAdapter.java9
-rw-r--r--src/java/org/apache/fop/render/pcl/PCLRenderer.java255
-rw-r--r--src/java/org/apache/fop/render/pcl/PCLRendererContext.java17
-rw-r--r--src/java/org/apache/fop/render/pcl/PCLRendererContextConstants.java32
-rw-r--r--src/java/org/apache/fop/render/pcl/PCLSVGHandler.java8
-rw-r--r--src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java200
-rw-r--r--src/java/org/apache/fop/render/pdf/FopPDFImage.java374
-rw-r--r--src/java/org/apache/fop/render/pdf/ImageRawCCITTFaxAdapter.java110
-rw-r--r--src/java/org/apache/fop/render/pdf/ImageRawJPEGAdapter.java96
-rw-r--r--src/java/org/apache/fop/render/pdf/ImageRenderedAdapter.java249
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFGraphics2DAdapter.java3
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFImageHandler.java44
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFImageHandlerGraphics2D.java68
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFImageHandlerRawCCITTFax.java83
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFImageHandlerRawJPEG.java83
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFImageHandlerRegistry.java84
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFImageHandlerRenderedImage.java84
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFImageHandlerXML.java74
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFRenderer.java160
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFRendererConfigurator.java2
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFSVGHandler.java51
-rw-r--r--src/java/org/apache/fop/render/ps/ImageEncoderCCITTFax.java71
-rw-r--r--src/java/org/apache/fop/render/ps/ImageEncoderJPEG.java51
-rw-r--r--src/java/org/apache/fop/render/ps/PSGraphics2DAdapter.java34
-rw-r--r--src/java/org/apache/fop/render/ps/PSImageUtils.java100
-rw-r--r--src/java/org/apache/fop/render/ps/PSRenderer.java232
-rw-r--r--src/java/org/apache/fop/render/ps/PSSupportedFlavors.java67
-rw-r--r--src/java/org/apache/fop/render/ps/ResourceHandler.java149
-rw-r--r--src/java/org/apache/fop/render/rtf/RTFHandler.java188
-rw-r--r--src/java/org/apache/fop/render/rtf/rtflib/rtfdoc/RtfExternalGraphic.java26
-rw-r--r--src/java/org/apache/fop/svg/PDFBridgeContext.java45
-rw-r--r--src/java/org/apache/fop/svg/PDFGraphics2D.java17
-rw-r--r--src/java/org/apache/fop/svg/PDFImageElementBridge.java209
-rw-r--r--src/java/org/apache/fop/svg/PDFTranscoder.java57
-rw-r--r--src/java/org/apache/fop/util/dijkstra/DefaultEdgeDirectory.java109
-rw-r--r--src/java/org/apache/fop/util/dijkstra/DijkstraAlgorithm.java215
-rw-r--r--src/java/org/apache/fop/util/dijkstra/Edge.java47
-rw-r--r--src/java/org/apache/fop/util/dijkstra/EdgeDirectory.java45
-rw-r--r--src/java/org/apache/fop/util/dijkstra/Vertex.java32
91 files changed, 4855 insertions, 1472 deletions
diff --git a/src/java/META-INF/services/org.apache.fop.render.pdf.PDFImageHandler b/src/java/META-INF/services/org.apache.fop.render.pdf.PDFImageHandler
new file mode 100644
index 000000000..b72796c2d
--- /dev/null
+++ b/src/java/META-INF/services/org.apache.fop.render.pdf.PDFImageHandler
@@ -0,0 +1,5 @@
+org.apache.fop.render.pdf.PDFImageHandlerRawJPEG
+org.apache.fop.render.pdf.PDFImageHandlerRawCCITTFax
+org.apache.fop.render.pdf.PDFImageHandlerGraphics2D
+org.apache.fop.render.pdf.PDFImageHandlerRenderedImage
+org.apache.fop.render.pdf.PDFImageHandlerXML \ No newline at end of file
diff --git a/src/java/META-INF/services/org.apache.xmlgraphics.image.loader.spi.ImageConverter b/src/java/META-INF/services/org.apache.xmlgraphics.image.loader.spi.ImageConverter
new file mode 100644
index 000000000..c3c688a2c
--- /dev/null
+++ b/src/java/META-INF/services/org.apache.xmlgraphics.image.loader.spi.ImageConverter
@@ -0,0 +1,2 @@
+org.apache.fop.image.loader.batik.ImageConverterSVG2G2D
+org.apache.fop.image.loader.batik.ImageConverterWMF2G2D
diff --git a/src/java/META-INF/services/org.apache.xmlgraphics.image.loader.spi.ImageLoaderFactory b/src/java/META-INF/services/org.apache.xmlgraphics.image.loader.spi.ImageLoaderFactory
new file mode 100644
index 000000000..b607a5e64
--- /dev/null
+++ b/src/java/META-INF/services/org.apache.xmlgraphics.image.loader.spi.ImageLoaderFactory
@@ -0,0 +1,2 @@
+org.apache.fop.image.loader.batik.ImageLoaderFactorySVG
+org.apache.fop.image.loader.batik.ImageLoaderFactoryWMF \ No newline at end of file
diff --git a/src/java/META-INF/services/org.apache.xmlgraphics.image.loader.spi.ImagePreloader b/src/java/META-INF/services/org.apache.xmlgraphics.image.loader.spi.ImagePreloader
new file mode 100644
index 000000000..c64d899b7
--- /dev/null
+++ b/src/java/META-INF/services/org.apache.xmlgraphics.image.loader.spi.ImagePreloader
@@ -0,0 +1,2 @@
+org.apache.fop.image.loader.batik.PreloaderWMF
+org.apache.fop.image.loader.batik.PreloaderSVG
diff --git a/src/java/org/apache/fop/apps/FOURIResolver.java b/src/java/org/apache/fop/apps/FOURIResolver.java
index d68905e4c..f38be542a 100644
--- a/src/java/org/apache/fop/apps/FOURIResolver.java
+++ b/src/java/org/apache/fop/apps/FOURIResolver.java
@@ -32,13 +32,13 @@ import javax.xml.transform.TransformerException;
import javax.xml.transform.URIResolver;
import javax.xml.transform.stream.StreamSource;
-// commons logging
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.apache.fop.util.DataURIResolver;
import org.apache.xmlgraphics.util.io.Base64EncodeStream;
+import org.apache.fop.util.DataURIResolver;
+
/**
* Provides FOP specific URI resolution. This is the default URIResolver
* {@link FOUserAgent} will use unless overidden.
@@ -132,10 +132,23 @@ public class FOURIResolver implements javax.xml.transform.URIResolver {
// Fallback to default resolution mechanism
if (source == null) {
URL absoluteURL = null;
- File file = new File(href);
+ int hashPos = href.indexOf('#');
+ String fileURL, fragment;
+ if (hashPos >= 0) {
+ fileURL = href.substring(0, hashPos);
+ fragment = href.substring(hashPos);
+ } else {
+ fileURL = href;
+ fragment = null;
+ }
+ File file = new File(fileURL);
if (file.canRead() && file.isFile()) {
try {
- absoluteURL = file.toURL();
+ if (fragment != null) {
+ absoluteURL = new URL(file.toURL().toExternalForm() + fragment);
+ } else {
+ absoluteURL = file.toURL();
+ }
} catch (MalformedURLException mfue) {
handleException(mfue, "Could not convert filename '" + href
+ "' to URL", throwExceptions);
diff --git a/src/java/org/apache/fop/apps/FOUserAgent.java b/src/java/org/apache/fop/apps/FOUserAgent.java
index 07928fef2..966e227fe 100644
--- a/src/java/org/apache/fop/apps/FOUserAgent.java
+++ b/src/java/org/apache/fop/apps/FOUserAgent.java
@@ -23,15 +23,18 @@ package org.apache.fop.apps;
import java.io.File;
import java.util.Date;
import java.util.Map;
+
import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import javax.xml.transform.URIResolver;
-// commons logging
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-// FOP
+import org.apache.xmlgraphics.image.loader.ImageContext;
+import org.apache.xmlgraphics.image.loader.ImageSessionContext;
+import org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext;
+
import org.apache.fop.Version;
import org.apache.fop.fo.FOEventHandler;
import org.apache.fop.pdf.PDFEncryptionParams;
@@ -110,6 +113,22 @@ public class FOUserAgent {
/** Set of keywords applicable to this document. */
protected String keywords = null;
+ private ImageSessionContext imageSessionContext = new AbstractImageSessionContext() {
+
+ public ImageContext getParentContext() {
+ return getFactory();
+ }
+
+ public float getTargetResolution() {
+ return FOUserAgent.this.getTargetResolution();
+ }
+
+ public Source resolveURI(String uri) {
+ return FOUserAgent.this.resolveURI(uri);
+ }
+
+ };
+
/**
* Default constructor
* @see org.apache.fop.apps.FopFactory
@@ -442,6 +461,14 @@ public class FOUserAgent {
setTargetResolution((float)dpi);
}
+ /**
+ * Returns the image session context for the image package.
+ * @return the ImageSessionContext instance for this rendering run
+ */
+ public ImageSessionContext getImageSessionContext() {
+ return this.imageSessionContext;
+ }
+
// ---------------------------------------------- environment-level stuff
// (convenience access to FopFactory methods)
diff --git a/src/java/org/apache/fop/apps/FopFactory.java b/src/java/org/apache/fop/apps/FopFactory.java
index c87a3041b..dc94d92fb 100644
--- a/src/java/org/apache/fop/apps/FopFactory.java
+++ b/src/java/org/apache/fop/apps/FopFactory.java
@@ -24,6 +24,7 @@ import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.MalformedURLException;
+import java.net.URISyntaxException;
import java.net.URL;
import java.util.Collection;
import java.util.Collections;
@@ -36,10 +37,12 @@ import javax.xml.transform.URIResolver;
import org.xml.sax.SAXException;
import org.apache.avalon.framework.configuration.Configuration;
-
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.xmlgraphics.image.loader.ImageContext;
+import org.apache.xmlgraphics.image.loader.ImageManager;
+
import org.apache.fop.fo.ElementMapping;
import org.apache.fop.fo.ElementMappingRegistry;
import org.apache.fop.fonts.FontCache;
@@ -57,7 +60,7 @@ import org.apache.fop.util.ContentHandlerFactoryRegistry;
* Information that may potentially be different for each rendering run can be
* found and managed in the FOUserAgent.
*/
-public class FopFactory {
+public class FopFactory implements ImageContext {
/** logger instance */
private static Log log = LogFactory.getLog(FopFactory.class);
@@ -83,6 +86,9 @@ public class FopFactory {
/** Image factory for creating fop image objects */
private ImageFactory imageFactory;
+ /** Image manager for loading and caching image objects */
+ private ImageManager imageManager;
+
/** Configuration layer used to configure fop */
private FopFactoryConfigurator config = null;
@@ -150,7 +156,8 @@ public class FopFactory {
this.elementMappingRegistry = new ElementMappingRegistry(this);
this.foURIResolver = new FOURIResolver(validateUserConfigStrictly());
this.colorSpaceCache = new ColorSpaceCache(foURIResolver);
- this.imageFactory = new ImageFactory();
+ this.imageFactory = new ImageFactory();
+ this.imageManager = new ImageManager(this);
this.rendererFactory = new RendererFactory();
this.xmlHandlers = new XMLHandlerRegistry();
this.ignoredNamespaces = new java.util.HashSet();
@@ -293,6 +300,14 @@ public class FopFactory {
}
/**
+ * Returns the image manager.
+ * @return the image manager
+ */
+ public ImageManager getImageManager() {
+ return this.imageManager;
+ }
+
+ /**
* Add the element mapping with the given class name.
* @param elementMapping the class name representing the element mapping.
*/
@@ -748,5 +763,6 @@ public class FopFactory {
*/
public ColorSpace getColorSpace(String baseUri, String iccProfileSrc) {
return colorSpaceCache.get(baseUri, iccProfileSrc);
- }
+ }
+
}
diff --git a/src/java/org/apache/fop/area/AreaTreeHandler.java b/src/java/org/apache/fop/area/AreaTreeHandler.java
index a107da833..7454f4667 100644
--- a/src/java/org/apache/fop/area/AreaTreeHandler.java
+++ b/src/java/org/apache/fop/area/AreaTreeHandler.java
@@ -189,12 +189,12 @@ public class AreaTreeHandler extends FOEventHandler {
}
}
- /**
- * {@inheritDoc}
- * @param pageSequence
- * is the pageSequence being started
- */
+ /** {@inheritDoc} */
public void startPageSequence(PageSequence pageSequence) {
+ startAbstractPageSequence(pageSequence);
+ }
+
+ private void startAbstractPageSequence(AbstractPageSequence pageSequence) {
rootFObj = pageSequence.getRoot();
finishPrevPageSequence(pageSequence.getInitialPageNumber());
pageSequence.initPageNumber();
@@ -238,18 +238,12 @@ public class AreaTreeHandler extends FOEventHandler {
}
}
- /**
- * @see org.apache.fop.fo.FOEventHandler#startExternalDocument(org.apache.fop.fo.extensions.ExternalDocument)
- */
+ /** {@inheritDoc} */
public void startExternalDocument(ExternalDocument document) {
- rootFObj = document.getRoot();
- finishPrevPageSequence(document.getInitialPageNumber());
- document.initPageNumber();
+ startAbstractPageSequence(document);
}
- /**
- * @see org.apache.fop.fo.FOEventHandler#endExternalDocument(org.apache.fop.fo.extensions.ExternalDocument)
- */
+ /** {@inheritDoc} */
public void endExternalDocument(ExternalDocument document) {
if (statistics != null) {
statistics.end();
diff --git a/src/java/org/apache/fop/area/AreaTreeParser.java b/src/java/org/apache/fop/area/AreaTreeParser.java
index b4a804712..d4cdf5239 100644
--- a/src/java/org/apache/fop/area/AreaTreeParser.java
+++ b/src/java/org/apache/fop/area/AreaTreeParser.java
@@ -47,6 +47,10 @@ import org.xml.sax.helpers.DefaultHandler;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.xmlgraphics.image.loader.ImageInfo;
+import org.apache.xmlgraphics.image.loader.ImageManager;
+import org.apache.xmlgraphics.image.loader.ImageSessionContext;
+
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.area.Trait.Background;
import org.apache.fop.area.Trait.InternalLink;
@@ -69,8 +73,6 @@ import org.apache.fop.fo.expr.PropertyException;
import org.apache.fop.fo.extensions.ExtensionAttachment;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontInfo;
-import org.apache.fop.image.FopImage;
-import org.apache.fop.image.ImageFactory;
import org.apache.fop.traits.BorderProps;
import org.apache.fop.util.ColorUtil;
import org.apache.fop.util.ContentHandlerFactory;
@@ -1048,22 +1050,19 @@ public class AreaTreeParser {
} catch (PropertyException e) {
throw new IllegalArgumentException(e.getMessage());
}
- String url = attributes.getValue("bkg-img");
- if (url != null) {
- bkg.setURL(url);
-
- ImageFactory fact = userAgent.getFactory().getImageFactory();
- FopImage img = fact.getImage(url, userAgent);
- if (img == null) {
- log.error("Background image not available: " + url);
- } else {
- // load dimensions
- if (!img.load(FopImage.DIMENSIONS)) {
- log.error("Cannot read background image dimensions: "
- + url);
- }
+ String uri = attributes.getValue("bkg-img");
+ if (uri != null) {
+ bkg.setURL(uri);
+
+ try {
+ ImageManager manager = userAgent.getFactory().getImageManager();
+ ImageSessionContext sessionContext
+ = userAgent.getImageSessionContext();
+ ImageInfo info = manager.getImageInfo(uri, sessionContext);
+ bkg.setImageInfo(info);
+ } catch (Exception e) {
+ log.error("Background image not available: " + uri, e);
}
- bkg.setFopImage(img);
String repeat = attributes.getValue("bkg-repeat");
if (repeat != null) {
diff --git a/src/java/org/apache/fop/area/Trait.java b/src/java/org/apache/fop/area/Trait.java
index b3c3d3947..98bed098c 100644
--- a/src/java/org/apache/fop/area/Trait.java
+++ b/src/java/org/apache/fop/area/Trait.java
@@ -22,9 +22,10 @@ package org.apache.fop.area;
import java.awt.Color;
import java.io.Serializable;
+import org.apache.xmlgraphics.image.loader.ImageInfo;
+
import org.apache.fop.fo.Constants;
import org.apache.fop.fonts.FontTriplet;
-import org.apache.fop.image.FopImage;
import org.apache.fop.traits.BorderProps;
import org.apache.fop.util.ColorUtil;
@@ -559,7 +560,7 @@ public class Trait implements Serializable {
private String url = null;
/** The background image if any. */
- private FopImage fopimage = null;
+ private ImageInfo imageInfo = null;
/** Background repeat enum for images. */
private int repeat;
@@ -603,11 +604,11 @@ public class Trait implements Serializable {
}
/**
- * Returns the FopImage representing the background image
+ * Returns the ImageInfo object representing the background image
* @return the background image, null if n/a
*/
- public FopImage getFopImage() {
- return fopimage;
+ public ImageInfo getImageInfo() {
+ return imageInfo;
}
/**
@@ -659,11 +660,11 @@ public class Trait implements Serializable {
}
/**
- * Sets the FopImage to use as the background image.
- * @param fopimage The FopImage to use
+ * Sets the ImageInfo of the image to use as the background image.
+ * @param info The background image's info object
*/
- public void setFopImage(FopImage fopimage) {
- this.fopimage = fopimage;
+ public void setImageInfo(ImageInfo info) {
+ this.imageInfo = info;
}
/**
diff --git a/src/java/org/apache/fop/cli/CommandLineOptions.java b/src/java/org/apache/fop/cli/CommandLineOptions.java
index 63a817071..3bb0aae7f 100644
--- a/src/java/org/apache/fop/cli/CommandLineOptions.java
+++ b/src/java/org/apache/fop/cli/CommandLineOptions.java
@@ -29,6 +29,11 @@ import java.util.Vector;
import javax.swing.UIManager;
+import org.xml.sax.SAXException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
import org.apache.fop.Version;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
@@ -38,19 +43,12 @@ import org.apache.fop.pdf.PDFAMode;
import org.apache.fop.pdf.PDFEncryptionManager;
import org.apache.fop.pdf.PDFEncryptionParams;
import org.apache.fop.pdf.PDFXMode;
-import org.apache.fop.render.awt.AWTRenderer;
import org.apache.fop.render.Renderer;
+import org.apache.fop.render.awt.AWTRenderer;
import org.apache.fop.render.pdf.PDFRenderer;
import org.apache.fop.render.xml.XMLRenderer;
import org.apache.fop.util.CommandLineLogger;
-// commons logging
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-// SAX
-import org.xml.sax.SAXException;
-
/**
* Options parses the commandline arguments
*/
@@ -71,6 +69,8 @@ public class CommandLineOptions {
public static final int XSLT_INPUT = 2;
/** input: Area Tree XML file */
public static final int AREATREE_INPUT = 3;
+ /** input: Image file */
+ public static final int IMAGE_INPUT = 4;
/* show configuration information */
private Boolean showConfiguration = Boolean.FALSE;
@@ -86,6 +86,8 @@ public class CommandLineOptions {
private File xmlfile = null;
/* area tree input file */
private File areatreefile = null;
+ /* area tree input file */
+ private File imagefile = null;
/* output file */
private File outfile = null;
/* input mode */
@@ -249,6 +251,8 @@ public class CommandLineOptions {
i = i + parseXMLInputOption(args, i);
} else if (args[i].equals("-atin")) {
i = i + parseAreaTreeInputOption(args, i);
+ } else if (args[i].equals("-imagein")) {
+ i = i + parseImageInputOption(args, i);
} else if (args[i].equals("-awt")) {
i = i + parseAWTOutputOption(args, i);
} else if (args[i].equals("-pdf")) {
@@ -594,6 +598,17 @@ public class CommandLineOptions {
}
}
+ private int parseImageInputOption(String[] args, int i) throws FOPException {
+ inputmode = IMAGE_INPUT;
+ if ((i + 1 == args.length)
+ || (args[i + 1].charAt(0) == '-')) {
+ throw new FOPException("you must specify the image file for the '-imagein' option");
+ } else {
+ imagefile = new File(args[i + 1]);
+ return 1;
+ }
+ }
+
private PDFEncryptionParams getPDFEncryptionParams() throws FOPException {
PDFEncryptionParams params = (PDFEncryptionParams)renderingOptions.get(
PDFRenderer.ENCRYPTION_PARAMS);
@@ -768,6 +783,20 @@ public class CommandLineOptions {
+ areatreefile.getAbsolutePath()
+ " not found ");
}
+ } else if (inputmode == IMAGE_INPUT) {
+ if (outputmode.equals(MimeConstants.MIME_XSL_FO)) {
+ throw new FOPException(
+ "FO output mode is only available if you use -xml and -xsl");
+ }
+ if (xmlfile != null) {
+ log.warn("image input mode, but XML file is set:");
+ log.error("XML file: " + xmlfile.toString());
+ }
+ if (!imagefile.exists()) {
+ throw new FileNotFoundException("Error: image file "
+ + imagefile.getAbsolutePath()
+ + " not found ");
+ }
}
} // end checkSettings
@@ -814,6 +843,8 @@ public class CommandLineOptions {
return new AreaTreeInputHandler(areatreefile);
case XSLT_INPUT:
return new InputHandler(xmlfile, xsltfile, xsltParams);
+ case IMAGE_INPUT:
+ return new ImageInputHandler(imagefile, xsltfile, xsltParams);
default:
throw new IllegalArgumentException("Error creating InputHandler object.");
}
@@ -920,6 +951,7 @@ public class CommandLineOptions {
+ " -fo infile xsl:fo input file \n"
+ " -xml infile xml input file, must be used together with -xsl \n"
+ " -atin infile area tree input file \n"
+ + " -imagein infile image input file \n"
+ " -xsl stylesheet xslt stylesheet \n \n"
+ " -param name value <value> to use for parameter <name> in xslt stylesheet\n"
+ " (repeat '-param name value' for each parameter)\n \n"
diff --git a/src/java/org/apache/fop/cli/ImageInputHandler.java b/src/java/org/apache/fop/cli/ImageInputHandler.java
new file mode 100644
index 000000000..fe3d5021f
--- /dev/null
+++ b/src/java/org/apache/fop/cli/ImageInputHandler.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.cli;
+
+import java.io.File;
+import java.io.StringReader;
+import java.util.Vector;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamSource;
+
+/**
+ * InputHandler for the images (for example TIFF) as input.
+ */
+public class ImageInputHandler extends InputHandler {
+
+ /**
+ * Main constructor.
+ * @param imagefile the image file
+ * @param xsltfile XSLT file (may be null in which case the default stylesheet is used)
+ * @param params Vector of command-line parameters (name, value,
+ * name, value, ...) for XSL stylesheet, null if none
+ */
+ public ImageInputHandler(File imagefile, File xsltfile, Vector params) {
+ super(imagefile, xsltfile, params);
+ }
+
+ /** {@inheritDoc} */
+ protected Source createMainSource() {
+ return new StreamSource(new StringReader(
+ "<image>" + this.sourcefile.toURI().toASCIIString() + "</image>"));
+ }
+
+ /** {@inheritDoc} */
+ protected Source createXSLTSource() {
+ Source src = super.createXSLTSource();
+ if (src == null) {
+ src = new StreamSource(getClass().getResource("image2fo.xsl").toExternalForm());
+ }
+ return src;
+ }
+
+}
diff --git a/src/java/org/apache/fop/cli/InputHandler.java b/src/java/org/apache/fop/cli/InputHandler.java
index bfbce269d..89977beb7 100644
--- a/src/java/org/apache/fop/cli/InputHandler.java
+++ b/src/java/org/apache/fop/cli/InputHandler.java
@@ -24,7 +24,6 @@ import java.io.File;
import java.io.OutputStream;
import java.util.Vector;
-// Imported TraX classes
import javax.xml.transform.ErrorListener;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
@@ -37,6 +36,7 @@ import javax.xml.transform.stream.StreamSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.Fop;
@@ -50,7 +50,8 @@ import org.apache.fop.render.awt.viewer.Renderable;
*/
public class InputHandler implements ErrorListener, Renderable {
- private File sourcefile = null; // either FO or XML/XSLT usage
+ /** original source file */
+ protected File sourcefile = null;
private File stylesheet = null; // for XML/XSLT usage
private Vector xsltParams = null; // for XML/XSLT usage
@@ -132,6 +133,26 @@ public class InputHandler implements ErrorListener, Renderable {
}
/**
+ * Creates a Source for the main input file.
+ * @return the Source for the main input file
+ */
+ protected Source createMainSource() {
+ return new StreamSource(this.sourcefile);
+ }
+
+ /**
+ * Creates a Source for the selected stylesheet.
+ * @return the Source for the selected stylesheet or null if there's no stylesheet
+ */
+ protected Source createXSLTSource() {
+ if (this.stylesheet != null) {
+ return new StreamSource(this.stylesheet);
+ } else {
+ return null;
+ }
+ }
+
+ /**
* Transforms the input document to the input format expected by FOP using XSLT.
* @param result the Result object where the result of the XSL transformation is sent to
* @throws FOPException in case of an error during processing
@@ -142,11 +163,11 @@ public class InputHandler implements ErrorListener, Renderable {
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer;
- if (stylesheet == null) { // FO Input
+ Source xsltSource = createXSLTSource();
+ if (xsltSource == null) { // FO Input
transformer = factory.newTransformer();
} else { // XML/XSLT input
- transformer = factory.newTransformer(new StreamSource(
- stylesheet));
+ transformer = factory.newTransformer(xsltSource);
// Set the value of parameters, if any, defined for stylesheet
if (xsltParams != null) {
@@ -159,7 +180,7 @@ public class InputHandler implements ErrorListener, Renderable {
transformer.setErrorListener(this);
// Create a SAXSource from the input Source file
- Source src = new StreamSource(sourcefile);
+ Source src = createMainSource();
// Start XSLT transformation and FOP processing
transformer.transform(src, result);
diff --git a/src/java/org/apache/fop/cli/image2fo.xsl b/src/java/org/apache/fop/cli/image2fo.xsl
new file mode 100644
index 000000000..3a2610230
--- /dev/null
+++ b/src/java/org/apache/fop/cli/image2fo.xsl
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<xsl:stylesheet
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:fo="http://www.w3.org/1999/XSL/Format"
+ xmlns:fox="http://xmlgraphics.apache.org/fop/extensions"
+ version="1.0">
+
+ <xsl:output method="xml" indent="yes"/>
+
+ <xsl:template name="masters">
+ <fo:layout-master-set>
+ <!-- not really used but mandatory -->
+ <fo:simple-page-master master-name="dummy" page-height="29.7cm" page-width="21cm">
+ <fo:region-body/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ </xsl:template>
+
+ <xsl:template match="/image">
+ <fo:root>
+ <xsl:call-template name="masters"/>
+ <fox:external-document>
+ <xsl:attribute name="src"><xsl:value-of select="."/></xsl:attribute>
+ </fox:external-document>
+ </fo:root>
+ </xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/java/org/apache/fop/fo/FOTreeBuilder.java b/src/java/org/apache/fop/fo/FOTreeBuilder.java
index 980404ffd..63fc5cb5b 100644
--- a/src/java/org/apache/fop/fo/FOTreeBuilder.java
+++ b/src/java/org/apache/fop/fo/FOTreeBuilder.java
@@ -276,9 +276,7 @@ public class FOTreeBuilder extends DefaultHandler {
*/
private int nestedMarkerDepth = 0;
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void startElement(String namespaceURI, String localName, String rawName,
Attributes attlist) throws SAXException {
@@ -359,19 +357,16 @@ public class FOTreeBuilder extends DefaultHandler {
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void endElement(String uri, String localName, String rawName)
throws SAXException {
if (currentFObj == null) {
- throw new IllegalStateException(
+ throw new SAXException(
"endElement() called for " + rawName
+ " where there is no current element.");
- } else
- if (!currentFObj.getLocalName().equals(localName)
+ } else if (!currentFObj.getLocalName().equals(localName)
|| !currentFObj.getNamespaceURI().equals(uri)) {
- log.warn("Mismatch: " + currentFObj.getLocalName()
+ throw new SAXException("Mismatch: " + currentFObj.getLocalName()
+ " (" + currentFObj.getNamespaceURI()
+ ") vs. " + localName + " (" + uri + ")");
}
diff --git a/src/java/org/apache/fop/fo/flow/ExternalGraphic.java b/src/java/org/apache/fop/fo/flow/ExternalGraphic.java
index f0285e6f4..dd45f585c 100644
--- a/src/java/org/apache/fop/fo/flow/ExternalGraphic.java
+++ b/src/java/org/apache/fop/fo/flow/ExternalGraphic.java
@@ -19,15 +19,22 @@
package org.apache.fop.fo.flow;
+import java.io.IOException;
+
+import org.xml.sax.Locator;
+
+import org.apache.xmlgraphics.image.loader.ImageException;
+import org.apache.xmlgraphics.image.loader.ImageInfo;
+import org.apache.xmlgraphics.image.loader.ImageManager;
+
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.datatypes.Length;
+import org.apache.fop.datatypes.URISpecification;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.ValidationException;
-import org.apache.fop.image.FopImage;
-import org.apache.fop.image.ImageFactory;
-import org.xml.sax.Locator;
+import org.apache.fop.fo.properties.FixedLength;
/**
* Class modelling the fo:external-graphic object.
@@ -63,21 +70,25 @@ public class ExternalGraphic extends AbstractGraphics {
super.bind(pList);
src = pList.get(PR_SRC).getString();
- //Additional processing: preload image
- url = ImageFactory.getURL(getSrc());
+ //Additional processing: obtain the image's intrinsic size and baseline information
+ url = URISpecification.getURL(src);
FOUserAgent userAgent = getUserAgent();
- ImageFactory fact = userAgent.getFactory().getImageFactory();
- FopImage fopimage = fact.getImage(url, userAgent);
- if (fopimage == null) {
- log.error("Image not available: " + getSrc());
- } else {
- // load dimensions
- if (!fopimage.load(FopImage.DIMENSIONS)) {
- log.error("Cannot read image dimensions: " + getSrc());
+ ImageManager manager = userAgent.getFactory().getImageManager();
+ ImageInfo info = null;
+ try {
+ info = manager.getImageInfo(url, userAgent.getImageSessionContext());
+ } catch (ImageException e) {
+ log.error("Image not available: " + e.getMessage());
+ } catch (IOException ioe) {
+ log.error("I/O error while loading image: " + ioe.getMessage());
+ }
+ if (info != null) {
+ this.intrinsicWidth = info.getSize().getWidthMpt();
+ this.intrinsicHeight = info.getSize().getHeightMpt();
+ int baseline = info.getSize().getBaselinePositionFromBottom();
+ if (baseline != 0) {
+ this.intrinsicAlignmentAdjust = new FixedLength(-baseline);
}
- this.intrinsicWidth = fopimage.getIntrinsicWidth();
- this.intrinsicHeight = fopimage.getIntrinsicHeight();
- this.intrinsicAlignmentAdjust = fopimage.getIntrinsicAlignmentAdjust();
}
//TODO Report to caller so he can decide to throw an exception
}
diff --git a/src/java/org/apache/fop/fo/pagination/Root.java b/src/java/org/apache/fop/fo/pagination/Root.java
index 139f7606a..6e079cf47 100644
--- a/src/java/org/apache/fop/fo/pagination/Root.java
+++ b/src/java/org/apache/fop/fo/pagination/Root.java
@@ -31,8 +31,8 @@ import org.apache.fop.fo.FONode;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.ValidationException;
-import org.apache.fop.fo.pagination.bookmarks.BookmarkTree;
import org.apache.fop.fo.extensions.destination.Destination;
+import org.apache.fop.fo.pagination.bookmarks.BookmarkTree;
/**
* The fo:root formatting object. Contains page masters, page-sequences.
@@ -73,20 +73,16 @@ public class Root extends FObj {
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
mediaUsage = pList.get(PR_MEDIA_USAGE).getEnum();
}
- /**
- * Signal end of this xml element.
- */
+ /** {@inheritDoc} */
protected void endOfNode() throws FOPException {
if (!pageSequenceFound || layoutMasterSet == null) {
- missingChildElementError("(layout-master-set, declarations?, " +
- "bookmark-tree?, (page-sequence+|fox:external-document))");
+ missingChildElementError("(layout-master-set, declarations?, "
+ + "bookmark-tree?, (page-sequence|fox:external-document)+)");
}
}
diff --git a/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java b/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java
index 866a020e2..68d4bcab1 100755
--- a/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java
+++ b/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java
@@ -21,13 +21,16 @@ package org.apache.fop.fo.properties;
import java.awt.Color;
+import org.apache.xmlgraphics.image.loader.ImageInfo;
+import org.apache.xmlgraphics.image.loader.ImageManager;
+import org.apache.xmlgraphics.image.loader.ImageSessionContext;
+
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.datatypes.Length;
import org.apache.fop.datatypes.PercentBaseContext;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.expr.PropertyException;
-import org.apache.fop.image.FopImage;
import org.apache.fop.image.ImageFactory;
/**
@@ -66,7 +69,7 @@ public class CommonBorderPaddingBackground {
public Length backgroundPositionVertical;
- private FopImage fopimage;
+ private ImageInfo backgroundImageInfo;
/** the "before" edge */
@@ -232,18 +235,16 @@ public class CommonBorderPaddingBackground {
Constants.PR_BACKGROUND_POSITION_VERTICAL).getLength();
//Additional processing: preload image
- String url = ImageFactory.getURL(backgroundImage);
+ String uri = ImageFactory.getURL(backgroundImage);
FOUserAgent userAgent = pList.getFObj().getUserAgent();
- ImageFactory fact = userAgent.getFactory().getImageFactory();
- fopimage = fact.getImage(url, userAgent);
- if (fopimage == null) {
- Property.log.error("Background image not available: " + backgroundImage);
- } else {
- // load dimensions
- if (!fopimage.load(FopImage.DIMENSIONS)) {
- Property.log.error("Cannot read background image dimensions: "
- + backgroundImage);
- }
+ ImageManager manager = userAgent.getFactory().getImageManager();
+ ImageSessionContext sessionContext = userAgent.getImageSessionContext();
+ ImageInfo info;
+ try {
+ info = manager.getImageInfo(uri, sessionContext);
+ this.backgroundImageInfo = info;
+ } catch (Exception e) {
+ Property.log.error("Background image not available: " + uri);
}
//TODO Report to caller so he can decide to throw an exception
}
@@ -315,11 +316,11 @@ public class CommonBorderPaddingBackground {
}
/**
- * @return the background image as a preloaded FopImage, null if there is
+ * @return the background image info object, null if there is
* no background image.
*/
- public FopImage getFopImage() {
- return this.fopimage;
+ public ImageInfo getImageInfo() {
+ return this.backgroundImageInfo;
}
/**
@@ -455,7 +456,7 @@ public class CommonBorderPaddingBackground {
* @return true if there is any kind of background to be painted
*/
public boolean hasBackground() {
- return ((backgroundColor != null || getFopImage() != null));
+ return ((backgroundColor != null || getImageInfo() != null));
}
/** @return true if border is non-zero. */
diff --git a/src/java/org/apache/fop/image/loader/batik/BatikUtil.java b/src/java/org/apache/fop/image/loader/batik/BatikUtil.java
new file mode 100644
index 000000000..1ae2a9917
--- /dev/null
+++ b/src/java/org/apache/fop/image/loader/batik/BatikUtil.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.image.loader.batik;
+
+/**
+ * Helper utilities for Apache Batik.
+ */
+public class BatikUtil {
+
+ /**
+ * Checks whether Apache Batik is available in the classpath.
+ * @return true if Apache Batik is available
+ */
+ public static boolean isBatikAvailable() {
+ try {
+ Class.forName("org.apache.batik.dom.svg.SVGDOMImplementation");
+ return true;
+ } catch (Exception e) {
+ //ignore
+ }
+ return false;
+ }
+
+}
diff --git a/src/java/org/apache/fop/image/loader/batik/ImageConverterSVG2G2D.java b/src/java/org/apache/fop/image/loader/batik/ImageConverterSVG2G2D.java
new file mode 100644
index 000000000..81b3b4c07
--- /dev/null
+++ b/src/java/org/apache/fop/image/loader/batik/ImageConverterSVG2G2D.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.image.loader.batik;
+
+import java.awt.Dimension;
+import java.awt.Graphics2D;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
+import java.util.Map;
+
+import org.apache.batik.bridge.BridgeContext;
+import org.apache.batik.bridge.GVTBuilder;
+import org.apache.batik.dom.svg.SVGDOMImplementation;
+import org.apache.batik.gvt.GraphicsNode;
+
+import org.apache.xmlgraphics.image.loader.Image;
+import org.apache.xmlgraphics.image.loader.ImageException;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.ImageProcessingHints;
+import org.apache.xmlgraphics.image.loader.impl.AbstractImageConverter;
+import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D;
+import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM;
+import org.apache.xmlgraphics.java2d.Graphics2DImagePainter;
+import org.apache.xmlgraphics.util.UnitConv;
+
+import org.apache.fop.svg.SVGUserAgent;
+
+/**
+ * This ImageConverter converts SVG images to Java2D.
+ * <p>
+ * Note: The target flavor is "generic" Java2D. No Batik-specific bridges are hooked into the
+ * conversion process. Specialized renderers may want to provide specialized adapters to profit
+ * from target-format features (for example with PDF or PS). This converter is mainly for formats
+ * which only support bitmap images or rudimentary Java2D support.
+ */
+public class ImageConverterSVG2G2D extends AbstractImageConverter {
+
+ /** {@inheritDoc} */
+ public Image convert(Image src, Map hints) throws ImageException {
+ checkSourceFlavor(src);
+ final ImageXMLDOM svg = (ImageXMLDOM)src;
+ if (!SVGDOMImplementation.SVG_NAMESPACE_URI.equals(svg.getRootNamespace())) {
+ throw new IllegalArgumentException("XML DOM is not in the SVG namespace: "
+ + svg.getRootNamespace());
+ }
+
+ //Prepare
+ float pxToMillimeter = (float)UnitConv.mm2in(72); //default: 72dpi
+ Number ptm = (Number)hints.get(ImageProcessingHints.SOURCE_RESOLUTION);
+ if (ptm != null) {
+ pxToMillimeter = (float)UnitConv.mm2in(ptm.doubleValue());
+ }
+ SVGUserAgent ua = new SVGUserAgent(
+ pxToMillimeter,
+ new AffineTransform());
+ GVTBuilder builder = new GVTBuilder();
+ final BridgeContext ctx = new BridgeContext(ua);
+
+ //Build the GVT tree
+ final GraphicsNode root;
+ try {
+ root = builder.build(ctx, svg.getDocument());
+ } catch (Exception e) {
+ throw new ImageException("GVT tree could not be built for SVG graphic", e);
+ }
+
+ //Create the painter
+ Graphics2DImagePainter painter = new Graphics2DImagePainter() {
+
+ public void paint(Graphics2D g2d, Rectangle2D area) {
+ // If no viewbox is defined in the svg file, a viewbox of 100x100 is
+ // assumed, as defined in SVGUserAgent.getViewportSize()
+ float iw = (float) ctx.getDocumentSize().getWidth();
+ float ih = (float) ctx.getDocumentSize().getHeight();
+ float w = (float) area.getWidth();
+ float h = (float) area.getHeight();
+ g2d.translate(area.getX(), area.getY());
+ g2d.scale(w / iw, h / ih);
+
+ root.paint(g2d);
+ }
+
+ public Dimension getImageSize() {
+ return new Dimension(svg.getSize().getWidthMpt(), svg.getSize().getHeightMpt());
+ }
+
+ };
+
+ ImageGraphics2D g2dImage = new ImageGraphics2D(src.getInfo(), painter);
+ return g2dImage;
+ }
+
+ /** {@inheritDoc} */
+ public ImageFlavor getSourceFlavor() {
+ return ImageFlavor.XML_DOM;
+ }
+
+ /** {@inheritDoc} */
+ public ImageFlavor getTargetFlavor() {
+ return ImageFlavor.GRAPHICS2D;
+ }
+
+}
diff --git a/src/java/org/apache/fop/image/loader/batik/ImageConverterWMF2G2D.java b/src/java/org/apache/fop/image/loader/batik/ImageConverterWMF2G2D.java
new file mode 100644
index 000000000..6babe4523
--- /dev/null
+++ b/src/java/org/apache/fop/image/loader/batik/ImageConverterWMF2G2D.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.image.loader.batik;
+
+import java.awt.Dimension;
+import java.awt.Graphics2D;
+import java.awt.geom.Rectangle2D;
+import java.util.Map;
+
+import org.apache.batik.transcoder.wmf.tosvg.WMFPainter;
+import org.apache.batik.transcoder.wmf.tosvg.WMFRecordStore;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.xmlgraphics.image.loader.Image;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.impl.AbstractImageConverter;
+import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D;
+import org.apache.xmlgraphics.java2d.Graphics2DImagePainter;
+
+/**
+ * This ImageConverter converts WMF (Windows Metafile) images (represented by Batik's
+ * WMFRecordStore) to Java2D.
+ */
+public class ImageConverterWMF2G2D extends AbstractImageConverter {
+
+ /** logger */
+ private static Log log = LogFactory.getLog(ImageConverterWMF2G2D.class);
+
+ /** {@inheritDoc} */
+ public Image convert(Image src, Map hints) {
+ checkSourceFlavor(src);
+ ImageWMF wmf = (ImageWMF)src;
+
+ Graphics2DImagePainter painter;
+ painter = new Graphics2DImagePainterWMF(wmf);
+
+ ImageGraphics2D g2dImage = new ImageGraphics2D(src.getInfo(), painter);
+ return g2dImage;
+ }
+
+ /** {@inheritDoc} */
+ public ImageFlavor getSourceFlavor() {
+ return ImageWMF.WMF_IMAGE;
+ }
+
+ /** {@inheritDoc} */
+ public ImageFlavor getTargetFlavor() {
+ return ImageFlavor.GRAPHICS2D;
+ }
+
+ private static class Graphics2DImagePainterWMF implements Graphics2DImagePainter {
+
+ private ImageWMF wmf;
+
+ public Graphics2DImagePainterWMF(ImageWMF wmf) {
+ this.wmf = wmf;
+ }
+
+ /** {@inheritDoc} */
+ public Dimension getImageSize() {
+ return wmf.getSize().getDimensionMpt();
+ }
+
+ /** {@inheritDoc} */
+ public void paint(Graphics2D g2d, Rectangle2D area) {
+ WMFRecordStore wmfStore = wmf.getRecordStore();
+ double w = area.getWidth();
+ double h = area.getHeight();
+
+ //Fit in paint area
+ g2d.translate(area.getX(), area.getY());
+ double sx = w / wmfStore.getWidthPixels();
+ double sy = h / wmfStore.getHeightPixels();
+ if (sx != 1.0 || sy != 1.0) {
+ g2d.scale(sx, sy);
+ }
+
+ WMFPainter painter = new WMFPainter(wmfStore, 1.0f);
+ long start = System.currentTimeMillis();
+ painter.paint(g2d);
+ if (log.isDebugEnabled()) {
+ long duration = System.currentTimeMillis() - start;
+ log.debug("Painting WMF took " + duration + " ms.");
+ }
+ }
+
+ }
+
+}
diff --git a/src/java/org/apache/fop/image/loader/batik/ImageLoaderFactorySVG.java b/src/java/org/apache/fop/image/loader/batik/ImageLoaderFactorySVG.java
new file mode 100644
index 000000000..fa58d8e4a
--- /dev/null
+++ b/src/java/org/apache/fop/image/loader/batik/ImageLoaderFactorySVG.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.image.loader.batik;
+
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.impl.AbstractImageLoaderFactory;
+import org.apache.xmlgraphics.image.loader.spi.ImageLoader;
+import org.apache.xmlgraphics.util.MimeConstants;
+
+/**
+ * Factory class for the ImageLoader for SVG.
+ */
+public class ImageLoaderFactorySVG extends AbstractImageLoaderFactory {
+
+ private static final ImageFlavor[] FLAVORS = new ImageFlavor[] {
+ ImageFlavor.XML_DOM};
+
+ private static final String[] MIMES = new String[] {
+ MimeConstants.MIME_SVG};
+
+ /** {@inheritDoc} */
+ public String[] getSupportedMIMETypes() {
+ return MIMES;
+ }
+
+ /** {@inheritDoc} */
+ public ImageFlavor[] getSupportedFlavors(String mime) {
+ return FLAVORS;
+ }
+
+ /** {@inheritDoc} */
+ public ImageLoader newImageLoader(ImageFlavor targetFlavor) {
+ return new ImageLoaderSVG(targetFlavor);
+ }
+
+ /** {@inheritDoc} */
+ public int getUsagePenalty(String mime, ImageFlavor flavor) {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isAvailable() {
+ return BatikUtil.isBatikAvailable();
+ }
+
+}
diff --git a/src/java/org/apache/fop/image/loader/batik/ImageLoaderFactoryWMF.java b/src/java/org/apache/fop/image/loader/batik/ImageLoaderFactoryWMF.java
new file mode 100644
index 000000000..bfa004d7e
--- /dev/null
+++ b/src/java/org/apache/fop/image/loader/batik/ImageLoaderFactoryWMF.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.image.loader.batik;
+
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.impl.AbstractImageLoaderFactory;
+import org.apache.xmlgraphics.image.loader.spi.ImageLoader;
+
+/**
+ * Factory class for the ImageLoader for WMF (Windows Metafile).
+ */
+public class ImageLoaderFactoryWMF extends AbstractImageLoaderFactory {
+
+ private static final ImageFlavor[] FLAVORS = new ImageFlavor[] {
+ ImageWMF.WMF_IMAGE};
+
+ private static final String[] MIMES = new String[] {
+ ImageWMF.MIME_WMF};
+
+ /** {@inheritDoc} */
+ public String[] getSupportedMIMETypes() {
+ return MIMES;
+ }
+
+ /** {@inheritDoc} */
+ public ImageFlavor[] getSupportedFlavors(String mime) {
+ return FLAVORS;
+ }
+
+ /** {@inheritDoc} */
+ public ImageLoader newImageLoader(ImageFlavor targetFlavor) {
+ return new ImageLoaderWMF(targetFlavor);
+ }
+
+ /** {@inheritDoc} */
+ public int getUsagePenalty(String mime, ImageFlavor flavor) {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isAvailable() {
+ return BatikUtil.isBatikAvailable();
+ }
+
+}
diff --git a/src/java/org/apache/fop/image/loader/batik/ImageLoaderSVG.java b/src/java/org/apache/fop/image/loader/batik/ImageLoaderSVG.java
new file mode 100644
index 000000000..64aff962b
--- /dev/null
+++ b/src/java/org/apache/fop/image/loader/batik/ImageLoaderSVG.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.image.loader.batik;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.batik.dom.svg.SVGDOMImplementation;
+
+import org.apache.xmlgraphics.image.loader.Image;
+import org.apache.xmlgraphics.image.loader.ImageException;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.ImageInfo;
+import org.apache.xmlgraphics.image.loader.ImageSessionContext;
+import org.apache.xmlgraphics.image.loader.impl.AbstractImageLoader;
+import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM;
+import org.apache.xmlgraphics.util.MimeConstants;
+
+/**
+ * ImageLoader for SVG (using Apache Batik).
+ */
+public class ImageLoaderSVG extends AbstractImageLoader {
+
+ private ImageFlavor targetFlavor;
+
+ /**
+ * Main constructor.
+ * @param targetFlavor the target flavor
+ */
+ public ImageLoaderSVG(ImageFlavor targetFlavor) {
+ if (!(ImageFlavor.XML_DOM.equals(targetFlavor))) {
+ throw new IllegalArgumentException("Unsupported target ImageFlavor: " + targetFlavor);
+ }
+ this.targetFlavor = targetFlavor;
+ }
+
+ /** {@inheritDoc} */
+ public ImageFlavor getTargetFlavor() {
+ return this.targetFlavor;
+ }
+
+ /** {@inheritDoc} */
+ public Image loadImage(ImageInfo info, Map hints, ImageSessionContext session)
+ throws ImageException, IOException {
+ if (!MimeConstants.MIME_SVG.equals(info.getMimeType())) {
+ throw new IllegalArgumentException("ImageInfo must be from an SVG image");
+ }
+ Image img = info.getOriginalImage();
+ if (!(img instanceof ImageXMLDOM)) {
+ throw new IllegalArgumentException(
+ "ImageInfo was expected to contain the SVG document as DOM");
+ }
+ ImageXMLDOM svgImage = (ImageXMLDOM)img;
+ if (!SVGDOMImplementation.SVG_NAMESPACE_URI.equals(svgImage.getRootNamespace())) {
+ throw new IllegalArgumentException(
+ "The Image is not in the SVG namespace: " + svgImage.getRootNamespace());
+ }
+ return svgImage;
+ }
+
+}
diff --git a/src/java/org/apache/fop/image/loader/batik/ImageLoaderWMF.java b/src/java/org/apache/fop/image/loader/batik/ImageLoaderWMF.java
new file mode 100644
index 000000000..e7f3e0f0a
--- /dev/null
+++ b/src/java/org/apache/fop/image/loader/batik/ImageLoaderWMF.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.image.loader.batik;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.xmlgraphics.image.loader.Image;
+import org.apache.xmlgraphics.image.loader.ImageException;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.ImageInfo;
+import org.apache.xmlgraphics.image.loader.ImageSessionContext;
+import org.apache.xmlgraphics.image.loader.impl.AbstractImageLoader;
+
+/**
+ * ImageLoader for WMF (Windows Metafile). Depends on the WMF preloader based on Apache Batik.
+ */
+public class ImageLoaderWMF extends AbstractImageLoader {
+
+ private ImageFlavor targetFlavor;
+
+ /**
+ * Main constructor.
+ * @param targetFlavor the target flavor
+ */
+ public ImageLoaderWMF(ImageFlavor targetFlavor) {
+ if (!(ImageWMF.WMF_IMAGE.equals(targetFlavor))) {
+ throw new IllegalArgumentException("Unsupported target ImageFlavor: " + targetFlavor);
+ }
+ this.targetFlavor = targetFlavor;
+ }
+
+ /** {@inheritDoc} */
+ public ImageFlavor getTargetFlavor() {
+ return this.targetFlavor;
+ }
+
+ /** {@inheritDoc} */
+ public Image loadImage(ImageInfo info, Map hints, ImageSessionContext session)
+ throws ImageException, IOException {
+ if (!ImageWMF.MIME_WMF.equals(info.getMimeType())) {
+ throw new IllegalArgumentException("ImageInfo must be from a WMF image");
+ }
+ Image img = info.getOriginalImage();
+ if (!(img instanceof ImageWMF)) {
+ throw new IllegalArgumentException(
+ "ImageInfo was expected to contain the Windows Metafile (WMF)");
+ }
+ ImageWMF wmfImage = (ImageWMF)img;
+ return wmfImage;
+ }
+
+}
diff --git a/src/java/org/apache/fop/image/loader/batik/ImageWMF.java b/src/java/org/apache/fop/image/loader/batik/ImageWMF.java
new file mode 100644
index 000000000..f785d27cc
--- /dev/null
+++ b/src/java/org/apache/fop/image/loader/batik/ImageWMF.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.image.loader.batik;
+
+import org.apache.batik.transcoder.wmf.tosvg.WMFRecordStore;
+
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.ImageInfo;
+import org.apache.xmlgraphics.image.loader.impl.AbstractImage;
+
+/**
+ * This class is an implementation of the Image interface exposing a RenderedImage.
+ */
+public class ImageWMF extends AbstractImage {
+
+ /** MIME type for WMF */
+ public static final String MIME_WMF = "image/x-wmf";
+
+ /** ImageFlavor for Batik's WMFRecordStore */
+ public static final ImageFlavor WMF_IMAGE = new ImageFlavor("WMFRecordStore");
+
+ private WMFRecordStore store;
+
+ /**
+ * Main constructor.
+ * @param info the image info object
+ * @param store the WMF record store containing the loaded WMF file
+ */
+ public ImageWMF(ImageInfo info, WMFRecordStore store) {
+ super(info);
+ this.store = store;
+ }
+
+ /** {@inheritDoc} */
+ public ImageFlavor getFlavor() {
+ return WMF_IMAGE;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isCacheable() {
+ return true;
+ }
+
+ /**
+ * Returns the contained WMF record store.
+ * @return the WMFRecordStore
+ */
+ public WMFRecordStore getRecordStore() {
+ return this.store;
+ }
+
+}
diff --git a/src/java/org/apache/fop/image/loader/batik/PreloaderSVG.java b/src/java/org/apache/fop/image/loader/batik/PreloaderSVG.java
new file mode 100644
index 000000000..e83a302da
--- /dev/null
+++ b/src/java/org/apache/fop/image/loader/batik/PreloaderSVG.java
@@ -0,0 +1,182 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.image.loader.batik;
+
+import java.awt.geom.AffineTransform;
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.Source;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.svg.SVGDocument;
+
+import org.apache.batik.bridge.BridgeContext;
+import org.apache.batik.bridge.UnitProcessor;
+import org.apache.batik.dom.svg.SAXSVGDocumentFactory;
+import org.apache.batik.dom.svg.SVGDOMImplementation;
+import org.apache.batik.dom.svg.SVGOMDocument;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.xmlgraphics.image.loader.ImageContext;
+import org.apache.xmlgraphics.image.loader.ImageInfo;
+import org.apache.xmlgraphics.image.loader.ImageSize;
+import org.apache.xmlgraphics.image.loader.impl.AbstractImagePreloader;
+import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM;
+import org.apache.xmlgraphics.image.loader.util.ImageUtil;
+import org.apache.xmlgraphics.util.MimeConstants;
+
+import org.apache.fop.svg.SVGUserAgent;
+import org.apache.fop.util.UnclosableInputStream;
+
+/**
+ * Image preloader for SVG images.
+ */
+public class PreloaderSVG extends AbstractImagePreloader {
+
+ /** Logger instance */
+ private static Log log = LogFactory.getLog(PreloaderSVG.class);
+
+ private boolean batikAvailable = true;
+
+ /** {@inheritDoc} */
+ public ImageInfo preloadImage(String uri, Source src, ImageContext context)
+ throws IOException {
+ if (!ImageUtil.hasInputStream(src)) {
+ //TODO Remove this and support DOMSource and possibly SAXSource
+ return null;
+ }
+ ImageInfo info = null;
+ if (batikAvailable) {
+ try {
+ Loader loader = new Loader();
+ info = loader.getImage(uri, src, context);
+ } catch (NoClassDefFoundError e) {
+ batikAvailable = false;
+ log.warn("Batik not in class path", e);
+ return null;
+ }
+ }
+ if (info != null) {
+ ImageUtil.closeQuietly(src); //Image is fully read
+ }
+ return info;
+ }
+
+ /**
+ * Returns the fully qualified classname of an XML parser for
+ * Batik classes that apparently need it (error messages, perhaps)
+ * @return an XML parser classname
+ */
+ public static String getParserName() {
+ try {
+ SAXParserFactory factory = SAXParserFactory.newInstance();
+ return factory.newSAXParser().getXMLReader().getClass().getName();
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ /**
+ * This method is put in another class so that the class loader does not
+ * attempt to load Batik related classes when constructing the SVGPreloader
+ * class.
+ */
+ class Loader {
+ private ImageInfo getImage(String uri, Source src,
+ ImageContext context) {
+ // parse document and get the size attributes of the svg element
+
+ InputStream in = new UnclosableInputStream(ImageUtil.needInputStream(src));
+ try {
+ int length = in.available();
+ in.mark(length + 1);
+ SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory(
+ getParserName());
+ SVGDocument doc = (SVGDocument) factory.createSVGDocument(src.getSystemId(), in);
+
+ Element e = doc.getRootElement();
+ float pxUnitToMillimeter = 25.4f / context.getSourceResolution();
+ SVGUserAgent userAg = new SVGUserAgent(pxUnitToMillimeter,
+ new AffineTransform());
+ BridgeContext ctx = new BridgeContext(userAg);
+ UnitProcessor.Context uctx = UnitProcessor.createContext(ctx, e);
+
+ String s;
+ // 'width' attribute - default is 100%
+ s = e.getAttributeNS(null, SVGOMDocument.SVG_WIDTH_ATTRIBUTE);
+ if (s.length() == 0) {
+ s = SVGOMDocument.SVG_SVG_WIDTH_DEFAULT_VALUE;
+ }
+ float width = UnitProcessor.svgHorizontalLengthToUserSpace(
+ s, SVGOMDocument.SVG_WIDTH_ATTRIBUTE, uctx);
+
+ // 'height' attribute - default is 100%
+ s = e.getAttributeNS(null, SVGOMDocument.SVG_HEIGHT_ATTRIBUTE);
+ if (s.length() == 0) {
+ s = SVGOMDocument.SVG_SVG_HEIGHT_DEFAULT_VALUE;
+ }
+ float height = UnitProcessor.svgVerticalLengthToUserSpace(
+ s, SVGOMDocument.SVG_HEIGHT_ATTRIBUTE, uctx);
+
+ ImageInfo info = new ImageInfo(uri, MimeConstants.MIME_SVG);
+ ImageSize size = new ImageSize();
+ size.setSizeInMillipoints(Math.round(width * 1000), Math.round(height * 1000));
+ //Set the resolution to that of the FOUserAgent
+ size.setResolution(context.getSourceResolution());
+ size.calcPixelsFromSize();
+ info.setSize(size);
+
+ //The whole image had to be loaded for this, so keep it
+ ImageXMLDOM xmlImage = new ImageXMLDOM(info,
+ doc, SVGDOMImplementation.SVG_NAMESPACE_URI);
+ info.getCustomObjects().put(ImageInfo.ORIGINAL_IMAGE, xmlImage);
+
+ return info;
+ } catch (NoClassDefFoundError ncdfe) {
+ try {
+ in.reset();
+ } catch (IOException ioe) {
+ // we're more interested in the original exception
+ }
+ batikAvailable = false;
+ log.warn("Batik not in class path", ncdfe);
+ return null;
+ } catch (IOException e) {
+ // If the svg is invalid then it throws an IOException
+ // so there is no way of knowing if it is an svg document
+
+ log.debug("Error while trying to load stream as an SVG file: "
+ + e.getMessage());
+ // assuming any exception means this document is not svg
+ // or could not be loaded for some reason
+ try {
+ in.reset();
+ } catch (IOException ioe) {
+ // we're more interested in the original exception
+ }
+ return null;
+ }
+ }
+ }
+
+}
diff --git a/src/java/org/apache/fop/image/loader/batik/PreloaderWMF.java b/src/java/org/apache/fop/image/loader/batik/PreloaderWMF.java
new file mode 100644
index 000000000..abb740411
--- /dev/null
+++ b/src/java/org/apache/fop/image/loader/batik/PreloaderWMF.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.image.loader.batik;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.xml.transform.Source;
+
+import org.apache.batik.transcoder.wmf.WMFConstants;
+import org.apache.batik.transcoder.wmf.tosvg.WMFRecordStore;
+import org.apache.commons.io.EndianUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.xmlgraphics.image.loader.ImageContext;
+import org.apache.xmlgraphics.image.loader.ImageInfo;
+import org.apache.xmlgraphics.image.loader.ImageSize;
+import org.apache.xmlgraphics.image.loader.impl.AbstractImagePreloader;
+import org.apache.xmlgraphics.image.loader.util.ImageUtil;
+
+import org.apache.fop.util.UnclosableInputStream;
+
+/**
+ * Image preloader for WMF images (Windows Metafile).
+ */
+public class PreloaderWMF extends AbstractImagePreloader {
+
+ /** Logger instance */
+ private static Log log = LogFactory.getLog(PreloaderWMF.class);
+
+ private boolean batikAvailable = true;
+
+ /** {@inheritDoc} */
+ public ImageInfo preloadImage(String uri, Source src, ImageContext context)
+ throws IOException {
+ if (!ImageUtil.hasInputStream(src)) {
+ return null;
+ }
+ ImageInfo info = null;
+ if (batikAvailable) {
+ try {
+ Loader loader = new Loader();
+ info = loader.getImage(uri, src, context);
+ } catch (NoClassDefFoundError e) {
+ batikAvailable = false;
+ log.warn("Batik not in class path", e);
+ return null;
+ }
+ }
+ if (info != null) {
+ ImageUtil.closeQuietly(src); //Image is fully read
+ }
+ return info;
+ }
+
+ /**
+ * This method is put in another class so that the class loader does not
+ * attempt to load Batik related classes when constructing the WMFPreloader
+ * class.
+ */
+ class Loader {
+ private ImageInfo getImage(String uri, Source src,
+ ImageContext context) {
+ // parse document and get the size attributes of the svg element
+
+ InputStream in = new UnclosableInputStream(ImageUtil.needInputStream(src));
+ try {
+ in.mark(4 + 1);
+
+ DataInputStream din = new DataInputStream(in);
+ int magic = EndianUtils.swapInteger(din.readInt());
+ din.reset();
+ if (magic != WMFConstants.META_ALDUS_APM) {
+ return null; //Not a WMF file
+ }
+
+ WMFRecordStore wmfStore = new WMFRecordStore();
+ wmfStore.read(din);
+ IOUtils.closeQuietly(din);
+
+ int width = wmfStore.getWidthUnits();
+ int height = wmfStore.getHeightUnits();
+ int dpi = wmfStore.getMetaFileUnitsPerInch();
+
+ ImageInfo info = new ImageInfo(uri, "image/x-wmf");
+ ImageSize size = new ImageSize();
+ size.setSizeInPixels(width, height);
+ size.setResolution(dpi);
+ size.calcSizeFromPixels();
+ info.setSize(size);
+ ImageWMF img = new ImageWMF(info, wmfStore);
+ info.getCustomObjects().put(ImageInfo.ORIGINAL_IMAGE, img);
+
+ return info;
+ } catch (NoClassDefFoundError ncdfe) {
+ try {
+ in.reset();
+ } catch (IOException ioe) {
+ // we're more interested in the original exception
+ }
+ batikAvailable = false;
+ log.warn("Batik not in class path", ncdfe);
+ return null;
+ } catch (IOException e) {
+ // If the svg is invalid then it throws an IOException
+ // so there is no way of knowing if it is an svg document
+
+ log.debug("Error while trying to load stream as an WMF file: "
+ + e.getMessage());
+ // assuming any exception means this document is not svg
+ // or could not be loaded for some reason
+ try {
+ in.reset();
+ } catch (IOException ioe) {
+ // we're more interested in the original exception
+ }
+ return null;
+ }
+ }
+ }
+
+}
diff --git a/src/java/org/apache/fop/image/loader/batik/package.html b/src/java/org/apache/fop/image/loader/batik/package.html
new file mode 100644
index 000000000..f664913d7
--- /dev/null
+++ b/src/java/org/apache/fop/image/loader/batik/package.html
@@ -0,0 +1,26 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<HTML>
+<TITLE>org.apache.fop.image2.impl.batik Package</TITLE>
+<BODY>
+<P>
+ Contains implementations of image loaders and converters which are dependent
+ on Apache Batik (SVG and WMF).
+</P>
+</BODY>
+</HTML> \ No newline at end of file
diff --git a/src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java
index 5668f81ef..0e51517f2 100644
--- a/src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java
@@ -21,10 +21,18 @@ package org.apache.fop.layoutmgr;
import java.awt.Dimension;
import java.awt.Rectangle;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.xmlgraphics.image.loader.ImageException;
+import org.apache.xmlgraphics.image.loader.ImageInfo;
+import org.apache.xmlgraphics.image.loader.ImageManager;
+import org.apache.xmlgraphics.image.loader.util.ImageUtil;
+
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.area.AreaTreeHandler;
import org.apache.fop.area.Block;
@@ -38,8 +46,6 @@ import org.apache.fop.area.inline.Viewport;
import org.apache.fop.datatypes.FODimension;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.extensions.ExternalDocument;
-import org.apache.fop.image.FopImage;
-import org.apache.fop.image.ImageFactory;
import org.apache.fop.layoutmgr.inline.ImageLayout;
/**
@@ -51,14 +57,13 @@ public class ExternalDocumentLayoutManager extends AbstractPageSequenceLayoutMan
private static Log log = LogFactory.getLog(ExternalDocumentLayoutManager.class);
- private FopImage image;
private ImageLayout imageLayout;
/**
* Constructor
*
* @param ath the area tree handler object
- * @param pseq fo:page-sequence to process
+ * @param document fox:external-document to process
*/
public ExternalDocumentLayoutManager(AreaTreeHandler ath, ExternalDocument document) {
super(ath, document);
@@ -80,38 +85,78 @@ public class ExternalDocumentLayoutManager extends AbstractPageSequenceLayoutMan
public void activateLayout() {
initialize();
- String uri = getExternalDocument().getSrc();
FOUserAgent userAgent = pageSeq.getUserAgent();
- ImageFactory fact = userAgent.getFactory().getImageFactory();
- this.image = fact.getImage(uri, userAgent);
- if (this.image == null) {
- log.error("Image not available: " + uri);
- return;
- } else {
- // load dimensions
- if (!this.image.load(FopImage.DIMENSIONS)) {
- log.error("Cannot read image dimensions: " + uri);
- return;
- }
- }
- Dimension intrinsicSize = new Dimension(
- image.getIntrinsicWidth(),
- image.getIntrinsicHeight());
- this.imageLayout = new ImageLayout(getExternalDocument(), this, intrinsicSize);
+ ImageManager imageManager = userAgent.getFactory().getImageManager();
- areaTreeHandler.getAreaTreeModel().startPageSequence(null);
- if (log.isDebugEnabled()) {
- log.debug("Starting layout");
+ String uri = getExternalDocument().getSrc();
+ Integer firstPageIndex = ImageUtil.getPageIndexFromURI(uri);
+ boolean hasPageIndex = (firstPageIndex != null);
+
+ try {
+ ImageInfo info = imageManager.getImageInfo(uri, userAgent.getImageSessionContext());
+
+ Object moreImages = info.getCustomObjects().get(ImageInfo.HAS_MORE_IMAGES);
+ boolean hasMoreImages = moreImages != null && !Boolean.FALSE.equals(moreImages);
+
+ Dimension intrinsicSize = info.getSize().getDimensionMpt();
+ ImageLayout layout = new ImageLayout(getExternalDocument(), this, intrinsicSize);
+
+ areaTreeHandler.getAreaTreeModel().startPageSequence(null);
+ if (log.isDebugEnabled()) {
+ log.debug("Starting layout");
+ }
+
+ makePageForImage(info, layout);
+
+ if (!hasPageIndex && hasMoreImages) {
+ if (log.isTraceEnabled()) {
+ log.trace("Starting multi-page processing...");
+ }
+ URI originalURI;
+ try {
+ originalURI = new URI(uri);
+ int pageIndex = 1;
+ while (hasMoreImages) {
+ URI tempURI = new URI(originalURI.getScheme(),
+ originalURI.getSchemeSpecificPart(),
+ "page=" + Integer.toString(pageIndex + 1));
+ if (log.isTraceEnabled()) {
+ log.trace("Subimage: " + tempURI.toASCIIString());
+ }
+ ImageInfo subinfo = imageManager.getImageInfo(
+ tempURI.toASCIIString(), userAgent.getImageSessionContext());
+
+ moreImages = subinfo.getCustomObjects().get(ImageInfo.HAS_MORE_IMAGES);
+ hasMoreImages = moreImages != null && !Boolean.FALSE.equals(moreImages);
+
+ intrinsicSize = subinfo.getSize().getDimensionMpt();
+ layout = new ImageLayout(
+ getExternalDocument(), this, intrinsicSize);
+
+ makePageForImage(subinfo, layout);
+
+ pageIndex++;
+ }
+ } catch (URISyntaxException e) {
+ log.error("Error parsing or constructing URIs based on URI: " + uri);
+ return;
+ }
+ }
+ } catch (IOException ioe) {
+ log.error("Image not available: " + uri, ioe);
+ } catch (ImageException ie) {
+ log.error("Error while inspecting image: " + uri + " (" + ie.getMessage() + ")");
}
+ }
+ private void makePageForImage(ImageInfo info, ImageLayout layout) {
+ this.imageLayout = layout;
curPage = makeNewPage(false, false);
-
- fillPage(); //TODO Implement multi-page documents (using new image package)
-
+ fillPage(info.getOriginalURI());
finishPage();
}
-
- private void fillPage() {
+
+ private void fillPage(String uri) {
Dimension imageSize = this.imageLayout.getViewportSize();
@@ -119,7 +164,7 @@ public class ExternalDocumentLayoutManager extends AbstractPageSequenceLayoutMan
blockArea.setIPD(imageSize.width);
LineArea lineArea = new LineArea();
- Image imageArea = new Image(getExternalDocument().getSrc());
+ Image imageArea = new Image(uri);
TraitSetter.setProducerID(imageArea, fobj.getId());
transferForeignAttributes(imageArea);
diff --git a/src/java/org/apache/fop/layoutmgr/TraitSetter.java b/src/java/org/apache/fop/layoutmgr/TraitSetter.java
index b50167b16..f1e5ffdc6 100644
--- a/src/java/org/apache/fop/layoutmgr/TraitSetter.java
+++ b/src/java/org/apache/fop/layoutmgr/TraitSetter.java
@@ -21,20 +21,19 @@ package org.apache.fop.layoutmgr;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.apache.fop.traits.BorderProps;
-import org.apache.fop.traits.MinOptMax;
+
import org.apache.fop.area.Area;
import org.apache.fop.area.Trait;
import org.apache.fop.datatypes.LengthBase;
import org.apache.fop.datatypes.PercentBaseContext;
import org.apache.fop.datatypes.SimplePercentBaseContext;
import org.apache.fop.fo.Constants;
-import org.apache.fop.fo.properties.CommonMarginBlock;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
+import org.apache.fop.fo.properties.CommonMarginBlock;
import org.apache.fop.fo.properties.CommonTextDecoration;
import org.apache.fop.fonts.Font;
-import org.apache.fop.fonts.FontInfo;
-import org.apache.fop.fonts.FontTriplet;
+import org.apache.fop.traits.BorderProps;
+import org.apache.fop.traits.MinOptMax;
/**
* This is a helper class used for setting common traits on areas.
@@ -303,9 +302,9 @@ public class TraitSetter {
Trait.Background back = new Trait.Background();
back.setColor(backProps.backgroundColor);
- if (backProps.getFopImage() != null) {
+ if (backProps.getImageInfo() != null) {
back.setURL(backProps.backgroundImage);
- back.setFopImage(backProps.getFopImage());
+ back.setImageInfo(backProps.getImageInfo());
back.setRepeat(backProps.backgroundRepeat);
if (backProps.backgroundPositionHorizontal != null) {
if (back.getRepeat() == Constants.EN_NOREPEAT
@@ -317,7 +316,7 @@ public class TraitSetter {
back.setHoriz(backProps.backgroundPositionHorizontal.getValue(
new SimplePercentBaseContext(context,
LengthBase.IMAGE_BACKGROUND_POSITION_HORIZONTAL,
- (width - back.getFopImage().getIntrinsicWidth())
+ (width - back.getImageInfo().getSize().getHeightMpt())
)
));
} else {
@@ -338,7 +337,7 @@ public class TraitSetter {
back.setVertical(backProps.backgroundPositionVertical.getValue(
new SimplePercentBaseContext(context,
LengthBase.IMAGE_BACKGROUND_POSITION_VERTICAL,
- (height - back.getFopImage().getIntrinsicHeight())
+ (height - back.getImageInfo().getSize().getHeightMpt())
)
));
} else {
diff --git a/src/java/org/apache/fop/layoutmgr/inline/ImageLayout.java b/src/java/org/apache/fop/layoutmgr/inline/ImageLayout.java
index b3ccea628..60247af39 100644
--- a/src/java/org/apache/fop/layoutmgr/inline/ImageLayout.java
+++ b/src/java/org/apache/fop/layoutmgr/inline/ImageLayout.java
@@ -29,7 +29,11 @@ import org.apache.fop.datatypes.Length;
import org.apache.fop.datatypes.PercentBaseContext;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.GraphicsProperties;
+import org.apache.fop.fo.properties.LengthRangeProperty;
+/**
+ * Helper class which calculates the size and position in the viewport of an image.
+ */
public class ImageLayout implements Constants {
/** logging instance */
@@ -45,6 +49,12 @@ public class ImageLayout implements Constants {
private Dimension viewportSize = new Dimension(-1, -1);
private boolean clip;
+ /**
+ * Main constructor
+ * @param props the properties for the image
+ * @param percentBaseContext the context object for percentage calculations
+ * @param intrinsicSize the image's intrinsic size
+ */
public ImageLayout(GraphicsProperties props, PercentBaseContext percentBaseContext,
Dimension intrinsicSize) {
this.props = props;
@@ -54,6 +64,9 @@ public class ImageLayout implements Constants {
doLayout();
}
+ /**
+ * Does the actual calculations for the image.
+ */
protected void doLayout() {
Length len;
@@ -63,25 +76,26 @@ public class ImageLayout implements Constants {
len = props.getBlockProgressionDimension().getOptimum(percentBaseContext).getLength();
if (len.getEnum() != EN_AUTO) {
bpd = len.getValue(percentBaseContext);
- } else {
- len = props.getHeight();
- if (len.getEnum() != EN_AUTO) {
- bpd = len.getValue(percentBaseContext);
- }
+ }
+ len = props.getBlockProgressionDimension().getMinimum(percentBaseContext).getLength();
+ if (bpd == -1 && len.getEnum() != EN_AUTO) {
+ //Establish minimum viewport size
+ bpd = len.getValue(percentBaseContext);
}
len = props.getInlineProgressionDimension().getOptimum(percentBaseContext).getLength();
if (len.getEnum() != EN_AUTO) {
ipd = len.getValue(percentBaseContext);
- } else {
- len = props.getWidth();
- if (len.getEnum() != EN_AUTO) {
- ipd = len.getValue(percentBaseContext);
- }
+ }
+ len = props.getInlineProgressionDimension().getMinimum(percentBaseContext).getLength();
+ if (ipd == -1 && len.getEnum() != EN_AUTO) {
+ //Establish minimum viewport size
+ ipd = len.getValue(percentBaseContext);
}
// if auto then use the intrinsic size of the content scaled
// to the content-height and content-width
+ boolean constrainIntrinsicSize = false;
int cwidth = -1;
int cheight = -1;
len = props.getContentWidth();
@@ -91,16 +105,19 @@ public class ImageLayout implements Constants {
if (ipd != -1) {
cwidth = ipd;
}
+ constrainIntrinsicSize = true;
break;
case EN_SCALE_DOWN_TO_FIT:
if (ipd != -1 && intrinsicSize.width > ipd) {
cwidth = ipd;
}
+ constrainIntrinsicSize = true;
break;
case EN_SCALE_UP_TO_FIT:
if (ipd != -1 && intrinsicSize.width < ipd) {
cwidth = ipd;
}
+ constrainIntrinsicSize = true;
break;
default:
cwidth = len.getValue(percentBaseContext);
@@ -113,64 +130,43 @@ public class ImageLayout implements Constants {
if (bpd != -1) {
cheight = bpd;
}
+ constrainIntrinsicSize = true;
break;
case EN_SCALE_DOWN_TO_FIT:
if (bpd != -1 && intrinsicSize.height > bpd) {
cheight = bpd;
}
+ constrainIntrinsicSize = true;
break;
case EN_SCALE_UP_TO_FIT:
if (bpd != -1 && intrinsicSize.height < bpd) {
cheight = bpd;
}
+ constrainIntrinsicSize = true;
break;
default:
cheight = len.getValue(percentBaseContext);
}
}
- int scaling = props.getScaling();
- if ((scaling == EN_UNIFORM) || (cwidth == -1) || cheight == -1) {
- if (cwidth == -1 && cheight == -1) {
- cwidth = intrinsicSize.width;
- cheight = intrinsicSize.height;
- } else if (cwidth == -1) {
- if (intrinsicSize.height == 0) {
- cwidth = 0;
- } else {
- cwidth = (int)(intrinsicSize.width * (double)cheight
- / intrinsicSize.height);
- }
- } else if (cheight == -1) {
- if (intrinsicSize.width == 0) {
- cheight = 0;
- } else {
- cheight = (int)(intrinsicSize.height * (double)cwidth
- / intrinsicSize.width);
- }
- } else {
- // adjust the larger
- if (intrinsicSize.width == 0 || intrinsicSize.height == 0) {
- cwidth = 0;
- cheight = 0;
- } else {
- double rat1 = (double) cwidth / intrinsicSize.width;
- double rat2 = (double) cheight / intrinsicSize.height;
- if (rat1 < rat2) {
- // reduce cheight
- cheight = (int)(rat1 * intrinsicSize.height);
- } else if (rat1 > rat2) {
- cwidth = (int)(rat2 * intrinsicSize.width);
- }
- }
- }
+ Dimension constrainedIntrinsicSize;
+ if (constrainIntrinsicSize) {
+ constrainedIntrinsicSize = constrain(intrinsicSize);
+ } else {
+ constrainedIntrinsicSize = intrinsicSize;
}
+
+ //Derive content extents where not explicit
+ Dimension adjustedDim = adjustContentSize(cwidth, cheight, constrainedIntrinsicSize);
+ cwidth = adjustedDim.width;
+ cheight = adjustedDim.height;
+ //Adjust viewport if not explicit
if (ipd == -1) {
- ipd = cwidth;
+ ipd = constrainExtent(cwidth, props.getInlineProgressionDimension());
}
if (bpd == -1) {
- bpd = cheight;
+ bpd = constrainExtent(cheight, props.getBlockProgressionDimension());
}
this.clip = false;
@@ -193,6 +189,90 @@ public class ImageLayout implements Constants {
this.placement = new Rectangle(xoffset, yoffset, cwidth, cheight);
}
+ private int constrainExtent(int extent, LengthRangeProperty range) {
+ Length len;
+ len = range.getMaximum(percentBaseContext).getLength();
+ if (len.getEnum() != EN_AUTO) {
+ int max = len.getValue(percentBaseContext);
+ if (max != -1) {
+ extent = Math.min(extent, max);
+ }
+ }
+ len = range.getMinimum(percentBaseContext).getLength();
+ if (len.getEnum() != EN_AUTO) {
+ int min = len.getValue(percentBaseContext);
+ if (min != -1) {
+ extent = Math.max(extent, min);
+ }
+ }
+ return extent;
+ }
+
+ private Dimension constrain(Dimension size) {
+ Dimension adjusted = new Dimension(size);
+ int effWidth = constrainExtent(size.width, props.getInlineProgressionDimension());
+ int effHeight = constrainExtent(size.height, props.getBlockProgressionDimension());
+ int scaling = props.getScaling();
+ if (scaling == EN_UNIFORM) {
+ double rat1 = (double)effWidth / size.width;
+ double rat2 = (double)effHeight / size.height;
+ if (rat1 < rat2) {
+ adjusted.width = effWidth;
+ adjusted.height = (int)(rat1 * size.height);
+ } else if (rat1 > rat2) {
+ adjusted.width = (int)(rat2 * size.width);
+ adjusted.height = effHeight;
+ }
+ } else {
+ adjusted.width = effWidth;
+ adjusted.height = effHeight;
+ }
+ return adjusted;
+ }
+
+ private Dimension adjustContentSize(
+ final int cwidth, final int cheight,
+ Dimension defaultSize) {
+ Dimension dim = new Dimension(cwidth, cheight);
+ int scaling = props.getScaling();
+ if ((scaling == EN_UNIFORM) || (cwidth == -1) || cheight == -1) {
+ if (cwidth == -1 && cheight == -1) {
+ dim.width = defaultSize.width;
+ dim.height = defaultSize.height;
+ } else if (cwidth == -1) {
+ if (defaultSize.height == 0) {
+ dim.width = 0;
+ } else {
+ dim.width = (int)(defaultSize.width * (double)cheight
+ / defaultSize.height);
+ }
+ } else if (cheight == -1) {
+ if (defaultSize.width == 0) {
+ dim.height = 0;
+ } else {
+ dim.height = (int)(defaultSize.height * (double)cwidth
+ / defaultSize.width);
+ }
+ } else {
+ // adjust the larger
+ if (defaultSize.width == 0 || defaultSize.height == 0) {
+ dim.width = 0;
+ dim.height = 0;
+ } else {
+ double rat1 = (double)cwidth / defaultSize.width;
+ double rat2 = (double)cheight / defaultSize.height;
+ if (rat1 < rat2) {
+ // reduce height
+ dim.height = (int)(rat1 * defaultSize.height);
+ } else if (rat1 > rat2) {
+ dim.width = (int)(rat2 * defaultSize.width);
+ }
+ }
+ }
+ }
+ return dim;
+ }
+
/**
* Given the ipd and the content width calculates the
* required x offset based on the text-align property
@@ -243,18 +323,34 @@ public class ImageLayout implements Constants {
return yoffset;
}
+ /**
+ * Returns the placement of the image inside the viewport.
+ * @return the placement of the image inside the viewport (coordinates in millipoints)
+ */
public Rectangle getPlacement() {
return this.placement;
}
+ /**
+ * Returns the size of the image's viewport.
+ * @return the viewport size (in millipoints)
+ */
public Dimension getViewportSize() {
return this.viewportSize;
}
+ /**
+ * Returns the size of the image's intrinsic (natural) size.
+ * @return the intrinsic size (in millipoints)
+ */
public Dimension getIntrinsicSize() {
return this.intrinsicSize;
}
+ /**
+ * Indicates whether the image is clipped.
+ * @return true if the image shall be clipped
+ */
public boolean isClipped() {
return this.clip;
}
diff --git a/src/java/org/apache/fop/pdf/AlphaRasterImage.java b/src/java/org/apache/fop/pdf/AlphaRasterImage.java
new file mode 100644
index 000000000..be476bdb2
--- /dev/null
+++ b/src/java/org/apache/fop/pdf/AlphaRasterImage.java
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.pdf;
+
+import java.awt.image.DataBuffer;
+import java.awt.image.Raster;
+import java.awt.image.RenderedImage;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.xmlgraphics.image.GraphicsUtil;
+
+/**
+ * PDFImage implementation for alpha channel "images".
+ */
+public class AlphaRasterImage implements PDFImage {
+
+ private int bitsPerComponent;
+ private PDFDeviceColorSpace colorSpace;
+ private Raster alpha;
+ private String key;
+
+ /**
+ * Create a alpha channel image.
+ * Creates a new bitmap image with the given data.
+ *
+ * @param k the key to be used to lookup the image
+ * @param alpha the alpha channel raster
+ */
+ public AlphaRasterImage(String k, Raster alpha) {
+ this.key = k;
+ this.bitsPerComponent = 8;
+ this.colorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_GRAY);
+ if (alpha == null) {
+ throw new NullPointerException("Parameter alpha must not be null");
+ }
+ this.alpha = alpha;
+ }
+
+ /**
+ * Create a alpha channel image.
+ * Extracts the alpha channel from the RenderedImage and creates a new bitmap image
+ * with the given data.
+ *
+ * @param k the key to be used to lookup the image
+ * @param image the image (must have an alpha channel)
+ */
+ public AlphaRasterImage(String k, RenderedImage image) {
+ this(k, GraphicsUtil.getAlphaRaster(image));
+ }
+
+ /** {@inheritDoc} */
+ public void setup(PDFDocument doc) {
+ //nop
+ }
+
+ /** {@inheritDoc} */
+ public String getKey() {
+ return key;
+ }
+
+ /** {@inheritDoc} */
+ public int getWidth() {
+ return alpha.getWidth();
+ }
+
+ /** {@inheritDoc} */
+ public int getHeight() {
+ return alpha.getHeight();
+ }
+
+ /** {@inheritDoc} */
+ public PDFDeviceColorSpace getColorSpace() {
+ return colorSpace;
+ }
+
+ /** {@inheritDoc} */
+ public int getBitsPerComponent() {
+ return bitsPerComponent;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isTransparent() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public PDFColor getTransparentColor() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ public String getMask() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ public String getSoftMask() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ public PDFReference getSoftMaskReference() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isInverted() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public void outputContents(OutputStream out) throws IOException {
+ int w = getWidth();
+ int h = getHeight();
+
+ //Check Raster
+ int nbands = alpha.getNumBands();
+ if (nbands != 1) {
+ throw new UnsupportedOperationException(
+ "Expected only one band/component for the alpha channel");
+ }
+ int dataType = alpha.getDataBuffer().getDataType();
+ if (dataType != DataBuffer.TYPE_BYTE) {
+ throw new UnsupportedOperationException("Unsupported DataBuffer type: "
+ + alpha.getDataBuffer().getClass().getName());
+ }
+
+ //...and write the Raster line by line with a reusable buffer
+ byte[] line = new byte[nbands * w];
+ for (int y = 0; y < h; y++) {
+ alpha.getDataElements(0, y, w, 1, line);
+ out.write(line);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void populateXObjectDictionary(PDFDictionary dict) {
+ //nop
+ }
+
+ /** {@inheritDoc} */
+ public PDFICCStream getICCStream() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isPS() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public String getFilterHint() {
+ return PDFFilterList.IMAGE_FILTER;
+ }
+
+ /** {@inheritDoc} */
+ public PDFFilter getPDFFilter() {
+ return null;
+ }
+
+}
+
+
diff --git a/src/java/org/apache/fop/pdf/BitmapImage.java b/src/java/org/apache/fop/pdf/BitmapImage.java
index 34c78ffe3..69b51dac3 100644
--- a/src/java/org/apache/fop/pdf/BitmapImage.java
+++ b/src/java/org/apache/fop/pdf/BitmapImage.java
@@ -30,7 +30,7 @@ import java.io.OutputStream;
public class BitmapImage implements PDFImage {
private int height;
private int width;
- private int bitsPerPixel;
+ private int bitsPerComponent;
private PDFDeviceColorSpace colorSpace;
private byte[] bitmaps;
private PDFReference maskRef;
@@ -53,10 +53,12 @@ public class BitmapImage implements PDFImage {
this.key = k;
this.height = height;
this.width = width;
- this.bitsPerPixel = 8;
+ this.bitsPerComponent = 8;
this.colorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
this.bitmaps = data;
- maskRef = new PDFReference(mask);
+ if (mask != null) {
+ maskRef = new PDFReference(mask);
+ }
}
/**
@@ -117,13 +119,9 @@ public class BitmapImage implements PDFImage {
return colorSpace;
}
- /**
- * Get the number of bits per pixel.
- *
- * @return the number of bits per pixel
- */
- public int getBitsPerPixel() {
- return bitsPerPixel;
+ /** {@inheritDoc} */
+ public int getBitsPerComponent() {
+ return bitsPerComponent;
}
/**
@@ -173,13 +171,16 @@ public class BitmapImage implements PDFImage {
return false;
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void outputContents(OutputStream out) throws IOException {
out.write(bitmaps);
}
+ /** {@inheritDoc} */
+ public void populateXObjectDictionary(PDFDictionary dict) {
+ //nop
+ }
+
/**
* Get the ICC stream.
* @return always returns null since this has no icc color space
diff --git a/src/java/org/apache/fop/pdf/FlateFilter.java b/src/java/org/apache/fop/pdf/FlateFilter.java
index d7dc81d1f..a652c4534 100644
--- a/src/java/org/apache/fop/pdf/FlateFilter.java
+++ b/src/java/org/apache/fop/pdf/FlateFilter.java
@@ -19,8 +19,8 @@
package org.apache.fop.pdf;
-import java.io.OutputStream;
import java.io.IOException;
+import java.io.OutputStream;
import org.apache.xmlgraphics.util.io.FlateEncodeOutputStream;
@@ -98,13 +98,13 @@ public class FlateFilter extends PDFFilter {
if (predictor > PREDICTION_NONE) {
PDFDictionary dict = new PDFDictionary();
dict.put("Predictor", predictor);
- if (colors > 0) {
+ if (colors > 1) {
dict.put("Colors", colors);
}
- if (bitsPerComponent > 0) {
+ if (bitsPerComponent > 0 && bitsPerComponent != 8) {
dict.put("BitsPerComponent", bitsPerComponent);
}
- if (columns > 0) {
+ if (columns > 1) {
dict.put("Columns", columns);
}
return dict;
diff --git a/src/java/org/apache/fop/pdf/PDFFactory.java b/src/java/org/apache/fop/pdf/PDFFactory.java
index f3069e85e..d0c48ee39 100644
--- a/src/java/org/apache/fop/pdf/PDFFactory.java
+++ b/src/java/org/apache/fop/pdf/PDFFactory.java
@@ -1490,9 +1490,6 @@ public class PDFFactory {
*/
public PDFICCStream makePDFICCStream() {
PDFICCStream iccStream = new PDFICCStream();
- iccStream.getFilterList().addDefaultFilters(
- getDocument().getFilterMap(),
- PDFFilterList.CONTENT_FILTER);
getDocument().registerObject(iccStream);
//getDocument().applyEncryption(iccStream);
diff --git a/src/java/org/apache/fop/pdf/PDFFilterList.java b/src/java/org/apache/fop/pdf/PDFFilterList.java
index 607ae174b..84131707b 100644
--- a/src/java/org/apache/fop/pdf/PDFFilterList.java
+++ b/src/java/org/apache/fop/pdf/PDFFilterList.java
@@ -34,6 +34,8 @@ public class PDFFilterList {
public static final String DEFAULT_FILTER = "default";
/** Key for the filter used for normal content*/
public static final String CONTENT_FILTER = "content";
+ /** Key for the filter used for precompressed content */
+ public static final String PRECOMPRESSED_FILTER = "precompressed";
/** Key for the filter used for images */
public static final String IMAGE_FILTER = "image";
/** Key for the filter used for JPEG images */
@@ -178,6 +180,9 @@ public class PDFFilterList {
} else if (TIFF_FILTER.equals(type)) {
//CCITT-encoded images are already well compressed
addFilter(new NullFilter());
+ } else if (PRECOMPRESSED_FILTER.equals(type)) {
+ //precompressed content doesn't need further compression
+ addFilter(new NullFilter());
} else {
// built-in default to flate
addFilter(new FlateFilter());
diff --git a/src/java/org/apache/fop/pdf/PDFICCStream.java b/src/java/org/apache/fop/pdf/PDFICCStream.java
index da1e8353c..f242c0cae 100644
--- a/src/java/org/apache/fop/pdf/PDFICCStream.java
+++ b/src/java/org/apache/fop/pdf/PDFICCStream.java
@@ -66,9 +66,7 @@ public class PDFICCStream extends PDFStream {
return length;
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected void outputRawStreamData(OutputStream out) throws IOException {
cp.write(out);
}
diff --git a/src/java/org/apache/fop/pdf/PDFImage.java b/src/java/org/apache/fop/pdf/PDFImage.java
index f880fa6ac..e0aaa44fa 100644
--- a/src/java/org/apache/fop/pdf/PDFImage.java
+++ b/src/java/org/apache/fop/pdf/PDFImage.java
@@ -69,11 +69,11 @@ public interface PDFImage {
PDFDeviceColorSpace getColorSpace();
/**
- * Get the bits per pixel for this image.
+ * Get the bits per color component for this image.
*
- * @return the bits per pixel
+ * @return the bits per component
*/
- int getBitsPerPixel();
+ int getBitsPerComponent();
/**
* Check if this image is a PostScript image.
@@ -130,6 +130,14 @@ public interface PDFImage {
void outputContents(OutputStream out) throws IOException;
/**
+ * Populates the XObject's dictionary with additional values. The values are added to the
+ * dictionary after all the values obtained from other methods from this interface have
+ * been put into the dictionary. That allows to override certain values.
+ * @param dict the dictionary to fill
+ */
+ void populateXObjectDictionary(PDFDictionary dict);
+
+ /**
* Get the ICC stream for this image.
*
* @return the ICC stream for this image if any
diff --git a/src/java/org/apache/fop/pdf/PDFImageXObject.java b/src/java/org/apache/fop/pdf/PDFImageXObject.java
index 35f3e543d..14fbeed4d 100644
--- a/src/java/org/apache/fop/pdf/PDFImageXObject.java
+++ b/src/java/org/apache/fop/pdf/PDFImageXObject.java
@@ -90,7 +90,7 @@ public class PDFImageXObject extends PDFXObject {
put("Subtype", new PDFName("Image"));
put("Width", new Integer(pdfimage.getWidth()));
put("Height", new Integer(pdfimage.getHeight()));
- put("BitsPerComponent", new Integer(pdfimage.getBitsPerPixel()));
+ put("BitsPerComponent", new Integer(pdfimage.getBitsPerComponent()));
PDFICCStream pdfICCStream = pdfimage.getICCStream();
if (pdfICCStream != null) {
@@ -119,18 +119,25 @@ public class PDFImageXObject extends PDFXObject {
if (pdfimage.isTransparent()) {
PDFColor transp = pdfimage.getTransparentColor();
PDFArray mask = new PDFArray(this);
- mask.add(new Integer(transp.red255()));
- mask.add(new Integer(transp.red255()));
- mask.add(new Integer(transp.green255()));
- mask.add(new Integer(transp.green255()));
- mask.add(new Integer(transp.blue255()));
- mask.add(new Integer(transp.blue255()));
+ if (pdfimage.getColorSpace().isGrayColorSpace()) {
+ mask.add(new Integer(transp.red255()));
+ mask.add(new Integer(transp.red255()));
+ } else {
+ mask.add(new Integer(transp.red255()));
+ mask.add(new Integer(transp.red255()));
+ mask.add(new Integer(transp.green255()));
+ mask.add(new Integer(transp.green255()));
+ mask.add(new Integer(transp.blue255()));
+ mask.add(new Integer(transp.blue255()));
+ }
put("Mask", mask);
}
PDFReference ref = pdfimage.getSoftMaskReference();
if (ref != null) {
put("SMask", ref);
}
+ //Important: do this at the end so previous values can be overwritten.
+ pdfimage.populateXObjectDictionary(this);
}
/** {@inheritDoc} */
diff --git a/src/java/org/apache/fop/pdf/PDFObject.java b/src/java/org/apache/fop/pdf/PDFObject.java
index f53183574..4bfd805f4 100644
--- a/src/java/org/apache/fop/pdf/PDFObject.java
+++ b/src/java/org/apache/fop/pdf/PDFObject.java
@@ -276,14 +276,23 @@ public abstract class PDFObject implements PDFWritable {
*/
protected byte[] encodeString(String string) {
return encodeText(string);
- /*
- final byte[] buf = encode(PDFText.escapeString(string));
+ }
+
+ /**
+ * Encodes binary data as hexadecimal string object.
+ * @param data the binary data
+ * @param out the OutputStream to write the encoded object to
+ * @throws IOException if an I/O error occurs
+ */
+ protected void encodeBinaryToHexString(byte[] data, OutputStream out) throws IOException {
+ out.write('<');
if (getDocumentSafely().isEncryptionActive()) {
- return PDFText.escapeByteArray(
- getDocument().getEncryption().encrypt(buf, this));
- } else {
- return buf;
- }*/
+ data = getDocument().getEncryption().encrypt(data, this);
+ }
+ String hex = PDFText.toHex(data, false);
+ byte[] encoded = hex.getBytes("US-ASCII");
+ out.write(encoded);
+ out.write('>');
}
/**
@@ -307,6 +316,9 @@ public abstract class PDFObject implements PDFWritable {
}
} else if (obj instanceof Boolean) {
writer.write(obj.toString());
+ } else if (obj instanceof byte[]) {
+ writer.flush();
+ encodeBinaryToHexString((byte[])obj, out);
} else {
writer.flush();
out.write(encodeText(obj.toString()));
diff --git a/src/java/org/apache/fop/pdf/PDFReference.java b/src/java/org/apache/fop/pdf/PDFReference.java
index 3b615735a..da388d368 100644
--- a/src/java/org/apache/fop/pdf/PDFReference.java
+++ b/src/java/org/apache/fop/pdf/PDFReference.java
@@ -51,6 +51,9 @@ public class PDFReference implements PDFWritable {
* @param ref an object reference
*/
public PDFReference(String ref) {
+ if (ref == null) {
+ throw new NullPointerException("ref must not be null");
+ }
this.indirectReference = ref;
}
diff --git a/src/java/org/apache/fop/pdf/PDFText.java b/src/java/org/apache/fop/pdf/PDFText.java
index 4c17148c1..d380ac8dd 100644
--- a/src/java/org/apache/fop/pdf/PDFText.java
+++ b/src/java/org/apache/fop/pdf/PDFText.java
@@ -25,12 +25,12 @@ import org.apache.avalon.framework.CascadingRuntimeException;
/**
* This class represents a simple number object. It also contains contains some
- * utility methods for outputing numbers to PDF.
+ * utility methods for outputting numbers to PDF.
*/
public class PDFText extends PDFObject {
- private static final char[] DIGITS =
- {'0', '1', '2', '3', '4', '5', '6', '7',
+ private static final char[] DIGITS
+ = {'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
private String text;
@@ -145,20 +145,34 @@ public class PDFText extends PDFObject {
/**
* Converts a byte array to a Hexadecimal String (3.2.3 in PDF 1.4 specs)
* @param data the data to encode
+ * @param brackets true if enclosing brackets should be included
* @return String the resulting string
*/
- public static final String toHex(byte[] data) {
+ public static final String toHex(byte[] data, boolean brackets) {
final StringBuffer sb = new StringBuffer(data.length * 2);
- sb.append("<");
+ if (brackets) {
+ sb.append("<");
+ }
for (int i = 0; i < data.length; i++) {
sb.append(DIGITS[(data[i] >>> 4) & 0x0F]);
sb.append(DIGITS[data[i] & 0x0F]);
}
- sb.append(">");
+ if (brackets) {
+ sb.append(">");
+ }
return sb.toString();
}
/**
+ * Converts a byte array to a Hexadecimal String (3.2.3 in PDF 1.4 specs)
+ * @param data the data to encode
+ * @return String the resulting string
+ */
+ public static final String toHex(byte[] data) {
+ return toHex(data, true);
+ }
+
+ /**
* Converts a String to UTF-16 (big endian).
* @param text text to convert
* @return byte[] UTF-16 stream
diff --git a/src/java/org/apache/fop/render/AbstractGenericSVGHandler.java b/src/java/org/apache/fop/render/AbstractGenericSVGHandler.java
index 7d21d78c0..f74699fd5 100644
--- a/src/java/org/apache/fop/render/AbstractGenericSVGHandler.java
+++ b/src/java/org/apache/fop/render/AbstractGenericSVGHandler.java
@@ -26,28 +26,18 @@ import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
-// DOM
import org.w3c.dom.Document;
-// Batik
-import org.apache.batik.bridge.GVTBuilder;
import org.apache.batik.bridge.BridgeContext;
+import org.apache.batik.bridge.GVTBuilder;
import org.apache.batik.dom.svg.SVGDOMImplementation;
import org.apache.batik.gvt.GraphicsNode;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
-// FOP
-import org.apache.fop.render.Graphics2DAdapter;
-import org.apache.fop.render.Graphics2DImagePainter;
-import org.apache.fop.render.RendererContextConstants;
-import org.apache.fop.render.XMLHandler;
-import org.apache.fop.render.RendererContext;
import org.apache.fop.render.RendererContext.RendererContextWrapper;
import org.apache.fop.svg.SVGUserAgent;
-// Commons-Logging
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
/**
* Generic XML handler for SVG. Uses Apache Batik for SVG processing and simply paints to
* a Graphics2DAdapter and thus ultimatively to Graphics2D interface that is presented.
@@ -76,6 +66,7 @@ public abstract class AbstractGenericSVGHandler implements XMLHandler, RendererC
*/
protected void renderSVGDocument(final RendererContext context,
final Document doc) throws IOException {
+ updateRendererContext(context);
final RendererContextWrapper wrappedContext = RendererContext.wrapRendererContext(context);
int x = wrappedContext.getCurrentXPosition();
int y = wrappedContext.getCurrentYPosition();
@@ -123,6 +114,15 @@ public abstract class AbstractGenericSVGHandler implements XMLHandler, RendererC
x, y, wrappedContext.getWidth(), wrappedContext.getHeight());
}
+ /**
+ * Override this method to update the renderer context if it needs special settings for
+ * certain conditions.
+ * @param context the renderer context
+ */
+ protected void updateRendererContext(RendererContext context) {
+ //nop
+ }
+
/** {@inheritDoc} */
public String getNamespace() {
return SVGDOMImplementation.SVG_NAMESPACE_URI;
diff --git a/src/java/org/apache/fop/render/AbstractGraphics2DAdapter.java b/src/java/org/apache/fop/render/AbstractGraphics2DAdapter.java
index b51b71d61..bc7bb95a1 100644
--- a/src/java/org/apache/fop/render/AbstractGraphics2DAdapter.java
+++ b/src/java/org/apache/fop/render/AbstractGraphics2DAdapter.java
@@ -33,8 +33,6 @@ import java.awt.image.DataBuffer;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
-import org.apache.fop.render.Graphics2DAdapter;
-import org.apache.fop.render.Graphics2DImagePainter;
import org.apache.fop.render.RendererContext.RendererContextWrapper;
import org.apache.fop.util.UnitConv;
@@ -49,9 +47,11 @@ public abstract class AbstractGraphics2DAdapter implements Graphics2DAdapter {
* @param context the renderer context for the current renderer
* @param resolution the requested bitmap resolution
* @param gray true if the generated image should be in grayscales
+ * @param withAlpha true if an alpha channel should be created
* @return the generated BufferedImage
*/
- protected BufferedImage paintToBufferedImage(Graphics2DImagePainter painter,
+ protected BufferedImage paintToBufferedImage(
+ org.apache.xmlgraphics.java2d.Graphics2DImagePainter painter,
RendererContextWrapper context, int resolution, boolean gray, boolean withAlpha) {
int bmw = (int)Math.ceil(UnitConv.mpt2px(context.getWidth(), resolution));
int bmh = (int)Math.ceil(UnitConv.mpt2px(context.getHeight(), resolution));
diff --git a/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java b/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java
index 4e5edece6..ac11d56d5 100644
--- a/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java
+++ b/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java
@@ -25,6 +25,10 @@ import java.awt.geom.Rectangle2D;
import java.util.List;
import java.util.Map;
+import org.w3c.dom.Document;
+
+import org.apache.xmlgraphics.image.loader.ImageSize;
+
import org.apache.fop.area.Area;
import org.apache.fop.area.Block;
import org.apache.fop.area.BlockViewport;
@@ -36,9 +40,7 @@ import org.apache.fop.area.inline.InlineArea;
import org.apache.fop.area.inline.Viewport;
import org.apache.fop.fo.Constants;
import org.apache.fop.fonts.FontMetrics;
-import org.apache.fop.image.FopImage;
import org.apache.fop.traits.BorderProps;
-import org.w3c.dom.Document;
/**
* Abstract base class for renderers like PDF and PostScript where many painting operations
@@ -151,51 +153,47 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer {
updateColor(back.getColor(), true);
fillRect(sx, sy, paddRectWidth, paddRectHeight);
}
- if (back.getFopImage() != null) {
- FopImage fopimage = back.getFopImage();
- if (fopimage != null && fopimage.load(FopImage.DIMENSIONS)) {
- saveGraphicsState();
- clipRect(sx, sy, paddRectWidth, paddRectHeight);
- int horzCount = (int)((paddRectWidth
- * 1000 / fopimage.getIntrinsicWidth()) + 1.0f);
- int vertCount = (int)((paddRectHeight
- * 1000 / fopimage.getIntrinsicHeight()) + 1.0f);
- if (back.getRepeat() == EN_NOREPEAT) {
- horzCount = 1;
- vertCount = 1;
- } else if (back.getRepeat() == EN_REPEATX) {
- vertCount = 1;
- } else if (back.getRepeat() == EN_REPEATY) {
- horzCount = 1;
- }
- //change from points to millipoints
- sx *= 1000;
- sy *= 1000;
- if (horzCount == 1) {
- sx += back.getHoriz();
- }
- if (vertCount == 1) {
- sy += back.getVertical();
- }
- for (int x = 0; x < horzCount; x++) {
- for (int y = 0; y < vertCount; y++) {
- // place once
- Rectangle2D pos;
- // Image positions are relative to the currentIP/BP
- pos = new Rectangle2D.Float(sx - currentIPPosition
- + (x * fopimage.getIntrinsicWidth()),
- sy - currentBPPosition
- + (y * fopimage.getIntrinsicHeight()),
- fopimage.getIntrinsicWidth(),
- fopimage.getIntrinsicHeight());
- drawImage(back.getURL(), pos);
- }
+ if (back.getImageInfo() != null) {
+ ImageSize imageSize = back.getImageInfo().getSize();
+ saveGraphicsState();
+ clipRect(sx, sy, paddRectWidth, paddRectHeight);
+ int horzCount = (int)((paddRectWidth
+ * 1000 / imageSize.getWidthMpt()) + 1.0f);
+ int vertCount = (int)((paddRectHeight
+ * 1000 / imageSize.getHeightMpt()) + 1.0f);
+ if (back.getRepeat() == EN_NOREPEAT) {
+ horzCount = 1;
+ vertCount = 1;
+ } else if (back.getRepeat() == EN_REPEATX) {
+ vertCount = 1;
+ } else if (back.getRepeat() == EN_REPEATY) {
+ horzCount = 1;
+ }
+ //change from points to millipoints
+ sx *= 1000;
+ sy *= 1000;
+ if (horzCount == 1) {
+ sx += back.getHoriz();
+ }
+ if (vertCount == 1) {
+ sy += back.getVertical();
+ }
+ for (int x = 0; x < horzCount; x++) {
+ for (int y = 0; y < vertCount; y++) {
+ // place once
+ Rectangle2D pos;
+ // Image positions are relative to the currentIP/BP
+ pos = new Rectangle2D.Float(sx - currentIPPosition
+ + (x * imageSize.getWidthMpt()),
+ sy - currentBPPosition
+ + (y * imageSize.getHeightMpt()),
+ imageSize.getWidthMpt(),
+ imageSize.getHeightMpt());
+ drawImage(back.getURL(), pos);
}
-
- restoreGraphicsState();
- } else {
- log.warn("Can't find background image: " + back.getURL());
}
+
+ restoreGraphicsState();
}
}
diff --git a/src/java/org/apache/fop/render/Graphics2DAdapter.java b/src/java/org/apache/fop/render/Graphics2DAdapter.java
index 2509b3864..e9b00f103 100644
--- a/src/java/org/apache/fop/render/Graphics2DAdapter.java
+++ b/src/java/org/apache/fop/render/Graphics2DAdapter.java
@@ -43,7 +43,7 @@ public interface Graphics2DAdapter {
* @param height height of the image
* @throws IOException In case of an I/O error while writing the output format
*/
- void paintImage(Graphics2DImagePainter painter,
+ void paintImage(org.apache.xmlgraphics.java2d.Graphics2DImagePainter painter,
RendererContext context,
int x, int y, int width, int height) throws IOException;
diff --git a/src/java/org/apache/fop/render/Graphics2DImagePainter.java b/src/java/org/apache/fop/render/Graphics2DImagePainter.java
index 3039a72a7..9301a6962 100644
--- a/src/java/org/apache/fop/render/Graphics2DImagePainter.java
+++ b/src/java/org/apache/fop/render/Graphics2DImagePainter.java
@@ -19,27 +19,12 @@
package org.apache.fop.render;
-import java.awt.Dimension;
-import java.awt.Graphics2D;
-import java.awt.geom.Rectangle2D;
-
/**
* This interface is used by the Graphics2DAdapter. Components that can paint using
* a Graphics2D instance can implement this interface to paint themselves.
+ * @deprecated use {@link org.apache.xmlgraphics.java2d.Graphics2DImagePainter} directly!
*/
-public interface Graphics2DImagePainter {
-
- /**
- * Called to paint the image. Implementations should scale so the image is
- * painted fully inside the given area indicated by then Rectangle2D object.
- * @param g2d the Graphics2D instance to paint on
- * @param area the target area for the image
- */
- void paint(Graphics2D g2d, Rectangle2D area);
+public interface Graphics2DImagePainter
+ extends org.apache.xmlgraphics.java2d.Graphics2DImagePainter {
- /**
- * @return the dimensions of the image to be painted in millipoints
- */
- Dimension getImageSize();
-
} \ No newline at end of file
diff --git a/src/java/org/apache/fop/render/afp/AFPGraphics2DAdapter.java b/src/java/org/apache/fop/render/afp/AFPGraphics2DAdapter.java
index 8fa0d0f72..687a0373e 100644
--- a/src/java/org/apache/fop/render/afp/AFPGraphics2DAdapter.java
+++ b/src/java/org/apache/fop/render/afp/AFPGraphics2DAdapter.java
@@ -22,8 +22,9 @@ package org.apache.fop.render.afp;
import java.awt.image.BufferedImage;
import java.io.IOException;
+import org.apache.xmlgraphics.java2d.Graphics2DImagePainter;
+
import org.apache.fop.render.AbstractGraphics2DAdapter;
-import org.apache.fop.render.Graphics2DImagePainter;
import org.apache.fop.render.RendererContext;
/**
diff --git a/src/java/org/apache/fop/render/afp/AFPRenderer.java b/src/java/org/apache/fop/render/afp/AFPRenderer.java
index de414fa10..cdb2f2ec8 100644
--- a/src/java/org/apache/fop/render/afp/AFPRenderer.java
+++ b/src/java/org/apache/fop/render/afp/AFPRenderer.java
@@ -20,10 +20,13 @@
package org.apache.fop.render.afp;
import java.awt.Color;
+import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
-import java.awt.image.BufferedImage;
+import java.awt.image.RenderedImage;
+import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
@@ -32,7 +35,22 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.ByteArrayOutputStream;
+
+import org.apache.xmlgraphics.image.codec.tiff.TIFFImage;
+import org.apache.xmlgraphics.image.loader.ImageException;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.ImageInfo;
+import org.apache.xmlgraphics.image.loader.ImageManager;
+import org.apache.xmlgraphics.image.loader.ImageSessionContext;
+import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawCCITTFax;
+import org.apache.xmlgraphics.image.loader.impl.ImageRendered;
+import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM;
+import org.apache.xmlgraphics.image.loader.util.ImageUtil;
+import org.apache.xmlgraphics.ps.ImageEncodingHelper;
+
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.MimeConstants;
import org.apache.fop.area.Block;
@@ -49,6 +67,7 @@ import org.apache.fop.area.inline.Leader;
import org.apache.fop.area.inline.SpaceArea;
import org.apache.fop.area.inline.TextArea;
import org.apache.fop.area.inline.WordArea;
+import org.apache.fop.datatypes.URISpecification;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.extensions.ExtensionAttachment;
import org.apache.fop.fonts.FontInfo;
@@ -56,10 +75,6 @@ import org.apache.fop.fonts.FontTriplet;
import org.apache.fop.fonts.base14.Courier;
import org.apache.fop.fonts.base14.Helvetica;
import org.apache.fop.fonts.base14.TimesRoman;
-import org.apache.fop.image.FopImage;
-import org.apache.fop.image.ImageFactory;
-import org.apache.fop.image.TIFFImage;
-import org.apache.fop.image.XMLImage;
import org.apache.fop.render.AbstractPathOrientedRenderer;
import org.apache.fop.render.Graphics2DAdapter;
import org.apache.fop.render.RendererContext;
@@ -74,7 +89,6 @@ import org.apache.fop.render.afp.modca.AFPConstants;
import org.apache.fop.render.afp.modca.AFPDataStream;
import org.apache.fop.render.afp.modca.ImageObject;
import org.apache.fop.render.afp.modca.PageObject;
-import org.w3c.dom.Document;
/**
* This is an implementation of a FOP Renderer that renders areas to AFP.
@@ -735,20 +749,117 @@ public class AFPRenderer extends AbstractPathOrientedRenderer {
return context;
}
- /**
- * {@inheritDoc}
- */
- public void drawImage(String url, Rectangle2D pos, Map foreignAttributes) {
+ private static final ImageFlavor[] FLAVORS = new ImageFlavor[]
+ {ImageFlavor.RAW_CCITTFAX,
+ ImageFlavor.GRAPHICS2D,
+ ImageFlavor.BUFFERED_IMAGE,
+ ImageFlavor.RENDERED_IMAGE,
+ ImageFlavor.XML_DOM};
+
+ /** {@inheritDoc} */
+ public void drawImage(String uri, Rectangle2D pos, Map foreignAttributes) {
+ uri = URISpecification.getURL(uri);
+ Rectangle posInt = new Rectangle(
+ (int)pos.getX(),
+ (int)pos.getY(),
+ (int)pos.getWidth(),
+ (int)pos.getHeight());
+ Point origin = new Point(currentIPPosition, currentBPPosition);
+ int x = origin.x + posInt.x;
+ int y = origin.y + posInt.y;
+
String name = null;
if (pageSegmentsMap != null) {
- name = (String) pageSegmentsMap.get(url);
+ name = (String) pageSegmentsMap.get(uri);
}
if (name != null) {
- int x = mpts2units(pos.getX() + currentIPPosition);
- int y = mpts2units(pos.getY() + currentBPPosition);
- afpDataStream.createIncludePageSegment(name, x, y);
+ afpDataStream.createIncludePageSegment(name, mpts2units(x), mpts2units(y));
} else {
- url = ImageFactory.getURL(url);
+ ImageManager manager = getUserAgent().getFactory().getImageManager();
+ ImageInfo info = null;
+ try {
+ ImageSessionContext sessionContext = getUserAgent().getImageSessionContext();
+ info = manager.getImageInfo(uri, sessionContext);
+
+ //Only now fully load/prepare the image
+ Map hints = ImageUtil.getDefaultHints(sessionContext);
+ org.apache.xmlgraphics.image.loader.Image img = manager.getImage(
+ info, FLAVORS, hints, sessionContext);
+
+ //...and process the image
+ if (img instanceof ImageGraphics2D) {
+ ImageGraphics2D imageG2D = (ImageGraphics2D)img;
+ RendererContext context = createRendererContext(
+ posInt.x, posInt.y,
+ posInt.width, posInt.height, foreignAttributes);
+ getGraphics2DAdapter().paintImage(imageG2D.getGraphics2DImagePainter(),
+ context,
+ origin.x + posInt.x, origin.y + posInt.y,
+ posInt.width, posInt.height);
+ } else if (img instanceof ImageRendered) {
+ ImageRendered imgRend = (ImageRendered)img;
+ RenderedImage ri = imgRend.getRenderedImage();
+
+ drawBufferedImage(ri, getResolution(),
+ posInt.x + currentIPPosition,
+ posInt.y + currentBPPosition,
+ posInt.width,
+ posInt.height);
+ } else if (img instanceof ImageRawCCITTFax) {
+ ImageRawCCITTFax ccitt = (ImageRawCCITTFax)img;
+ int afpx = mpts2units(posInt.x + currentIPPosition);
+ int afpy = mpts2units(posInt.y + currentBPPosition);
+ int afpw = mpts2units(posInt.getWidth());
+ int afph = mpts2units(posInt.getHeight());
+ int afpres = getResolution();
+ ImageObject io = afpDataStream.getImageObject(afpx, afpy, afpw, afph,
+ afpres, afpres);
+ io.setImageParameters(
+ (int) (ccitt.getSize().getDpiHorizontal() * 10),
+ (int) (ccitt.getSize().getDpiVertical() * 10),
+ ccitt.getSize().getWidthPx(),
+ ccitt.getSize().getHeightPx());
+ int compression = ccitt.getCompression();
+ switch (compression) {
+ case TIFFImage.COMP_FAX_G3_1D :
+ io.setImageEncoding((byte) 0x80);
+ break;
+ case TIFFImage.COMP_FAX_G3_2D :
+ io.setImageEncoding((byte) 0x81);
+ break;
+ case TIFFImage.COMP_FAX_G4_2D :
+ io.setImageEncoding((byte) 0x82);
+ break;
+ default:
+ throw new IllegalStateException(
+ "Invalid compression scheme: " + compression);
+ }
+ InputStream in = ccitt.createInputStream();
+ try {
+ byte[] buf = IOUtils.toByteArray(in);
+ io.setImageData(buf);
+ } finally {
+ IOUtils.closeQuietly(in);
+ }
+ } else if (img instanceof ImageXMLDOM) {
+ ImageXMLDOM imgXML = (ImageXMLDOM)img;
+ renderDocument(imgXML.getDocument(), imgXML.getRootNamespace(),
+ pos, foreignAttributes);
+ } else {
+ throw new UnsupportedOperationException("Unsupported image type: " + img);
+ }
+
+ } catch (ImageException ie) {
+ log.error("Error while processing image: "
+ + (info != null ? info.toString() : uri), ie);
+ } catch (FileNotFoundException fe) {
+ log.error(fe.getMessage());
+ } catch (IOException ioe) {
+ log.error("I/O error while processing image: "
+ + (info != null ? info.toString() : uri), ioe);
+ }
+
+ /*
ImageFactory fact = userAgent.getFactory().getImageFactory();
FopImage fopimage = fact.getImage(url, userAgent);
if (fopimage == null) {
@@ -768,6 +879,7 @@ public class AFPRenderer extends AbstractPathOrientedRenderer {
renderDocument(doc, ns, pos, foreignAttributes);
} else if (MimeConstants.MIME_EPS.equals(mime)) {
log.warn("EPS images are not supported by this renderer");
+ */
/*
* } else if (MimeConstants.MIME_JPEG.equals(mime)) { if
* (!fopimage.load(FopImage.ORIGINAL_DATA)) { return; }
@@ -785,7 +897,7 @@ public class AFPRenderer extends AbstractPathOrientedRenderer {
* io.setImageIDESize((byte)fopimage.getBitsPerPixel());
* io.setImageEncoding((byte)0x83);
* io.setImageData(fopimage.getRessourceBytes());
- */
+ *//*
} else if (MimeConstants.MIME_TIFF.equals(mime)
&& fopimage instanceof TIFFImage) {
TIFFImage tiffImage = (TIFFImage) fopimage;
@@ -852,43 +964,30 @@ public class AFPRenderer extends AbstractPathOrientedRenderer {
convertToGrayScaleImage(io, fopimage.getBitmaps(), fopimage
.getWidth(), fopimage.getHeight(), this.bitsPerPixel);
}
- }
+ }*/
}
}
/**
- * Writes a BufferedImage to an OutputStream as raw sRGB bitmaps.
+ * Writes a RenderedImage to an OutputStream as raw sRGB bitmaps.
*
- * @param img
- * the BufferedImage
+ * @param image
+ * the RenderedImage
* @param out
* the OutputStream
* @throws IOException
* In case of an I/O error.
*/
- public static void writeImage(BufferedImage img, OutputStream out)
+ public static void writeImage(RenderedImage image, OutputStream out)
throws IOException {
- int w = img.getWidth();
- int h = img.getHeight();
- int[] tmpMap = img.getRGB(0, 0, w, h, null, 0, w);
- for (int i = 0; i < h; i++) {
- for (int j = 0; j < w; j++) {
- int p = tmpMap[i * w + j];
- int r = (p >> 16) & 0xFF;
- int g = (p >> 8) & 0xFF;
- int b = (p) & 0xFF;
- out.write((byte) (r & 0xFF));
- out.write((byte) (g & 0xFF));
- out.write((byte) (b & 0xFF));
- }
- }
+ ImageEncodingHelper.encodeRenderedImageAsRGB(image, out);
}
/**
* Draws a BufferedImage to AFP.
*
- * @param bi
- * the BufferedImage
+ * @param image
+ * the RenderedImage
* @param imageResolution
* the resolution of the BufferedImage
* @param x
@@ -900,7 +999,7 @@ public class AFPRenderer extends AbstractPathOrientedRenderer {
* @param h
* the height of the viewport (in mpt)
*/
- public void drawBufferedImage(BufferedImage bi, int imageResolution, int x,
+ public void drawBufferedImage(RenderedImage image, int imageResolution, int x,
int y, int w, int h) {
int afpx = mpts2units(x);
int afpy = mpts2units(y);
@@ -910,21 +1009,24 @@ public class AFPRenderer extends AbstractPathOrientedRenderer {
ByteArrayOutputStream baout = new ByteArrayOutputStream();
try {
// Serialize image
- writeImage(bi, baout);
+ //TODO Eventually, this should be changed not to buffer as this increases the
+ //memory consumption (see PostScript output)
+ writeImage(image, baout);
byte[] buf = baout.toByteArray();
// Generate image
ImageObject io = afpDataStream.getImageObject(afpx, afpy, afpw,
afph, afpres, afpres);
io.setImageParameters(imageResolution, imageResolution,
- bi.getWidth(), bi.getHeight());
+ image.getWidth(), image.getHeight());
if (colorImages) {
io.setImageIDESize((byte)24);
io.setImageData(buf);
} else {
// TODO Teach it how to handle grayscale BufferedImages directly
// because this is pretty inefficient
- convertToGrayScaleImage(io, buf, bi.getWidth(), bi.getHeight(), this.bitsPerPixel);
+ convertToGrayScaleImage(io, buf,
+ image.getWidth(), image.getHeight(), this.bitsPerPixel);
}
} catch (IOException ioe) {
log.error("Error while serializing bitmap: " + ioe.getMessage(),
diff --git a/src/java/org/apache/fop/render/java2d/Java2DGraphics2DAdapter.java b/src/java/org/apache/fop/render/java2d/Java2DGraphics2DAdapter.java
index f74df6cff..1182fe42d 100644
--- a/src/java/org/apache/fop/render/java2d/Java2DGraphics2DAdapter.java
+++ b/src/java/org/apache/fop/render/java2d/Java2DGraphics2DAdapter.java
@@ -21,12 +21,14 @@ package org.apache.fop.render.java2d;
import java.awt.Color;
import java.awt.Dimension;
+import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
+import org.apache.xmlgraphics.java2d.Graphics2DImagePainter;
+
import org.apache.fop.render.Graphics2DAdapter;
-import org.apache.fop.render.Graphics2DImagePainter;
import org.apache.fop.render.RendererContext;
/**
@@ -34,16 +36,6 @@ import org.apache.fop.render.RendererContext;
*/
public class Java2DGraphics2DAdapter implements Graphics2DAdapter {
- private Java2DGraphicsState state;
-
- /**
- * Main constructor
- * @param state the state tracker for this rendering run
- */
- public Java2DGraphics2DAdapter(Java2DGraphicsState state) {
- this.state = state;
- }
-
/** {@inheritDoc} */
public void paintImage(Graphics2DImagePainter painter,
RendererContext context,
@@ -63,26 +55,28 @@ public class Java2DGraphics2DAdapter implements Graphics2DAdapter {
float sy = fheight / (float)imh;
Java2DRenderer renderer = (Java2DRenderer)context.getRenderer();
- renderer.saveGraphicsState();
- state.getGraph().setColor(Color.black);
- state.getGraph().setBackground(Color.black);
+ Java2DGraphicsState state = renderer.state;
+
+ //Create copy and paint on that
+ Graphics2D g2d = (Graphics2D)state.getGraph().create();
+ g2d.setColor(Color.black);
+ g2d.setBackground(Color.black);
//TODO Clip to the image area.
// transform so that the coordinates (0,0) is from the top left
// and positive is down and to the right. (0,0) is where the
// viewBox puts it.
- state.getGraph().translate(fx, fy);
+ g2d.translate(fx, fy);
AffineTransform at = AffineTransform.getScaleInstance(sx, sy);
if (!at.isIdentity()) {
- state.getGraph().transform(at);
+ g2d.transform(at);
}
Rectangle2D area = new Rectangle2D.Double(0.0, 0.0, imw, imh);
- painter.paint(state.getGraph(), area);
+ painter.paint(g2d, area);
- renderer.restoreGraphicsState();
-
+ g2d.dispose();
}
}
diff --git a/src/java/org/apache/fop/render/java2d/Java2DRenderer.java b/src/java/org/apache/fop/render/java2d/Java2DRenderer.java
index eededf13b..0f73c5761 100644
--- a/src/java/org/apache/fop/render/java2d/Java2DRenderer.java
+++ b/src/java/org/apache/fop/render/java2d/Java2DRenderer.java
@@ -25,7 +25,6 @@ import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
-import java.awt.color.ColorSpace;
import java.awt.font.GlyphVector;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
@@ -33,14 +32,6 @@ import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
-import java.awt.image.ColorModel;
-import java.awt.image.ComponentColorModel;
-import java.awt.image.DataBuffer;
-import java.awt.image.DataBufferByte;
-import java.awt.image.PixelInterleavedSampleModel;
-import java.awt.image.Raster;
-import java.awt.image.SampleModel;
-import java.awt.image.WritableRaster;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
@@ -51,7 +42,15 @@ import java.util.List;
import java.util.Map;
import java.util.Stack;
-import org.w3c.dom.Document;
+import org.apache.xmlgraphics.image.loader.ImageException;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.ImageInfo;
+import org.apache.xmlgraphics.image.loader.ImageManager;
+import org.apache.xmlgraphics.image.loader.ImageSessionContext;
+import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D;
+import org.apache.xmlgraphics.image.loader.impl.ImageRendered;
+import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM;
+import org.apache.xmlgraphics.image.loader.util.ImageUtil;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
@@ -65,13 +64,11 @@ import org.apache.fop.area.inline.Leader;
import org.apache.fop.area.inline.SpaceArea;
import org.apache.fop.area.inline.TextArea;
import org.apache.fop.area.inline.WordArea;
+import org.apache.fop.datatypes.URISpecification;
import org.apache.fop.fo.Constants;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.fonts.Typeface;
-import org.apache.fop.image.FopImage;
-import org.apache.fop.image.ImageFactory;
-import org.apache.fop.image.XMLImage;
import org.apache.fop.render.AbstractPathOrientedRenderer;
import org.apache.fop.render.Graphics2DAdapter;
import org.apache.fop.render.RendererContext;
@@ -184,7 +181,7 @@ public abstract class Java2DRenderer extends AbstractPathOrientedRenderer implem
/** {@inheritDoc} */
public Graphics2DAdapter getGraphics2DAdapter() {
- return new Java2DGraphics2DAdapter(state);
+ return new Java2DGraphics2DAdapter();
}
/**
@@ -884,76 +881,58 @@ public abstract class Java2DRenderer extends AbstractPathOrientedRenderer implem
/**
* {@inheritDoc}
*/
- protected void drawImage(String url, Rectangle2D pos, Map foreignAttributes) {
+ protected void drawImage(String uri, Rectangle2D pos, Map foreignAttributes) {
int x = currentIPPosition + (int)Math.round(pos.getX());
int y = currentBPPosition + (int)Math.round(pos.getY());
- url = ImageFactory.getURL(url);
-
- ImageFactory fact = userAgent.getFactory().getImageFactory();
- FopImage fopimage = fact.getImage(url, userAgent);
-
- if (fopimage == null) {
- return;
- }
- if (!fopimage.load(FopImage.DIMENSIONS)) {
- return;
- }
- int w = fopimage.getWidth();
- int h = fopimage.getHeight();
- String mime = fopimage.getMimeType();
- if ("text/xml".equals(mime)) {
- if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
- return;
- }
- Document doc = ((XMLImage) fopimage).getDocument();
- String ns = ((XMLImage) fopimage).getNameSpace();
- renderDocument(doc, ns, pos, foreignAttributes);
-
- } else if ("image/svg+xml".equals(mime)) {
- if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
- return;
- }
- Document doc = ((XMLImage) fopimage).getDocument();
- String ns = ((XMLImage) fopimage).getNameSpace();
-
- renderDocument(doc, ns, pos, foreignAttributes);
- } else if ("image/eps".equals(mime)) {
- log.warn("EPS images are not supported by this renderer");
- } else {
- if (!fopimage.load(FopImage.BITMAP)) {
- log.warn("Loading of bitmap failed: " + url);
- return;
+ uri = URISpecification.getURL(uri);
+
+ ImageManager manager = getUserAgent().getFactory().getImageManager();
+ ImageInfo info = null;
+ try {
+ ImageSessionContext sessionContext = getUserAgent().getImageSessionContext();
+ info = manager.getImageInfo(uri, sessionContext);
+ final ImageFlavor[] flavors = new ImageFlavor[]
+ {ImageFlavor.GRAPHICS2D,
+ ImageFlavor.BUFFERED_IMAGE,
+ ImageFlavor.RENDERED_IMAGE,
+ ImageFlavor.XML_DOM};
+ Map hints = ImageUtil.getDefaultHints(sessionContext);
+ org.apache.xmlgraphics.image.loader.Image img = manager.getImage(
+ info, flavors, hints, sessionContext);
+ if (img instanceof ImageGraphics2D) {
+ ImageGraphics2D imageG2D = (ImageGraphics2D)img;
+ int width = (int)pos.getWidth();
+ int height = (int)pos.getHeight();
+ RendererContext context = createRendererContext(
+ x, y, width, height, foreignAttributes);
+ getGraphics2DAdapter().paintImage(imageG2D.getGraphics2DImagePainter(),
+ context, x, y, width, height);
+ } else if (img instanceof ImageRendered) {
+ ImageRendered imgRend = (ImageRendered)img;
+ AffineTransform at = new AffineTransform();
+ at.translate(x / 1000f, y / 1000f);
+ double sx = pos.getWidth() / info.getSize().getWidthMpt();
+ double sy = pos.getHeight() / info.getSize().getHeightMpt();
+ sx *= userAgent.getSourceResolution() / info.getSize().getDpiHorizontal();
+ sy *= userAgent.getSourceResolution() / info.getSize().getDpiVertical();
+ at.scale(sx, sy);
+ state.getGraph().drawRenderedImage(imgRend.getRenderedImage(), at);
+ } else if (img instanceof ImageXMLDOM) {
+ ImageXMLDOM imgXML = (ImageXMLDOM)img;
+ renderDocument(imgXML.getDocument(), imgXML.getRootNamespace(),
+ pos, foreignAttributes);
}
-
- byte[] raw = fopimage.getBitmaps();
-
- // TODO Hardcoded color and sample models, FIX ME!
- ColorModel cm = new ComponentColorModel(
- ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB),
- new int[] {8, 8, 8},
- false, false,
- ColorModel.OPAQUE, DataBuffer.TYPE_BYTE);
- SampleModel sampleModel = new PixelInterleavedSampleModel(
- DataBuffer.TYPE_BYTE, w, h, 3, w * 3, new int[] {0, 1, 2});
- DataBuffer dbuf = new DataBufferByte(raw, w * h * 3);
-
- WritableRaster raster = Raster.createWritableRaster(sampleModel,
- dbuf, null);
-
- java.awt.Image awtImage;
- // Combine the color model and raster into a buffered image
- awtImage = new BufferedImage(cm, raster, false, null);
-
- state.getGraph().drawImage(awtImage,
- (int)(x / 1000f), (int)(y / 1000f),
- (int)(pos.getWidth() / 1000f), (int)(pos.getHeight() / 1000f), null);
+ } catch (ImageException ie) {
+ log.error("Error while processing image: "
+ + (info != null ? info.toString() : uri), ie);
+ } catch (IOException ioe) {
+ log.error("I/O error while processing image: "
+ + (info != null ? info.toString() : uri), ioe);
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected RendererContext createRendererContext(int x, int y, int width, int height,
Map foreignAttributes) {
RendererContext context = super.createRendererContext(
@@ -962,9 +941,7 @@ public abstract class Java2DRenderer extends AbstractPathOrientedRenderer implem
return context;
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int print(Graphics g, PageFormat pageFormat, int pageIndex)
throws PrinterException {
if (pageIndex >= getNumberOfPages()) {
@@ -1004,6 +981,10 @@ public abstract class Java2DRenderer extends AbstractPathOrientedRenderer implem
//not necessary in Java2D
}
+ /**
+ * Controls the page background.
+ * @param transparentPageBackground true if the background should be transparent
+ */
public void setTransparentPageBackground(boolean transparentPageBackground) {
this.transparentPageBackground = transparentPageBackground;
}
diff --git a/src/java/org/apache/fop/render/pcl/PCLGraphics2DAdapter.java b/src/java/org/apache/fop/render/pcl/PCLGraphics2DAdapter.java
index 80c010afc..06a4d37a0 100644
--- a/src/java/org/apache/fop/render/pcl/PCLGraphics2DAdapter.java
+++ b/src/java/org/apache/fop/render/pcl/PCLGraphics2DAdapter.java
@@ -28,11 +28,13 @@ import java.io.IOException;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
+import org.apache.xmlgraphics.java2d.GraphicContext;
+import org.apache.xmlgraphics.java2d.Graphics2DImagePainter;
+
import org.apache.fop.render.AbstractGraphics2DAdapter;
-import org.apache.fop.render.Graphics2DImagePainter;
import org.apache.fop.render.RendererContext;
import org.apache.fop.util.UnitConv;
-import org.apache.xmlgraphics.java2d.GraphicContext;
/**
* Graphics2DAdapter implementation for PCL and HP GL/2.
@@ -110,7 +112,8 @@ public class PCLGraphics2DAdapter extends AbstractGraphics2DAdapter {
if (!painted) {
//Fallback solution: Paint to a BufferedImage
int resolution = (int)Math.round(context.getUserAgent().getTargetResolution());
- BufferedImage bi = paintToBufferedImage(painter, pclContext, resolution, true, false);
+ BufferedImage bi = paintToBufferedImage(painter, pclContext,
+ resolution, !pclContext.isColorCanvas(), false);
pcl.setCursorPos(x, y);
gen.paintBitmap(bi, new Dimension(width, height), pclContext.isSourceTransparency());
diff --git a/src/java/org/apache/fop/render/pcl/PCLRenderer.java b/src/java/org/apache/fop/render/pcl/PCLRenderer.java
index 732ce0f8f..b48c28089 100644
--- a/src/java/org/apache/fop/render/pcl/PCLRenderer.java
+++ b/src/java/org/apache/fop/render/pcl/PCLRenderer.java
@@ -24,24 +24,17 @@ import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
+import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
-import java.awt.color.ColorSpace;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
-import java.awt.image.ColorModel;
-import java.awt.image.ComponentColorModel;
-import java.awt.image.DataBuffer;
-import java.awt.image.DataBufferByte;
-import java.awt.image.PixelInterleavedSampleModel;
-import java.awt.image.Raster;
import java.awt.image.RenderedImage;
-import java.awt.image.SampleModel;
-import java.awt.image.WritableRaster;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
@@ -52,7 +45,19 @@ import org.w3c.dom.Document;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
+import org.apache.xmlgraphics.image.loader.ImageException;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.ImageInfo;
+import org.apache.xmlgraphics.image.loader.ImageManager;
+import org.apache.xmlgraphics.image.loader.ImageSessionContext;
+import org.apache.xmlgraphics.image.loader.ImageSize;
+import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D;
+import org.apache.xmlgraphics.image.loader.impl.ImageRendered;
+import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM;
+import org.apache.xmlgraphics.image.loader.util.ImageUtil;
import org.apache.xmlgraphics.java2d.GraphicContext;
+import org.apache.xmlgraphics.java2d.Graphics2DImagePainter;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.MimeConstants;
@@ -70,16 +75,12 @@ import org.apache.fop.area.inline.SpaceArea;
import org.apache.fop.area.inline.TextArea;
import org.apache.fop.area.inline.Viewport;
import org.apache.fop.area.inline.WordArea;
+import org.apache.fop.datatypes.URISpecification;
import org.apache.fop.fo.extensions.ExtensionElementMapping;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.fonts.FontMetrics;
-import org.apache.fop.image.EPSImage;
-import org.apache.fop.image.FopImage;
-import org.apache.fop.image.ImageFactory;
-import org.apache.fop.image.XMLImage;
import org.apache.fop.render.Graphics2DAdapter;
-import org.apache.fop.render.Graphics2DImagePainter;
import org.apache.fop.render.PrintRenderer;
import org.apache.fop.render.RendererContext;
import org.apache.fop.render.RendererContextConstants;
@@ -135,6 +136,13 @@ public class PCLRenderer extends PrintRenderer {
private boolean allTextAsBitmaps = false;
/**
+ * Controls whether an RGB canvas is used when converting Java2D graphics to bitmaps.
+ * This can be used to work around problems with Apache Batik, for example, but setting
+ * this to true will increase memory consumption.
+ */
+ private boolean useColorCanvas = false;
+
+ /**
* Controls whether the generation of PJL commands gets disabled.
*/
private boolean disabledPJL = false;
@@ -991,87 +999,88 @@ public class PCLRenderer extends PrintRenderer {
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
+ protected RendererContext createRendererContext(int x, int y, int width, int height,
+ Map foreignAttributes) {
+ RendererContext context = super.createRendererContext(
+ x, y, width, height, foreignAttributes);
+ context.setProperty(PCLRendererContextConstants.PCL_COLOR_CANVAS,
+ Boolean.valueOf(this.useColorCanvas));
+ return context;
+ }
+
+ /** {@inheritDoc} */
public void renderImage(Image image, Rectangle2D pos) {
drawImage(image.getURL(), pos, image.getForeignAttributes());
}
+ private static final ImageFlavor[] FLAVORS = new ImageFlavor[]
+ {ImageFlavor.GRAPHICS2D,
+ ImageFlavor.BUFFERED_IMAGE,
+ ImageFlavor.RENDERED_IMAGE,
+ ImageFlavor.XML_DOM};
/**
* Draw an image at the indicated location.
- * @param url the URI/URL of the image
+ * @param uri the URI/URL of the image
* @param pos the position of the image
* @param foreignAttributes an optional Map with foreign attributes, may be null
*/
- protected void drawImage(String url, Rectangle2D pos, Map foreignAttributes) {
- url = ImageFactory.getURL(url);
- ImageFactory fact = userAgent.getFactory().getImageFactory();
- FopImage fopimage = fact.getImage(url, userAgent);
- if (fopimage == null) {
- return;
- }
- if (!fopimage.load(FopImage.DIMENSIONS)) {
- return;
- }
- String mime = fopimage.getMimeType();
- if ("text/xml".equals(mime)) {
- if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
- return;
- }
- Document doc = ((XMLImage) fopimage).getDocument();
- String ns = ((XMLImage) fopimage).getNameSpace();
-
- renderDocument(doc, ns, pos, foreignAttributes);
- } else if ("image/svg+xml".equals(mime)) {
- if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
- return;
- }
- Document doc = ((XMLImage) fopimage).getDocument();
- String ns = ((XMLImage) fopimage).getNameSpace();
-
- renderDocument(doc, ns, pos, foreignAttributes);
- } else if (fopimage instanceof EPSImage) {
- log.warn("EPS images are not supported by this renderer");
- } else {
- if (!fopimage.load(FopImage.BITMAP)) {
- log.error("Bitmap image could not be processed: " + fopimage);
- return;
- }
- byte[] imgmap = fopimage.getBitmaps();
+ protected void drawImage(String uri, Rectangle2D pos, Map foreignAttributes) {
+ uri = URISpecification.getURL(uri);
+ Rectangle posInt = new Rectangle(
+ (int)pos.getX(),
+ (int)pos.getY(),
+ (int)pos.getWidth(),
+ (int)pos.getHeight());
+ Point origin = new Point(currentIPPosition, currentBPPosition);
+ int x = origin.x + posInt.x;
+ int y = origin.y + posInt.y;
+
+ ImageManager manager = getUserAgent().getFactory().getImageManager();
+ ImageInfo info = null;
+ try {
+ ImageSessionContext sessionContext = getUserAgent().getImageSessionContext();
+ info = manager.getImageInfo(uri, sessionContext);
- ColorModel cm = new ComponentColorModel(
- ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB),
- new int[] {8, 8, 8},
- false, false,
- ColorModel.OPAQUE, DataBuffer.TYPE_BYTE);
- int imgw = fopimage.getWidth();
- int imgh = fopimage.getHeight();
- SampleModel sampleModel = new PixelInterleavedSampleModel(
- DataBuffer.TYPE_BYTE, imgw, imgh, 3, imgw * 3, new int[] {0, 1, 2});
- DataBuffer dbuf = new DataBufferByte(imgmap, imgw * imgh * 3);
-
- WritableRaster raster = Raster.createWritableRaster(sampleModel,
- dbuf, null);
-
- // Combine the color model and raster into a buffered image
- RenderedImage img = new BufferedImage(cm, raster, false, null);
-
- try {
- setCursorPos(this.currentIPPosition + (int)pos.getX(),
- this.currentBPPosition + (int)pos.getY());
- gen.paintBitmap(img,
- new Dimension((int)pos.getWidth(), (int)pos.getHeight()),
+ //Only now fully load/prepare the image
+ Map hints = ImageUtil.getDefaultHints(sessionContext);
+ org.apache.xmlgraphics.image.loader.Image img = manager.getImage(
+ info, FLAVORS, hints, sessionContext);
+
+ //...and process the image
+ if (img instanceof ImageGraphics2D) {
+ ImageGraphics2D imageG2D = (ImageGraphics2D)img;
+ RendererContext context = createRendererContext(
+ posInt.x, posInt.y,
+ posInt.width, posInt.height, foreignAttributes);
+ getGraphics2DAdapter().paintImage(imageG2D.getGraphics2DImagePainter(),
+ context, x, y, posInt.width, posInt.height);
+ } else if (img instanceof ImageRendered) {
+ ImageRendered imgRend = (ImageRendered)img;
+ RenderedImage ri = imgRend.getRenderedImage();
+ setCursorPos(x, y);
+ gen.paintBitmap(ri,
+ new Dimension(posInt.width, posInt.height),
false);
- } catch (IOException ioe) {
- handleIOTrouble(ioe);
+ } else if (img instanceof ImageXMLDOM) {
+ ImageXMLDOM imgXML = (ImageXMLDOM)img;
+ renderDocument(imgXML.getDocument(), imgXML.getRootNamespace(),
+ pos, foreignAttributes);
+ } else {
+ throw new UnsupportedOperationException("Unsupported image type: " + img);
}
+
+ } catch (ImageException ie) {
+ log.error("Error while processing image: "
+ + (info != null ? info.toString() : uri), ie);
+ } catch (FileNotFoundException fe) {
+ log.error(fe.getMessage());
+ } catch (IOException ioe) {
+ handleIOTrouble(ioe);
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void renderForeignObject(ForeignObject fo, Rectangle2D pos) {
Document doc = fo.getDocument();
String ns = fo.getNameSpace();
@@ -1153,52 +1162,45 @@ public class PCLRenderer extends PrintRenderer {
}
// background image
- if (back.getFopImage() != null) {
- FopImage fopimage = back.getFopImage();
- if (fopimage != null && fopimage.load(FopImage.DIMENSIONS)) {
- saveGraphicsState();
- clipRect(sx, sy, paddRectWidth, paddRectHeight);
- int horzCount = (int) ((paddRectWidth * 1000 / fopimage
- .getIntrinsicWidth()) + 1.0f);
- int vertCount = (int) ((paddRectHeight * 1000 / fopimage
- .getIntrinsicHeight()) + 1.0f);
- if (back.getRepeat() == EN_NOREPEAT) {
- horzCount = 1;
- vertCount = 1;
- } else if (back.getRepeat() == EN_REPEATX) {
- vertCount = 1;
- } else if (back.getRepeat() == EN_REPEATY) {
- horzCount = 1;
- }
- // change from points to millipoints
- sx *= 1000;
- sy *= 1000;
- if (horzCount == 1) {
- sx += back.getHoriz();
- }
- if (vertCount == 1) {
- sy += back.getVertical();
- }
- for (int x = 0; x < horzCount; x++) {
- for (int y = 0; y < vertCount; y++) {
- // place once
- Rectangle2D pos;
- // Image positions are relative to the currentIP/BP
- pos = new Rectangle2D.Float(
- sx - currentIPPosition
- + (x * fopimage.getIntrinsicWidth()),
- sy - currentBPPosition
- + (y * fopimage.getIntrinsicHeight()),
- fopimage.getIntrinsicWidth(),
- fopimage.getIntrinsicHeight());
- drawImage(back.getURL(), pos, null);
- }
+ if (back.getImageInfo() != null) {
+ ImageSize imageSize = back.getImageInfo().getSize();
+ saveGraphicsState();
+ clipRect(sx, sy, paddRectWidth, paddRectHeight);
+ int horzCount = (int) ((paddRectWidth * 1000 / imageSize.getWidthMpt()) + 1.0f);
+ int vertCount = (int) ((paddRectHeight * 1000 / imageSize.getHeightMpt()) + 1.0f);
+ if (back.getRepeat() == EN_NOREPEAT) {
+ horzCount = 1;
+ vertCount = 1;
+ } else if (back.getRepeat() == EN_REPEATX) {
+ vertCount = 1;
+ } else if (back.getRepeat() == EN_REPEATY) {
+ horzCount = 1;
+ }
+ // change from points to millipoints
+ sx *= 1000;
+ sy *= 1000;
+ if (horzCount == 1) {
+ sx += back.getHoriz();
+ }
+ if (vertCount == 1) {
+ sy += back.getVertical();
+ }
+ for (int x = 0; x < horzCount; x++) {
+ for (int y = 0; y < vertCount; y++) {
+ // place once
+ Rectangle2D pos;
+ // Image positions are relative to the currentIP/BP
+ pos = new Rectangle2D.Float(
+ sx - currentIPPosition
+ + (x * imageSize.getWidthMpt()),
+ sy - currentBPPosition
+ + (y * imageSize.getHeightMpt()),
+ imageSize.getWidthMpt(),
+ imageSize.getHeightMpt());
+ drawImage(back.getURL(), pos, null);
}
- restoreGraphicsState();
- } else {
- log.warn(
- "Can't find background image: " + back.getURL());
}
+ restoreGraphicsState();
}
}
@@ -1508,6 +1510,11 @@ public class PCLRenderer extends PrintRenderer {
}
}
+ /**
+ * Controls whether all text should be generated as bitmaps or only text for which there's
+ * no native font.
+ * @param allTextAsBitmaps true if all text should be painted as bitmaps
+ */
public void setAllTextAsBitmaps(boolean allTextAsBitmaps) {
this.allTextAsBitmaps = allTextAsBitmaps;
}
diff --git a/src/java/org/apache/fop/render/pcl/PCLRendererContext.java b/src/java/org/apache/fop/render/pcl/PCLRendererContext.java
index 32510137b..62d4bcaa4 100644
--- a/src/java/org/apache/fop/render/pcl/PCLRendererContext.java
+++ b/src/java/org/apache/fop/render/pcl/PCLRendererContext.java
@@ -62,11 +62,28 @@ public class PCLRendererContext extends RendererContext.RendererContextWrapper {
&& "true".equalsIgnoreCase((String)getForeignAttributes().get(qName));
}
+ /**
+ * Indicates whether the background should not be erased prior to painting.
+ * @return true if the background shouldn't be erased
+ */
public boolean isSourceTransparency() {
QName qName = new QName(ExtensionElementMapping.URI, null, "source-transparency");
return getForeignAttributes() != null
&& "true".equalsIgnoreCase((String)getForeignAttributes().get(qName));
}
+ /**
+ * Indicates whether an RGB canvas should be used rather than one with grayscales.
+ * This can be used to work around limitations of Apache Batik if you get error while
+ * processing SVG graphics. Note, however, that RGB mode will use more memory.
+ * @return true if an EGB canvas should be used
+ */
+ public boolean isColorCanvas() {
+ QName qName = new QName(ExtensionElementMapping.URI, null, "color-canvas");
+ Boolean prop = (Boolean)context.getProperty(PCLRendererContextConstants.PCL_COLOR_CANVAS);
+ return Boolean.TRUE.equals(prop)
+ || (getForeignAttributes() != null
+ && "true".equalsIgnoreCase((String)getForeignAttributes().get(qName)));
+ }
} \ No newline at end of file
diff --git a/src/java/org/apache/fop/render/pcl/PCLRendererContextConstants.java b/src/java/org/apache/fop/render/pcl/PCLRendererContextConstants.java
new file mode 100644
index 000000000..66ce40f5e
--- /dev/null
+++ b/src/java/org/apache/fop/render/pcl/PCLRendererContextConstants.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pcl;
+
+import org.apache.fop.render.RendererContextConstants;
+
+/**
+ * Defines a number of standard constants (keys) for use by the RendererContext class.
+ */
+public interface PCLRendererContextConstants extends RendererContextConstants {
+
+ /** The PDF document that this image is being drawn into. */
+ String PCL_COLOR_CANVAS = "color-canvas";
+
+}
diff --git a/src/java/org/apache/fop/render/pcl/PCLSVGHandler.java b/src/java/org/apache/fop/render/pcl/PCLSVGHandler.java
index 8c9179063..a016c692f 100644
--- a/src/java/org/apache/fop/render/pcl/PCLSVGHandler.java
+++ b/src/java/org/apache/fop/render/pcl/PCLSVGHandler.java
@@ -22,6 +22,7 @@ package org.apache.fop.render.pcl;
// FOP
import org.apache.fop.render.AbstractGenericSVGHandler;
import org.apache.fop.render.Renderer;
+import org.apache.fop.render.RendererContext;
/**
* PCL XML handler for SVG. Uses Apache Batik for SVG processing.
@@ -36,5 +37,12 @@ public class PCLSVGHandler extends AbstractGenericSVGHandler {
return (renderer instanceof PCLRenderer);
}
+ /** {@inheritDoc} */
+ protected void updateRendererContext(RendererContext context) {
+ //Work around a problem in Batik: Gradients cannot be done in ColorSpace.CS_GRAY
+ context.setProperty(PCLRendererContextConstants.PCL_COLOR_CANVAS,
+ Boolean.TRUE);
+ }
+
}
diff --git a/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java b/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java
new file mode 100644
index 000000000..cd80a6797
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java
@@ -0,0 +1,200 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf;
+import java.awt.color.ColorSpace;
+import java.awt.color.ICC_Profile;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.xmlgraphics.image.loader.Image;
+
+import org.apache.fop.pdf.PDFColor;
+import org.apache.fop.pdf.PDFConformanceException;
+import org.apache.fop.pdf.PDFDeviceColorSpace;
+import org.apache.fop.pdf.PDFDictionary;
+import org.apache.fop.pdf.PDFDocument;
+import org.apache.fop.pdf.PDFICCBasedColorSpace;
+import org.apache.fop.pdf.PDFICCStream;
+import org.apache.fop.pdf.PDFImage;
+import org.apache.fop.pdf.PDFReference;
+import org.apache.fop.util.ColorProfileUtil;
+
+/**
+ * Abstract PDFImage implementation for the PDF renderer.
+ */
+public abstract class AbstractImageAdapter implements PDFImage {
+
+ /** logging instance */
+ private static Log log = LogFactory.getLog(AbstractImageAdapter.class);
+
+ private String key;
+ /** the image */
+ protected Image image;
+
+ private PDFICCStream pdfICCStream = null;
+
+ /**
+ * Creates a new PDFImage from an Image instance.
+ * @param image the image
+ * @param key XObject key
+ */
+ public AbstractImageAdapter(Image image, String key) {
+ this.image = image;
+ this.key = key;
+ }
+
+ /** {@inheritDoc} */
+ public String getKey() {
+ // key to look up XObject
+ return this.key;
+ }
+
+ /**
+ * Returns the image's color space.
+ * @return the color space
+ */
+ protected ColorSpace getImageColorSpace() {
+ return image.getColorSpace();
+ }
+
+ /** {@inheritDoc} */
+ public void setup(PDFDocument doc) {
+
+ ICC_Profile prof = image.getICCProfile();
+ PDFDeviceColorSpace pdfCS = toPDFColorSpace(getImageColorSpace());
+ if (prof != null) {
+ boolean defaultsRGB = ColorProfileUtil.isDefaultsRGB(prof);
+ String desc = ColorProfileUtil.getICCProfileDescription(prof);
+ if (log.isDebugEnabled()) {
+ log.debug("Image returns ICC profile: " + desc + ", default sRGB=" + defaultsRGB);
+ }
+ PDFICCBasedColorSpace cs = doc.getResources().getICCColorSpaceByProfileName(desc);
+ if (!defaultsRGB) {
+ if (cs == null) {
+ pdfICCStream = doc.getFactory().makePDFICCStream();
+ pdfICCStream.setColorSpace(prof, pdfCS);
+ cs = doc.getFactory().makeICCBasedColorSpace(null, null, pdfICCStream);
+ } else {
+ pdfICCStream = cs.getICCStream();
+ }
+ } else {
+ if (cs == null && "sRGB".equals(desc)) {
+ //It's the default sRGB profile which we mapped to DefaultRGB in PDFRenderer
+ cs = doc.getResources().getColorSpace("DefaultRGB");
+ }
+ pdfICCStream = cs.getICCStream();
+ }
+ }
+ if (doc.getProfile().getPDFAMode().isPDFA1LevelB()) {
+ if (pdfCS != null
+ && pdfCS.getColorSpace() != PDFDeviceColorSpace.DEVICE_RGB
+ && pdfCS.getColorSpace() != PDFDeviceColorSpace.DEVICE_GRAY
+ && prof == null) {
+ //See PDF/A-1, ISO 19005:1:2005(E), 6.2.3.3
+ //FOP is currently restricted to DeviceRGB if PDF/A-1 is active.
+ throw new PDFConformanceException(
+ "PDF/A-1 does not allow mixing DeviceRGB and DeviceCMYK: "
+ + image.getInfo());
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ public int getWidth() {
+ return image.getSize().getWidthPx();
+ }
+
+ /** {@inheritDoc} */
+ public int getHeight() {
+ return image.getSize().getHeightPx();
+ }
+
+ /** {@inheritDoc} */
+ public boolean isTransparent() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public PDFColor getTransparentColor() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ public String getMask() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ public String getSoftMask() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ public PDFReference getSoftMaskReference() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isInverted() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isPS() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public PDFICCStream getICCStream() {
+ return pdfICCStream;
+ }
+
+ /** {@inheritDoc} */
+ public void populateXObjectDictionary(PDFDictionary dict) {
+ //nop
+ }
+
+ /**
+ * Converts a ColorSpace object to a PDFColorSpace object.
+ * @param cs ColorSpace instance
+ * @return PDFColorSpace new converted object
+ */
+ public static PDFDeviceColorSpace toPDFColorSpace(ColorSpace cs) {
+ if (cs == null) {
+ return null;
+ }
+
+ PDFDeviceColorSpace pdfCS = new PDFDeviceColorSpace(0);
+ switch (cs.getType()) {
+ case ColorSpace.TYPE_CMYK:
+ pdfCS.setColorSpace(PDFDeviceColorSpace.DEVICE_CMYK);
+ break;
+ case ColorSpace.TYPE_GRAY:
+ pdfCS.setColorSpace(PDFDeviceColorSpace.DEVICE_GRAY);
+ break;
+ default:
+ pdfCS.setColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
+ }
+ return pdfCS;
+ }
+
+}
+
diff --git a/src/java/org/apache/fop/render/pdf/FopPDFImage.java b/src/java/org/apache/fop/render/pdf/FopPDFImage.java
deleted file mode 100644
index e60c207d6..000000000
--- a/src/java/org/apache/fop/render/pdf/FopPDFImage.java
+++ /dev/null
@@ -1,374 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/* $Id$ */
-
-package org.apache.fop.render.pdf;
-import java.awt.color.ColorSpace;
-import java.awt.color.ICC_Profile;
-import java.io.IOException;
-import java.io.OutputStream;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import org.apache.fop.image.EPSImage;
-import org.apache.fop.image.FopImage;
-import org.apache.fop.image.TIFFImage;
-import org.apache.fop.pdf.BitmapImage;
-import org.apache.fop.pdf.CCFFilter;
-import org.apache.fop.pdf.DCTFilter;
-import org.apache.fop.pdf.PDFColor;
-import org.apache.fop.pdf.PDFConformanceException;
-import org.apache.fop.pdf.PDFDeviceColorSpace;
-import org.apache.fop.pdf.PDFDictionary;
-import org.apache.fop.pdf.PDFDocument;
-import org.apache.fop.pdf.PDFFilter;
-import org.apache.fop.pdf.PDFFilterList;
-import org.apache.fop.pdf.PDFICCBasedColorSpace;
-import org.apache.fop.pdf.PDFICCStream;
-import org.apache.fop.pdf.PDFImage;
-import org.apache.fop.pdf.PDFReference;
-import org.apache.fop.util.ColorProfileUtil;
-
-/**
- * PDFImage implementation for the PDF renderer.
- */
-public class FopPDFImage implements PDFImage {
-
- /** logging instance */
- private static Log log = LogFactory.getLog(FopPDFImage.class);
-
- private FopImage fopImage;
- private PDFICCStream pdfICCStream = null;
- private PDFFilter pdfFilter = null;
- private String maskRef;
- private PDFReference softMask;
- private boolean isPS = false;
- private boolean isCCF = false;
- private boolean isDCT = false;
- private String key;
-
- /**
- * Creates a new PDFImage from a FopImage
- * @param image Image
- * @param key XObject key
- */
- public FopPDFImage(FopImage image, String key) {
- fopImage = image;
- this.key = key;
- isPS = (fopImage instanceof EPSImage);
- }
-
- /**
- * {@inheritDoc}
- */
- public String getKey() {
- // key to look up XObject
- return this.key;
- }
-
- /**
- * {@inheritDoc}
- */
- public void setup(PDFDocument doc) {
- if ("image/jpeg".equals(fopImage.getMimeType())) {
- pdfFilter = new DCTFilter();
- pdfFilter.setApplied(true);
- isDCT = true;
-
- } else if ("image/tiff".equals(fopImage.getMimeType())
- && fopImage instanceof TIFFImage) {
- TIFFImage tiffImage = (TIFFImage) fopImage;
- if (tiffImage.getStripCount() == 1) {
- int comp = tiffImage.getCompression();
- if (comp == 1) {
- // Nothing to do
- } else if (comp == 3) {
- pdfFilter = new CCFFilter();
- pdfFilter.setApplied(true);
- isCCF = true;
- } else if (comp == 4) {
- pdfFilter = new CCFFilter();
- pdfFilter.setApplied(true);
- PDFDictionary dict = new PDFDictionary();
- dict.put("K", -1);
- dict.put("Columns", tiffImage.getWidth());
- ((CCFFilter)pdfFilter).setDecodeParms(dict);
- isCCF = true;
- } else if (comp == 6) {
- pdfFilter = new DCTFilter();
- pdfFilter.setApplied(true);
- isDCT = true;
- }
- }
- }
- if (isPS || isDCT || isCCF) {
- fopImage.load(FopImage.ORIGINAL_DATA);
- } else {
- fopImage.load(FopImage.BITMAP);
- }
- ICC_Profile prof = fopImage.getICCProfile();
- PDFDeviceColorSpace pdfCS = toPDFColorSpace(fopImage.getColorSpace());
- if (prof != null) {
- boolean defaultsRGB = ColorProfileUtil.isDefaultsRGB(prof);
- String desc = ColorProfileUtil.getICCProfileDescription(prof);
- if (log.isDebugEnabled()) {
- log.debug("Image returns ICC profile: " + desc + ", default sRGB=" + defaultsRGB);
- }
- PDFICCBasedColorSpace cs = doc.getResources().getICCColorSpaceByProfileName(desc);
- if (!defaultsRGB) {
- if (cs == null) {
- pdfICCStream = doc.getFactory().makePDFICCStream();
- pdfICCStream.setColorSpace(prof, pdfCS);
- cs = doc.getFactory().makeICCBasedColorSpace(null, null, pdfICCStream);
- } else {
- pdfICCStream = cs.getICCStream();
- }
- } else {
- if (cs == null && "sRGB".equals(desc)) {
- //It's the default sRGB profile which we mapped to DefaultRGB in PDFRenderer
- cs = doc.getResources().getColorSpace("DefaultRGB");
- }
- pdfICCStream = cs.getICCStream();
- }
- }
- //Handle transparency mask if applicable
- if (fopImage.hasSoftMask()) {
- doc.getProfile().verifyTransparencyAllowed(fopImage.getOriginalURI());
- //TODO Implement code to combine image with background color if transparency is not
- //allowed (need BufferedImage support for that)
- byte [] softMaskBitmap = fopImage.getSoftMask();
- if (softMaskBitmap == null) {
- return;
- }
- BitmapImage fopimg = new BitmapImage
- ("Mask:" + key, fopImage.getWidth(), fopImage.getHeight(),
- softMaskBitmap, null);
- fopimg.setColorSpace(new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_GRAY));
- softMask = doc.addImage(null, fopimg).makeReference();
- }
- if (doc.getProfile().getPDFAMode().isPDFA1LevelB()) {
- if (pdfCS != null
- && pdfCS.getColorSpace() != PDFDeviceColorSpace.DEVICE_RGB
- && pdfCS.getColorSpace() != PDFDeviceColorSpace.DEVICE_GRAY
- && prof == null) {
- //See PDF/A-1, ISO 19005:1:2005(E), 6.2.3.3
- //FOP is currently restricted to DeviceRGB if PDF/A-1 is active.
- throw new PDFConformanceException(
- "PDF/A-1 does not allow mixing DeviceRGB and DeviceCMYK: "
- + fopImage.getOriginalURI());
- }
- }
- }
-
- /**
- * {@inheritDoc}
- */
- public int getWidth() {
- return fopImage.getWidth();
- }
-
- /**
- * {@inheritDoc}
- */
- public int getHeight() {
- return fopImage.getHeight();
- }
-
- /**
- * {@inheritDoc}
- */
- public PDFDeviceColorSpace getColorSpace() {
- // DeviceGray, DeviceRGB, or DeviceCMYK
- if (isCCF || isDCT || isPS) {
- return toPDFColorSpace(fopImage.getColorSpace());
- } else {
- return toPDFColorSpace(ColorSpace.getInstance(ColorSpace.CS_sRGB));
- }
- }
-
- /**
- * {@inheritDoc}
- */
- public int getBitsPerPixel() {
- if (isCCF) {
- return fopImage.getBitsPerPixel();
- } else {
- return 8; //TODO This is suboptimal, handling everything as RGB
- //The image wrappers can mostly only return RGB bitmaps right now. This should
- //be improved so the renderers can deal directly with RenderedImage instances.
- }
- }
-
- /**
- * {@inheritDoc}
- */
- public boolean isTransparent() {
- return fopImage.isTransparent();
- }
-
- /**
- * {@inheritDoc}
- */
- public PDFColor getTransparentColor() {
- return new PDFColor(fopImage.getTransparentColor().getRed(),
- fopImage.getTransparentColor().getGreen(),
- fopImage.getTransparentColor().getBlue());
- }
-
- /**
- * {@inheritDoc}
- */
- public String getMask() {
- return maskRef;
- }
-
- /** {@inheritDoc} */
- public PDFReference getSoftMaskReference() {
- return softMask;
- }
-
- /** @return true for CMYK images generated by Adobe Photoshop */
- public boolean isInverted() {
- return fopImage.isInverted();
- }
-
- /**
- * {@inheritDoc}
- */
- public boolean isPS() {
- return isPS;
- }
-
- /**
- * {@inheritDoc}
- */
- public PDFFilter getPDFFilter() {
- return pdfFilter;
- }
-
- /**
- * {@inheritDoc}
- */
- public void outputContents(OutputStream out) throws IOException {
- if (isPS) {
- outputPostScriptContents(out);
- } else {
- if (fopImage.getBitmapsSize() > 0) {
- out.write(fopImage.getBitmaps());
- } else {
- out.write(fopImage.getRessourceBytes());
- }
- }
- }
-
- /**
- * Serializes an EPS image to an OutputStream.
- * @param out OutputStream to write to
- * @throws IOException in case of an I/O problem
- */
- protected void outputPostScriptContents(OutputStream out) throws IOException {
- EPSImage epsImage = (EPSImage) fopImage;
- int[] bbox = epsImage.getBBox();
- int bboxw = bbox[2] - bbox[0];
- int bboxh = bbox[3] - bbox[1];
-
- // delegate the stream work to PDFStream
- //PDFStream imgStream = new PDFStream(0);
-
- StringBuffer preamble = new StringBuffer();
- preamble.append("%%BeginDocument: " + epsImage.getDocName() + "\n");
-
- preamble.append("userdict begin % Push userdict on dict stack\n");
- preamble.append("/PreEPS_state save def\n");
- preamble.append("/dict_stack countdictstack def\n");
- preamble.append("/ops_count count 1 sub def\n");
- preamble.append("/showpage {} def\n");
-
-
- preamble.append((double)(1f / (double) bboxw) + " "
- + (double)(1f / (double) bboxh) + " scale\n");
- preamble.append(-bbox[0] + " " + (-bbox[1]) + " translate\n");
- preamble.append(bbox[0] + " " + bbox[1] + " "
- + bboxw + " " + bboxh + " rectclip\n");
- preamble.append("newpath\n");
-
- StringBuffer post = new StringBuffer();
- post.append("%%EndDocument\n");
- post.append("count ops_count sub {pop} repeat\n");
- post.append("countdictstack dict_stack sub {end} repeat\n");
- post.append("PreEPS_state restore\n");
- post.append("end % userdict\n");
-
- //Write Preamble
- out.write(PDFDocument.encode(preamble.toString()));
- //Write EPS contents
- out.write(((EPSImage)fopImage).getEPSImage());
- //Writing trailer
- out.write(PDFDocument.encode(post.toString()));
- }
-
- /**
- * {@inheritDoc}
- */
- public PDFICCStream getICCStream() {
- return pdfICCStream;
- }
-
- /**
- * Converts a ColorSpace object to a PDFColorSpace object.
- * @param cs ColorSpace instance
- * @return PDFColorSpace new converted object
- */
- public static PDFDeviceColorSpace toPDFColorSpace(ColorSpace cs) {
- if (cs == null) {
- return null;
- }
-
- PDFDeviceColorSpace pdfCS = new PDFDeviceColorSpace(0);
- switch(cs.getType()) {
- case ColorSpace.TYPE_CMYK:
- pdfCS.setColorSpace(PDFDeviceColorSpace.DEVICE_CMYK);
- break;
- case ColorSpace.TYPE_RGB:
- pdfCS.setColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
- break;
- case ColorSpace.TYPE_GRAY:
- pdfCS.setColorSpace(PDFDeviceColorSpace.DEVICE_GRAY);
- break;
- }
- return pdfCS;
- }
-
- /**
- * {@inheritDoc}
- */
- public String getFilterHint() {
- if (isPS) {
- return PDFFilterList.CONTENT_FILTER;
- } else if (isDCT) {
- return PDFFilterList.JPEG_FILTER;
- } else if (isCCF) {
- return PDFFilterList.TIFF_FILTER;
- } else {
- return PDFFilterList.IMAGE_FILTER;
- }
- }
-
-}
-
diff --git a/src/java/org/apache/fop/render/pdf/ImageRawCCITTFaxAdapter.java b/src/java/org/apache/fop/render/pdf/ImageRawCCITTFaxAdapter.java
new file mode 100644
index 000000000..b80e2c03e
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/ImageRawCCITTFaxAdapter.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf;
+import java.awt.color.ColorSpace;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.xmlgraphics.image.codec.tiff.TIFFImage;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawCCITTFax;
+
+import org.apache.fop.pdf.CCFFilter;
+import org.apache.fop.pdf.PDFDeviceColorSpace;
+import org.apache.fop.pdf.PDFDictionary;
+import org.apache.fop.pdf.PDFDocument;
+import org.apache.fop.pdf.PDFFilter;
+import org.apache.fop.pdf.PDFFilterList;
+
+/**
+ * PDFImage implementation for the PDF renderer which handles raw CCITT fax images.
+ */
+public class ImageRawCCITTFaxAdapter extends AbstractImageAdapter {
+
+ private PDFFilter pdfFilter = null;
+
+ /**
+ * Creates a new PDFImage from an Image instance.
+ * @param image the CCITT encoded image
+ * @param key XObject key
+ */
+ public ImageRawCCITTFaxAdapter(ImageRawCCITTFax image, String key) {
+ super(image, key);
+ }
+
+ /**
+ * Returns the {@link ImageRawCCITTFax} instance for this adapter.
+ * @return the image instance
+ */
+ public ImageRawCCITTFax getImage() {
+ return ((ImageRawCCITTFax)this.image);
+ }
+
+ /** {@inheritDoc} */
+ public void setup(PDFDocument doc) {
+ pdfFilter = new CCFFilter();
+ pdfFilter.setApplied(true);
+ PDFDictionary dict = new PDFDictionary();
+ dict.put("Columns", this.image.getSize().getWidthPx());
+ int compression = getImage().getCompression();
+ switch (compression) {
+ case TIFFImage.COMP_FAX_G3_1D :
+ dict.put("K", 0);
+ break;
+ case TIFFImage.COMP_FAX_G3_2D :
+ dict.put("K", 1);
+ break;
+ case TIFFImage.COMP_FAX_G4_2D :
+ dict.put("K", -1);
+ break;
+ default:
+ throw new IllegalStateException("Invalid compression scheme: " + compression);
+ }
+ ((CCFFilter)pdfFilter).setDecodeParms(dict);
+
+ super.setup(doc);
+ }
+
+ /** {@inheritDoc} */
+ public PDFDeviceColorSpace getColorSpace() {
+ return toPDFColorSpace(ColorSpace.getInstance(ColorSpace.CS_GRAY));
+ }
+
+ /** {@inheritDoc} */
+ public int getBitsPerComponent() {
+ return 1;
+ }
+
+ /** {@inheritDoc} */
+ public PDFFilter getPDFFilter() {
+ return pdfFilter;
+ }
+
+ /** {@inheritDoc} */
+ public void outputContents(OutputStream out) throws IOException {
+ getImage().writeTo(out);
+ }
+
+ /** {@inheritDoc} */
+ public String getFilterHint() {
+ return PDFFilterList.TIFF_FILTER;
+ }
+
+}
+
diff --git a/src/java/org/apache/fop/render/pdf/ImageRawJPEGAdapter.java b/src/java/org/apache/fop/render/pdf/ImageRawJPEGAdapter.java
new file mode 100644
index 000000000..4b0ce4a85
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/ImageRawJPEGAdapter.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.xmlgraphics.image.loader.impl.ImageRawJPEG;
+
+import org.apache.fop.pdf.DCTFilter;
+import org.apache.fop.pdf.PDFDeviceColorSpace;
+import org.apache.fop.pdf.PDFDocument;
+import org.apache.fop.pdf.PDFFilter;
+import org.apache.fop.pdf.PDFFilterList;
+
+/**
+ * PDFImage implementation for the PDF renderer which handles raw JPEG images.
+ */
+public class ImageRawJPEGAdapter extends AbstractImageAdapter {
+
+ private PDFFilter pdfFilter = null;
+
+ /**
+ * Creates a new PDFImage from an Image instance.
+ * @param image the JPEG image
+ * @param key XObject key
+ */
+ public ImageRawJPEGAdapter(ImageRawJPEG image, String key) {
+ super(image, key);
+ }
+
+ /**
+ * Returns the {@link ImageRawJPEG} instance for this adapter.
+ * @return the image instance
+ */
+ public ImageRawJPEG getImage() {
+ return ((ImageRawJPEG)this.image);
+ }
+
+ /** {@inheritDoc} */
+ public void setup(PDFDocument doc) {
+ pdfFilter = new DCTFilter();
+ pdfFilter.setApplied(true);
+
+ super.setup(doc);
+ }
+
+ /** {@inheritDoc} */
+ public PDFDeviceColorSpace getColorSpace() {
+ // DeviceGray, DeviceRGB, or DeviceCMYK
+ return toPDFColorSpace(getImageColorSpace());
+ }
+
+ /** {@inheritDoc} */
+ public int getBitsPerComponent() {
+ return 8;
+ }
+
+ /** @return true for CMYK images generated by Adobe Photoshop */
+ public boolean isInverted() {
+ return getImage().isInverted();
+ }
+
+ /** {@inheritDoc} */
+ public PDFFilter getPDFFilter() {
+ return pdfFilter;
+ }
+
+ /** {@inheritDoc} */
+ public void outputContents(OutputStream out) throws IOException {
+ getImage().writeTo(out);
+ }
+
+ /** {@inheritDoc} */
+ public String getFilterHint() {
+ return PDFFilterList.JPEG_FILTER;
+ }
+
+}
+
diff --git a/src/java/org/apache/fop/render/pdf/ImageRenderedAdapter.java b/src/java/org/apache/fop/render/pdf/ImageRenderedAdapter.java
new file mode 100644
index 000000000..62e83da81
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/ImageRenderedAdapter.java
@@ -0,0 +1,249 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf;
+import java.awt.color.ColorSpace;
+import java.awt.image.ColorModel;
+import java.awt.image.IndexColorModel;
+import java.awt.image.RenderedImage;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.commons.io.output.ByteArrayOutputStream;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.xmlgraphics.image.loader.impl.ImageRendered;
+import org.apache.xmlgraphics.ps.ImageEncodingHelper;
+
+import org.apache.fop.pdf.AlphaRasterImage;
+import org.apache.fop.pdf.PDFArray;
+import org.apache.fop.pdf.PDFColor;
+import org.apache.fop.pdf.PDFDeviceColorSpace;
+import org.apache.fop.pdf.PDFDictionary;
+import org.apache.fop.pdf.PDFDocument;
+import org.apache.fop.pdf.PDFFilter;
+import org.apache.fop.pdf.PDFFilterList;
+import org.apache.fop.pdf.PDFName;
+import org.apache.fop.pdf.PDFReference;
+
+/**
+ * PDFImage implementation for the PDF renderer which handles RenderedImages.
+ */
+public class ImageRenderedAdapter extends AbstractImageAdapter {
+
+ /** logging instance */
+ private static Log log = LogFactory.getLog(ImageRenderedAdapter.class);
+
+ private ImageEncodingHelper encodingHelper;
+
+ private PDFFilter pdfFilter = null;
+ private String maskRef;
+ private PDFReference softMask;
+
+ /**
+ * Creates a new PDFImage from an Image instance.
+ * @param image the image
+ * @param key XObject key
+ */
+ public ImageRenderedAdapter(ImageRendered image, String key) {
+ super(image, key);
+ this.encodingHelper = new ImageEncodingHelper(image.getRenderedImage());
+ }
+
+ /**
+ * Returns the ImageRendered instance for this adapter.
+ * @return the ImageRendered instance
+ */
+ public ImageRendered getImage() {
+ return ((ImageRendered)this.image);
+ }
+
+ private ColorModel getEffectiveColorModel() {
+ return encodingHelper.getEncodedColorModel();
+ }
+
+ /** {@inheritDoc} */
+ protected ColorSpace getImageColorSpace() {
+ return getEffectiveColorModel().getColorSpace();
+ }
+
+ /** {@inheritDoc} */
+ public void setup(PDFDocument doc) {
+ RenderedImage ri = getImage().getRenderedImage();
+ ColorModel cm = getEffectiveColorModel();
+
+ super.setup(doc);
+
+ //Handle transparency mask if applicable
+ ColorModel orgcm = ri.getColorModel();
+ if (orgcm.hasAlpha() && orgcm.getTransparency() == ColorModel.TRANSLUCENT) {
+ doc.getProfile().verifyTransparencyAllowed(image.getInfo().getOriginalURI());
+ //TODO Implement code to combine image with background color if transparency is not
+ //allowed (need BufferedImage support for that)
+
+ AlphaRasterImage alphaImage = new AlphaRasterImage("Mask:" + getKey(), ri);
+ this.softMask = doc.addImage(null, alphaImage).makeReference();
+ }
+ }
+
+ /** {@inheritDoc} */
+ public PDFDeviceColorSpace getColorSpace() {
+ // DeviceGray, DeviceRGB, or DeviceCMYK
+ return toPDFColorSpace(getEffectiveColorModel().getColorSpace());
+ }
+
+ /** {@inheritDoc} */
+ public int getBitsPerComponent() {
+ ColorModel cm = getEffectiveColorModel();
+ if (cm instanceof IndexColorModel) {
+ IndexColorModel icm = (IndexColorModel)cm;
+ return icm.getComponentSize(0);
+ } else {
+ return cm.getComponentSize(0);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public boolean isTransparent() {
+ ColorModel cm = getEffectiveColorModel();
+ if (cm instanceof IndexColorModel) {
+ if (cm.getTransparency() == IndexColorModel.TRANSLUCENT) {
+ return true;
+ }
+ }
+ return (getImage().getTransparentColor() != null);
+ }
+
+ private static Integer getIndexOfFirstTransparentColorInPalette(RenderedImage image) {
+ ColorModel cm = image.getColorModel();
+ if (cm instanceof IndexColorModel) {
+ IndexColorModel icm = (IndexColorModel)cm;
+ //Identify the transparent color in the palette
+ byte[] alphas = new byte[icm.getMapSize()];
+ byte[] reds = new byte[icm.getMapSize()];
+ byte[] greens = new byte[icm.getMapSize()];
+ byte[] blues = new byte[icm.getMapSize()];
+ icm.getAlphas(alphas);
+ icm.getReds(reds);
+ icm.getGreens(greens);
+ icm.getBlues(blues);
+ for (int i = 0;
+ i < ((IndexColorModel) cm).getMapSize();
+ i++) {
+ if ((alphas[i] & 0xFF) == 0) {
+ return new Integer(i);
+ }
+ }
+ }
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ public PDFColor getTransparentColor() {
+ ColorModel cm = getEffectiveColorModel();
+ if (cm instanceof IndexColorModel) {
+ IndexColorModel icm = (IndexColorModel)cm;
+ if (cm.getTransparency() == IndexColorModel.TRANSLUCENT) {
+ int transPixel = icm.getTransparentPixel();
+ return new PDFColor(
+ icm.getRed(transPixel),
+ icm.getGreen(transPixel),
+ icm.getBlue(transPixel));
+ }
+ }
+ return new PDFColor(getImage().getTransparentColor());
+ }
+
+ /** {@inheritDoc} */
+ public String getMask() {
+ return maskRef;
+ }
+
+ /** {@inheritDoc} */
+ public PDFReference getSoftMaskReference() {
+ return softMask;
+ }
+
+ /** {@inheritDoc} */
+ public PDFFilter getPDFFilter() {
+ return pdfFilter;
+ }
+
+ /** {@inheritDoc} */
+ public void outputContents(OutputStream out) throws IOException {
+ encodingHelper.encode(out);
+ }
+
+ private static final int MAX_HIVAL = 255;
+
+ /** {@inheritDoc} */
+ public void populateXObjectDictionary(PDFDictionary dict) {
+ ColorModel cm = getEffectiveColorModel();
+ if (cm instanceof IndexColorModel) {
+ IndexColorModel icm = (IndexColorModel)cm;
+ PDFArray indexed = new PDFArray(dict);
+ indexed.add(new PDFName("Indexed"));
+
+ if (icm.getColorSpace().getType() != ColorSpace.TYPE_RGB) {
+ log.warn("Indexed color space is not using RGB as base color space."
+ + " The image may not be handled correctly."
+ + " Base color space: " + icm.getColorSpace()
+ + " Image: " + image.getInfo());
+ }
+ indexed.add(new PDFName(toPDFColorSpace(icm.getColorSpace()).getName()));
+ int c = icm.getMapSize();
+ int hival = c - 1;
+ if (hival > MAX_HIVAL) {
+ throw new UnsupportedOperationException("hival must not go beyond " + MAX_HIVAL);
+ }
+ indexed.add(new Integer(hival));
+ int[] palette = new int[c];
+ icm.getRGBs(palette);
+ ByteArrayOutputStream baout = new ByteArrayOutputStream();
+ for (int i = 0; i < c; i++) {
+ //TODO Probably doesn't work for non RGB based color spaces
+ //See log warning above
+ int entry = palette[i];
+ baout.write((entry & 0xFF0000) >> 16);
+ baout.write((entry & 0xFF00) >> 8);
+ baout.write(entry & 0xFF);
+ }
+ indexed.add(baout.toByteArray());
+
+ dict.put("ColorSpace", indexed);
+ dict.put("BitsPerComponent", icm.getPixelSize());
+
+ Integer index = getIndexOfFirstTransparentColorInPalette(getImage().getRenderedImage());
+ if (index != null) {
+ PDFArray mask = new PDFArray(dict);
+ mask.add(index);
+ mask.add(index);
+ dict.put("Mask", mask);
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ public String getFilterHint() {
+ return PDFFilterList.IMAGE_FILTER;
+ }
+
+}
+
diff --git a/src/java/org/apache/fop/render/pdf/PDFGraphics2DAdapter.java b/src/java/org/apache/fop/render/pdf/PDFGraphics2DAdapter.java
index eb0e4b378..d1e0abd5c 100644
--- a/src/java/org/apache/fop/render/pdf/PDFGraphics2DAdapter.java
+++ b/src/java/org/apache/fop/render/pdf/PDFGraphics2DAdapter.java
@@ -28,8 +28,9 @@ import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
+import org.apache.xmlgraphics.java2d.Graphics2DImagePainter;
+
import org.apache.fop.render.AbstractGraphics2DAdapter;
-import org.apache.fop.render.Graphics2DImagePainter;
import org.apache.fop.render.RendererContext;
import org.apache.fop.render.RendererContext.RendererContextWrapper;
import org.apache.fop.svg.PDFGraphics2D;
diff --git a/src/java/org/apache/fop/render/pdf/PDFImageHandler.java b/src/java/org/apache/fop/render/pdf/PDFImageHandler.java
index 52043b38e..d62dcbc5b 100644
--- a/src/java/org/apache/fop/render/pdf/PDFImageHandler.java
+++ b/src/java/org/apache/fop/render/pdf/PDFImageHandler.java
@@ -19,11 +19,15 @@
package org.apache.fop.render.pdf;
+import java.awt.Point;
+import java.awt.Rectangle;
import java.io.IOException;
-import org.apache.fop.image.FopImage;
-import org.apache.fop.pdf.PDFDocument;
+import org.apache.xmlgraphics.image.loader.Image;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+
import org.apache.fop.pdf.PDFXObject;
+import org.apache.fop.render.RendererContext;
/**
* This interface is used for handling all sorts of image type for PDF output.
@@ -31,20 +35,38 @@ import org.apache.fop.pdf.PDFXObject;
public interface PDFImageHandler {
/**
- * Returns the MIME type supported by this instance.
- * @return the MIME type
+ * Returns the priority for this image handler. A lower value means higher priority. This
+ * information is used to build the ordered/prioritized list of supported ImageFlavors for
+ * the PDF renderer. The built-in handlers use priorities between 100 and 999.
+ * @return a positive integer (>0) indicating the priority
+ */
+ int getPriority();
+
+ /**
+ * Returns the {@link ImageFlavor}s supported by this instance
+ * @return the supported image flavors
+ */
+ ImageFlavor[] getSupportedImageFlavors();
+
+ /**
+ * Returns the {@link Image} subclass supported by this instance.
+ * @return the Image type
*/
- String getSupportedMimeType();
+ Class getSupportedImageClass();
/**
- * Generates the PDF objects for the given FopImage instance and returns
- * the resulting XObject.
+ * Generates the PDF objects for the given {@link Image} instance. If the handler generates
+ * an XObject, it shall return it or otherwise return null. A generated XObject shall be
+ * placed in the current viewport according to the two parameters "origin" and "pos".
+ * @param context the PDF renderer context
* @param image the image to be handled
- * @param uri the URI of the image
- * @param pdfDoc the target PDF document
- * @return the generated XObject
+ * @param origin the current position in the current viewport (in millipoints)
+ * @param pos the position and scaling of the image relative to the origin point
+ * (in millipoints)
+ * @return the generated XObject or null if no XObject was generated
* @throws IOException if an I/O error occurs
*/
- PDFXObject generateImage(FopImage image, String uri, PDFDocument pdfDoc) throws IOException;
+ PDFXObject generateImage(RendererContext context, Image image,
+ Point origin, Rectangle pos) throws IOException;
}
diff --git a/src/java/org/apache/fop/render/pdf/PDFImageHandlerGraphics2D.java b/src/java/org/apache/fop/render/pdf/PDFImageHandlerGraphics2D.java
new file mode 100644
index 000000000..f1825297e
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/PDFImageHandlerGraphics2D.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf;
+
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.io.IOException;
+
+import org.apache.xmlgraphics.image.loader.Image;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D;
+
+import org.apache.fop.pdf.PDFXObject;
+import org.apache.fop.render.RendererContext;
+
+/**
+ * PDFImageHandler implementation which handles Graphics2D images.
+ */
+public class PDFImageHandlerGraphics2D implements PDFImageHandler {
+
+ private static final ImageFlavor[] FLAVORS = new ImageFlavor[] {
+ ImageFlavor.GRAPHICS2D,
+ };
+
+ /** {@inheritDoc} */
+ public PDFXObject generateImage(RendererContext context, Image image,
+ Point origin, Rectangle pos)
+ throws IOException {
+ PDFRenderer renderer = (PDFRenderer)context.getRenderer();
+ ImageGraphics2D imageG2D = (ImageGraphics2D)image;
+ renderer.getGraphics2DAdapter().paintImage(imageG2D.getGraphics2DImagePainter(),
+ context, origin.x + pos.x, origin.y + pos.y, pos.width, pos.height);
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ public int getPriority() {
+ return 200;
+ }
+
+ /** {@inheritDoc} */
+ public Class getSupportedImageClass() {
+ return ImageGraphics2D.class;
+ }
+
+ /** {@inheritDoc} */
+ public ImageFlavor[] getSupportedImageFlavors() {
+ return FLAVORS;
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawCCITTFax.java b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawCCITTFax.java
new file mode 100644
index 000000000..65142878a
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawCCITTFax.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf;
+
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.io.IOException;
+
+import org.apache.xmlgraphics.image.loader.Image;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawCCITTFax;
+
+import org.apache.fop.pdf.PDFDocument;
+import org.apache.fop.pdf.PDFImage;
+import org.apache.fop.pdf.PDFResourceContext;
+import org.apache.fop.pdf.PDFXObject;
+import org.apache.fop.render.RendererContext;
+
+/**
+ * PDFImageHandler implementation which handles CCITT encoded images (CCITT fax group 3/4).
+ */
+public class PDFImageHandlerRawCCITTFax implements PDFImageHandler {
+
+ private static final ImageFlavor[] FLAVORS = new ImageFlavor[] {
+ ImageFlavor.RAW_CCITTFAX,
+ };
+
+ /** {@inheritDoc} */
+ public PDFXObject generateImage(RendererContext context, Image image,
+ Point origin, Rectangle pos)
+ throws IOException {
+ PDFRenderer renderer = (PDFRenderer)context.getRenderer();
+ ImageRawCCITTFax ccitt = (ImageRawCCITTFax)image;
+ PDFDocument pdfDoc = (PDFDocument)context.getProperty(
+ PDFRendererContextConstants.PDF_DOCUMENT);
+ PDFResourceContext resContext = (PDFResourceContext)context.getProperty(
+ PDFRendererContextConstants.PDF_CONTEXT);
+
+ PDFImage pdfimage = new ImageRawCCITTFaxAdapter(ccitt, image.getInfo().getOriginalURI());
+ PDFXObject xobj = pdfDoc.addImage(resContext, pdfimage);
+
+ float x = (float)pos.getX() / 1000f;
+ float y = (float)pos.getY() / 1000f;
+ float w = (float)pos.getWidth() / 1000f;
+ float h = (float)pos.getHeight() / 1000f;
+ renderer.placeImage(x, y, w, h, xobj);
+
+ return xobj;
+ }
+
+ /** {@inheritDoc} */
+ public int getPriority() {
+ return 100;
+ }
+
+ /** {@inheritDoc} */
+ public Class getSupportedImageClass() {
+ return ImageRawCCITTFax.class;
+ }
+
+ /** {@inheritDoc} */
+ public ImageFlavor[] getSupportedImageFlavors() {
+ return FLAVORS;
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawJPEG.java b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawJPEG.java
new file mode 100644
index 000000000..58c9f1f53
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawJPEG.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf;
+
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.io.IOException;
+
+import org.apache.xmlgraphics.image.loader.Image;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawJPEG;
+
+import org.apache.fop.pdf.PDFDocument;
+import org.apache.fop.pdf.PDFImage;
+import org.apache.fop.pdf.PDFResourceContext;
+import org.apache.fop.pdf.PDFXObject;
+import org.apache.fop.render.RendererContext;
+
+/**
+ * PDFImageHandler implementation which handles raw JPEG images.
+ */
+public class PDFImageHandlerRawJPEG implements PDFImageHandler {
+
+ private static final ImageFlavor[] FLAVORS = new ImageFlavor[] {
+ ImageFlavor.RAW_JPEG,
+ };
+
+ /** {@inheritDoc} */
+ public PDFXObject generateImage(RendererContext context, Image image,
+ Point origin, Rectangle pos)
+ throws IOException {
+ PDFRenderer renderer = (PDFRenderer)context.getRenderer();
+ ImageRawJPEG jpeg = (ImageRawJPEG)image;
+ PDFDocument pdfDoc = (PDFDocument)context.getProperty(
+ PDFRendererContextConstants.PDF_DOCUMENT);
+ PDFResourceContext resContext = (PDFResourceContext)context.getProperty(
+ PDFRendererContextConstants.PDF_CONTEXT);
+
+ PDFImage pdfimage = new ImageRawJPEGAdapter(jpeg, image.getInfo().getOriginalURI());
+ PDFXObject xobj = pdfDoc.addImage(resContext, pdfimage);
+
+ float x = (float)pos.getX() / 1000f;
+ float y = (float)pos.getY() / 1000f;
+ float w = (float)pos.getWidth() / 1000f;
+ float h = (float)pos.getHeight() / 1000f;
+ renderer.placeImage(x, y, w, h, xobj);
+
+ return xobj;
+ }
+
+ /** {@inheritDoc} */
+ public int getPriority() {
+ return 100;
+ }
+
+ /** {@inheritDoc} */
+ public Class getSupportedImageClass() {
+ return ImageRawJPEG.class;
+ }
+
+ /** {@inheritDoc} */
+ public ImageFlavor[] getSupportedImageFlavors() {
+ return FLAVORS;
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/PDFImageHandlerRegistry.java b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRegistry.java
index 36e4ea7e1..536fc19b0 100644
--- a/src/java/org/apache/fop/render/pdf/PDFImageHandlerRegistry.java
+++ b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRegistry.java
@@ -19,11 +19,17 @@
package org.apache.fop.render.pdf;
+import java.util.Comparator;
import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
+import org.apache.xmlgraphics.image.loader.Image;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
import org.apache.xmlgraphics.util.Service;
/**
@@ -36,8 +42,23 @@ public class PDFImageHandlerRegistry {
/** the logger */
private static Log log = LogFactory.getLog(PDFImageHandlerRegistry.class);
+ private static final Comparator HANDLER_COMPARATOR = new Comparator() {
+ public int compare(Object o1, Object o2) {
+ PDFImageHandler h1 = (PDFImageHandler)o1;
+ PDFImageHandler h2 = (PDFImageHandler)o2;
+ return h1.getPriority() - h2.getPriority();
+ }
+ };
+
/** Map containing PDF image handlers for various MIME types */
private Map handlers = new java.util.HashMap();
+ /** List containing the same handlers as above but ordered by priority */
+ private List handlerList = new java.util.LinkedList();
+
+ /** Sorted Set of registered handlers */
+ private ImageFlavor[] supportedFlavors = new ImageFlavor[0];
+ private int handlerRegistrations;
+ private int lastSync;
/**
* Default constructor.
@@ -75,25 +96,74 @@ public class PDFImageHandlerRegistry {
* Add an image handler. The handler itself is inspected to find out what it supports.
* @param handler the PDFImageHandler instance
*/
- public void addHandler(PDFImageHandler handler) {
- String mime = handler.getSupportedMimeType();
- handlers.put(mime, handler);
+ public synchronized void addHandler(PDFImageHandler handler) {
+ Class imageClass = handler.getSupportedImageClass();
+ this.handlers.put(imageClass, handler);
+
+ //Sorted insert
+ ListIterator iter = this.handlerList.listIterator();
+ while (iter.hasNext()) {
+ PDFImageHandler h = (PDFImageHandler)iter.next();
+ if (HANDLER_COMPARATOR.compare(handler, h) < 0) {
+ iter.previous();
+ break;
+ }
+ }
+ iter.add(handler);
+ this.handlerRegistrations++;
}
/**
* Returns an PDFImageHandler which handles an specific image type given the MIME type
* of the image.
- * @param mime the requested MIME type
+ * @param img the Image to be handled
* @return the PDFImageHandler responsible for handling the image or null if none is available
*/
- public PDFImageHandler getHandler(String mime) {
- PDFImageHandler handler;
+ public PDFImageHandler getHandler(Image img) {
+ return getHandler(img.getClass());
+ }
- handler = (PDFImageHandler)handlers.get(mime);
+ /**
+ * Returns an PDFImageHandler which handles an specific image type given the MIME type
+ * of the image.
+ * @param imageClass the Image subclass for which to get a handler
+ * @return the PDFImageHandler responsible for handling the image or null if none is available
+ */
+ protected synchronized PDFImageHandler getHandler(Class imageClass) {
+ PDFImageHandler handler = null;
+ Class cl = imageClass;
+ while (cl != null) {
+ handler = (PDFImageHandler)handlers.get(cl);
+ if (handler != null) {
+ break;
+ }
+ cl = cl.getSuperclass();
+ }
return handler;
}
/**
+ * Returns the ordered array of supported image flavors.
+ * @return the array of image flavors
+ */
+ public synchronized ImageFlavor[] getSupportedFlavors() {
+ if (this.lastSync != this.handlerRegistrations) {
+ //Extract all ImageFlavors into a single array
+ List flavors = new java.util.ArrayList();
+ Iterator iter = this.handlerList.iterator();
+ while (iter.hasNext()) {
+ ImageFlavor[] f = ((PDFImageHandler)iter.next()).getSupportedImageFlavors();
+ for (int i = 0; i < f.length; i++) {
+ flavors.add(f[i]);
+ }
+ }
+ this.supportedFlavors = (ImageFlavor[])flavors.toArray(new ImageFlavor[flavors.size()]);
+ this.lastSync = this.handlerRegistrations;
+ }
+ return this.supportedFlavors;
+ }
+
+ /**
* Discovers PDFImageHandler implementations through the classpath and dynamically
* registers them.
*/
diff --git a/src/java/org/apache/fop/render/pdf/PDFImageHandlerRenderedImage.java b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRenderedImage.java
new file mode 100644
index 000000000..628883b9f
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRenderedImage.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf;
+
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.io.IOException;
+
+import org.apache.xmlgraphics.image.loader.Image;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.impl.ImageRendered;
+
+import org.apache.fop.pdf.PDFDocument;
+import org.apache.fop.pdf.PDFImage;
+import org.apache.fop.pdf.PDFResourceContext;
+import org.apache.fop.pdf.PDFXObject;
+import org.apache.fop.render.RendererContext;
+
+/**
+ * PDFImageHandler implementation which handles RenderedImage instances.
+ */
+public class PDFImageHandlerRenderedImage implements PDFImageHandler {
+
+ private static final ImageFlavor[] FLAVORS = new ImageFlavor[] {
+ ImageFlavor.BUFFERED_IMAGE,
+ ImageFlavor.RENDERED_IMAGE
+ };
+
+ /** {@inheritDoc} */
+ public PDFXObject generateImage(RendererContext context, Image image,
+ Point origin, Rectangle pos)
+ throws IOException {
+ PDFRenderer renderer = (PDFRenderer)context.getRenderer();
+ ImageRendered imageRend = (ImageRendered)image;
+ PDFDocument pdfDoc = (PDFDocument)context.getProperty(
+ PDFRendererContextConstants.PDF_DOCUMENT);
+ PDFResourceContext resContext = (PDFResourceContext)context.getProperty(
+ PDFRendererContextConstants.PDF_CONTEXT);
+
+ PDFImage pdfimage = new ImageRenderedAdapter(imageRend, image.getInfo().getOriginalURI());
+ PDFXObject xobj = pdfDoc.addImage(resContext, pdfimage);
+
+ float x = (float)pos.getX() / 1000f;
+ float y = (float)pos.getY() / 1000f;
+ float w = (float)pos.getWidth() / 1000f;
+ float h = (float)pos.getHeight() / 1000f;
+ renderer.placeImage(x, y, w, h, xobj);
+
+ return xobj;
+ }
+
+ /** {@inheritDoc} */
+ public int getPriority() {
+ return 300;
+ }
+
+ /** {@inheritDoc} */
+ public Class getSupportedImageClass() {
+ return ImageRendered.class;
+ }
+
+ /** {@inheritDoc} */
+ public ImageFlavor[] getSupportedImageFlavors() {
+ return FLAVORS;
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/PDFImageHandlerXML.java b/src/java/org/apache/fop/render/pdf/PDFImageHandlerXML.java
new file mode 100644
index 000000000..ba47cce69
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/PDFImageHandlerXML.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf;
+
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.io.IOException;
+import java.util.Map;
+
+import org.w3c.dom.Document;
+
+import org.apache.xmlgraphics.image.loader.Image;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM;
+
+import org.apache.fop.pdf.PDFXObject;
+import org.apache.fop.render.RendererContext;
+
+/**
+ * PDFImageHandler implementation which handles XML-based images.
+ */
+public class PDFImageHandlerXML implements PDFImageHandler {
+
+ private static final ImageFlavor[] FLAVORS = new ImageFlavor[] {
+ ImageFlavor.XML_DOM,
+ };
+
+ /** {@inheritDoc} */
+ public PDFXObject generateImage(RendererContext context, Image image,
+ Point origin, Rectangle pos)
+ throws IOException {
+ PDFRenderer renderer = (PDFRenderer)context.getRenderer();
+ ImageXMLDOM imgXML = (ImageXMLDOM)image;
+ Document doc = imgXML.getDocument();
+ String ns = imgXML.getRootNamespace();
+ Map foreignAttributes = (Map)context.getProperty(
+ PDFRendererContextConstants.FOREIGN_ATTRIBUTES);
+ renderer.renderDocument(doc, ns, pos, foreignAttributes);
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ public int getPriority() {
+ return 400;
+ }
+
+ /** {@inheritDoc} */
+ public Class getSupportedImageClass() {
+ return ImageXMLDOM.class;
+ }
+
+ /** {@inheritDoc} */
+ public ImageFlavor[] getSupportedImageFlavors() {
+ return FLAVORS;
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderer.java b/src/java/org/apache/fop/render/pdf/PDFRenderer.java
index f4aa5ee90..3b28cc34c 100644
--- a/src/java/org/apache/fop/render/pdf/PDFRenderer.java
+++ b/src/java/org/apache/fop/render/pdf/PDFRenderer.java
@@ -21,6 +21,8 @@ package org.apache.fop.render.pdf;
// Java
import java.awt.Color;
+import java.awt.Point;
+import java.awt.Rectangle;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_Profile;
import java.awt.geom.AffineTransform;
@@ -37,11 +39,13 @@ import java.util.Map;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
-import org.w3c.dom.Document;
-
import org.apache.commons.io.IOUtils;
-import org.apache.commons.io.output.CountingOutputStream;
+import org.apache.xmlgraphics.image.loader.ImageException;
+import org.apache.xmlgraphics.image.loader.ImageInfo;
+import org.apache.xmlgraphics.image.loader.ImageManager;
+import org.apache.xmlgraphics.image.loader.ImageSessionContext;
+import org.apache.xmlgraphics.image.loader.util.ImageUtil;
import org.apache.xmlgraphics.xmp.Metadata;
import org.apache.xmlgraphics.xmp.schemas.XMPBasicAdapter;
import org.apache.xmlgraphics.xmp.schemas.XMPBasicSchema;
@@ -68,14 +72,12 @@ import org.apache.fop.area.inline.Leader;
import org.apache.fop.area.inline.SpaceArea;
import org.apache.fop.area.inline.TextArea;
import org.apache.fop.area.inline.WordArea;
+import org.apache.fop.datatypes.URISpecification;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.extensions.ExtensionAttachment;
import org.apache.fop.fo.extensions.xmp.XMPMetadata;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.Typeface;
-import org.apache.fop.image.FopImage;
-import org.apache.fop.image.ImageFactory;
-import org.apache.fop.image.XMLImage;
import org.apache.fop.pdf.PDFAMode;
import org.apache.fop.pdf.PDFAction;
import org.apache.fop.pdf.PDFAnnotList;
@@ -1661,29 +1663,44 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void renderImage(Image image, Rectangle2D pos) {
endTextObject();
String url = image.getURL();
- putImage(url, pos);
+ putImage(url, pos, image.getForeignAttributes());
}
/** {@inheritDoc} */
protected void drawImage(String url, Rectangle2D pos, Map foreignAttributes) {
endTextObject();
- putImage(url, pos);
+ putImage(url, pos, foreignAttributes);
}
/**
* Adds a PDF XObject (a bitmap or form) to the PDF that will later be referenced.
- * @param url URL of the bitmap
+ * @param uri URL of the bitmap
* @param pos Position of the bitmap
+ * @deprecated Use {@link @putImage(String, Rectangle2D, Map)} instead.
*/
- protected void putImage(String url, Rectangle2D pos) {
- url = ImageFactory.getURL(url);
- PDFXObject xobject = pdfDoc.getXObject(url);
+ protected void putImage(String uri, Rectangle2D pos) {
+ putImage(uri, pos, null);
+ }
+
+ /**
+ * Adds a PDF XObject (a bitmap or form) to the PDF that will later be referenced.
+ * @param uri URL of the bitmap
+ * @param pos Position of the bitmap
+ * @param foreignAttributes foreign attributes associated with the image
+ */
+ protected void putImage(String uri, Rectangle2D pos, Map foreignAttributes) {
+ Rectangle posInt = new Rectangle(
+ (int)pos.getX(),
+ (int)pos.getY(),
+ (int)pos.getWidth(),
+ (int)pos.getHeight());
+
+ uri = URISpecification.getURL(uri);
+ PDFXObject xobject = pdfDoc.getXObject(uri);
if (xobject != null) {
float w = (float) pos.getWidth() / 1000f;
float h = (float) pos.getHeight() / 1000f;
@@ -1691,79 +1708,47 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
(float)pos.getY() / 1000f, w, h, xobject);
return;
}
+ Point origin = new Point(currentIPPosition, currentBPPosition);
+ int x = origin.x + posInt.x;
+ int y = origin.y + posInt.y;
- ImageFactory fact = userAgent.getFactory().getImageFactory();
- FopImage fopimage = fact.getImage(url, userAgent);
- if (fopimage == null) {
- return;
- }
- if (!fopimage.load(FopImage.DIMENSIONS)) {
- return;
- }
- String mime = fopimage.getMimeType();
-
- //First check for a dynamically registered handler
- PDFImageHandler handler = imageHandlerRegistry.getHandler(mime);
- if (handler != null) {
- PDFXObject xobj;
- try {
- xobj = handler.generateImage(fopimage, url, pdfDoc);
- } catch (IOException ioe) {
- log.error("I/O error while handling " + mime + " image", ioe);
- return;
- }
- fact.releaseImage(url, userAgent);
+ ImageManager manager = getUserAgent().getFactory().getImageManager();
+ ImageInfo info = null;
+ try {
+ ImageSessionContext sessionContext = getUserAgent().getImageSessionContext();
+ info = manager.getImageInfo(uri, sessionContext);
- float w = (float)pos.getWidth() / 1000f;
- float h = (float)pos.getHeight() / 1000f;
- placeImage((float) pos.getX() / 1000,
- (float) pos.getY() / 1000, w, h, xobj);
- } else if ("text/xml".equals(mime)) {
- if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
- return;
- }
- Document doc = ((XMLImage) fopimage).getDocument();
- String ns = ((XMLImage) fopimage).getNameSpace();
-
- renderDocument(doc, ns, pos, null);
- } else if ("image/svg+xml".equals(mime)) {
- if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
- return;
- }
- Document doc = ((XMLImage) fopimage).getDocument();
- String ns = ((XMLImage) fopimage).getNameSpace();
-
- renderDocument(doc, ns, pos, null);
- } else if ("image/eps".equals(mime)) {
- FopPDFImage pdfimage = new FopPDFImage(fopimage, url);
- PDFXObject xobj = pdfDoc.addImage(currentContext, pdfimage);
- fact.releaseImage(url, userAgent);
- float w = (float)pos.getWidth() / 1000f;
- float h = (float)pos.getHeight() / 1000f;
- placeImage((float) pos.getX() / 1000,
- (float) pos.getY() / 1000, w, h, xobj);
- } else if ("image/jpeg".equals(mime) || "image/tiff".equals(mime)) {
- FopPDFImage pdfimage = new FopPDFImage(fopimage, url);
- PDFXObject xobj = pdfDoc.addImage(currentContext, pdfimage);
- fact.releaseImage(url, userAgent);
-
- float w = (float)pos.getWidth() / 1000f;
- float h = (float)pos.getHeight() / 1000f;
- placeImage((float) pos.getX() / 1000,
- (float) pos.getY() / 1000, w, h, xobj);
- } else {
- if (!fopimage.load(FopImage.BITMAP)) {
- return;
+
+ Map hints = ImageUtil.getDefaultHints(sessionContext);
+ org.apache.xmlgraphics.image.loader.Image img = manager.getImage(
+ info, imageHandlerRegistry.getSupportedFlavors(), hints, sessionContext);
+
+ //First check for a dynamically registered handler
+ PDFImageHandler handler = imageHandlerRegistry.getHandler(img.getClass());
+ if (handler != null) {
+ if (log.isDebugEnabled()) {
+ log.debug("Using PDFImageHandler: " + handler.getClass().getName());
+ }
+ try {
+ RendererContext context = createRendererContext(
+ x, y, posInt.width, posInt.height, foreignAttributes);
+ handler.generateImage(context, img, origin, posInt);
+ } catch (IOException ioe) {
+ log.error("I/O error while handling image: " + info, ioe);
+ return;
+ }
+ } else {
+ throw new UnsupportedOperationException(
+ "No PDFImageHandler available for image: "
+ + info + " (" + img.getClass().getName() + ")");
}
- FopPDFImage pdfimage = new FopPDFImage(fopimage, url);
- PDFXObject xobj = pdfDoc.addImage(currentContext, pdfimage);
- fact.releaseImage(url, userAgent);
-
- float w = (float) pos.getWidth() / 1000f;
- float h = (float) pos.getHeight() / 1000f;
- placeImage((float) pos.getX() / 1000f,
- (float) pos.getY() / 1000f, w, h, xobj);
+ } catch (ImageException ie) {
+ log.error("Error while processing image: "
+ + (info != null ? info.toString() : uri), ie);
+ } catch (IOException ioe) {
+ log.error("I/O error while processing image: "
+ + (info != null ? info.toString() : uri), ioe);
}
// output new data
@@ -1782,7 +1767,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
* @param h height for image
* @param xobj the image XObject
*/
- protected void placeImage(float x, float y, float w, float h, PDFXObject xobj) {
+ public void placeImage(float x, float y, float w, float h, PDFXObject xobj) {
saveGraphicsState();
currentStream.add(format(w) + " 0 0 "
+ format(-h) + " "
@@ -1792,10 +1777,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
restoreGraphicsState();
}
- /**
- * {@inheritDoc}
- * int, int, int, int, java.util.Map)
- */
+ /** {@inheritDoc} */
protected RendererContext createRendererContext(int x, int y, int width, int height,
Map foreignAttributes) {
RendererContext context = super.createRendererContext(
diff --git a/src/java/org/apache/fop/render/pdf/PDFRendererConfigurator.java b/src/java/org/apache/fop/render/pdf/PDFRendererConfigurator.java
index 5c5894d3b..aec094e3b 100644
--- a/src/java/org/apache/fop/render/pdf/PDFRendererConfigurator.java
+++ b/src/java/org/apache/fop/render/pdf/PDFRendererConfigurator.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-/* $Id: $ */
+/* $Id$ */
package org.apache.fop.render.pdf;
diff --git a/src/java/org/apache/fop/render/pdf/PDFSVGHandler.java b/src/java/org/apache/fop/render/pdf/PDFSVGHandler.java
index b0ae29067..cbc0a8ec9 100644
--- a/src/java/org/apache/fop/render/pdf/PDFSVGHandler.java
+++ b/src/java/org/apache/fop/render/pdf/PDFSVGHandler.java
@@ -19,42 +19,40 @@
package org.apache.fop.render.pdf;
+import java.awt.Color;
+import java.awt.geom.AffineTransform;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Map;
-import java.awt.Color;
-import java.awt.geom.AffineTransform;
import org.w3c.dom.Document;
-import org.apache.fop.render.AbstractGenericSVGHandler;
-import org.apache.fop.render.Renderer;
-import org.apache.fop.render.RendererContext;
-import org.apache.fop.render.RendererContextConstants;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.batik.bridge.BridgeContext;
+import org.apache.batik.bridge.GVTBuilder;
+import org.apache.batik.dom.svg.SVGDOMImplementation;
+import org.apache.batik.gvt.GraphicsNode;
+import org.apache.batik.util.SVGConstants;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.fo.extensions.ExtensionElementMapping;
+import org.apache.fop.fonts.FontInfo;
import org.apache.fop.pdf.PDFDocument;
import org.apache.fop.pdf.PDFPage;
+import org.apache.fop.pdf.PDFResourceContext;
import org.apache.fop.pdf.PDFState;
import org.apache.fop.pdf.PDFStream;
-import org.apache.fop.pdf.PDFResourceContext;
+import org.apache.fop.render.AbstractGenericSVGHandler;
+import org.apache.fop.render.Renderer;
+import org.apache.fop.render.RendererContext;
+import org.apache.fop.render.RendererContextConstants;
import org.apache.fop.svg.PDFAElementBridge;
import org.apache.fop.svg.PDFBridgeContext;
import org.apache.fop.svg.PDFGraphics2D;
import org.apache.fop.svg.SVGUserAgent;
import org.apache.fop.util.QName;
-import org.apache.fop.fo.extensions.ExtensionElementMapping;
-import org.apache.fop.fonts.FontInfo;
-
-// Commons-Logging
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import org.apache.avalon.framework.configuration.Configuration;
-
-import org.apache.batik.bridge.GVTBuilder;
-import org.apache.batik.bridge.BridgeContext;
-import org.apache.batik.dom.svg.SVGDOMImplementation;
-import org.apache.batik.gvt.GraphicsNode;
-import org.apache.batik.util.SVGConstants;
/**
* PDF XML handler for SVG (uses Apache Batik).
@@ -154,10 +152,15 @@ public class PDFSVGHandler extends AbstractGenericSVGHandler
int xOffset = pdfInfo.currentXPosition;
int yOffset = pdfInfo.currentYPosition;
- final float deviceResolution = context.getUserAgent().getTargetResolution();
+ FOUserAgent userAgent = context.getUserAgent();
+ log.debug("Generating SVG at "
+ + userAgent.getTargetResolution()
+ + "dpi.");
+ final float deviceResolution = userAgent.getTargetResolution();
+ log.debug("Generating SVG at " + deviceResolution + "dpi.");
log.debug("Generating SVG at " + deviceResolution + "dpi.");
- final float uaResolution = context.getUserAgent().getSourceResolution();
+ final float uaResolution = userAgent.getSourceResolution();
SVGUserAgent ua = new SVGUserAgent(25.4f / uaResolution, new AffineTransform());
//Scale for higher resolution on-the-fly images from Batik
@@ -176,6 +179,8 @@ public class PDFSVGHandler extends AbstractGenericSVGHandler
BridgeContext ctx = new PDFBridgeContext(ua,
(strokeText ? null : pdfInfo.fi),
+ userAgent.getFactory().getImageManager(),
+ userAgent.getImageSessionContext(),
new AffineTransform());
GraphicsNode root;
diff --git a/src/java/org/apache/fop/render/ps/ImageEncoderCCITTFax.java b/src/java/org/apache/fop/render/ps/ImageEncoderCCITTFax.java
new file mode 100644
index 000000000..ab39966d4
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/ImageEncoderCCITTFax.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.xmlgraphics.image.codec.tiff.TIFFImage;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawCCITTFax;
+import org.apache.xmlgraphics.ps.ImageEncoder;
+
+/**
+ * ImageEncoder implementation for CCITT encoded images.
+ */
+public class ImageEncoderCCITTFax implements ImageEncoder {
+
+ private final ImageRawCCITTFax ccitt;
+
+ /**
+ * Main constructor.
+ * @param ccitt the CCITT encoded image
+ */
+ public ImageEncoderCCITTFax(ImageRawCCITTFax ccitt) {
+ this.ccitt = ccitt;
+ }
+
+ /** {@inheritDoc} */
+ public void writeTo(OutputStream out) throws IOException {
+ ccitt.writeTo(out);
+ }
+
+ /** {@inheritDoc} */
+ public String getImplicitFilter() {
+ PSDictionary dict = new PSDictionary();
+ dict.put("/Columns", new Integer(ccitt.getSize().getWidthPx()));
+ int compression = ccitt.getCompression();
+ switch (compression) {
+ case TIFFImage.COMP_FAX_G3_1D :
+ dict.put("/K", new Integer(0));
+ break;
+ case TIFFImage.COMP_FAX_G3_2D :
+ dict.put("/K", new Integer(1));
+ break;
+ case TIFFImage.COMP_FAX_G4_2D :
+ dict.put("/K", new Integer(-1));
+ break;
+ default:
+ throw new IllegalStateException(
+ "Invalid compression scheme: " + compression);
+ }
+
+ return dict.toString() + " /CCITTFaxDecode";
+ }
+} \ No newline at end of file
diff --git a/src/java/org/apache/fop/render/ps/ImageEncoderJPEG.java b/src/java/org/apache/fop/render/ps/ImageEncoderJPEG.java
new file mode 100644
index 000000000..ef4b9f16c
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/ImageEncoderJPEG.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.xmlgraphics.image.loader.impl.ImageRawJPEG;
+import org.apache.xmlgraphics.ps.ImageEncoder;
+
+/**
+ * ImageEncoder implementation for JPEG images.
+ */
+public class ImageEncoderJPEG implements ImageEncoder {
+ private final ImageRawJPEG jpeg;
+
+ /**
+ * Main constructor
+ * @param jpeg the JPEG image
+ */
+ public ImageEncoderJPEG(ImageRawJPEG jpeg) {
+ this.jpeg = jpeg;
+ }
+
+ /** {@inheritDoc} */
+ public void writeTo(OutputStream out) throws IOException {
+ jpeg.writeTo(out);
+ }
+
+ /** {@inheritDoc} */
+ public String getImplicitFilter() {
+ return "<< >> /DCTDecode";
+ }
+} \ No newline at end of file
diff --git a/src/java/org/apache/fop/render/ps/PSGraphics2DAdapter.java b/src/java/org/apache/fop/render/ps/PSGraphics2DAdapter.java
index 286bbca80..787af69a1 100644
--- a/src/java/org/apache/fop/render/ps/PSGraphics2DAdapter.java
+++ b/src/java/org/apache/fop/render/ps/PSGraphics2DAdapter.java
@@ -24,33 +24,43 @@ import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
-import org.apache.fop.render.Graphics2DAdapter;
-import org.apache.fop.render.Graphics2DImagePainter;
-import org.apache.fop.render.RendererContext;
+import org.apache.xmlgraphics.java2d.Graphics2DImagePainter;
import org.apache.xmlgraphics.java2d.ps.PSGraphics2D;
import org.apache.xmlgraphics.ps.PSGenerator;
+import org.apache.fop.render.Graphics2DAdapter;
+import org.apache.fop.render.RendererContext;
+
/**
* Graphics2DAdapter implementation for PostScript.
*/
public class PSGraphics2DAdapter implements Graphics2DAdapter {
- private PSRenderer renderer;
+ private PSGenerator gen;
+ private boolean clip = true;
/**
* Main constructor
* @param renderer the Renderer instance to which this instance belongs
*/
public PSGraphics2DAdapter(PSRenderer renderer) {
- this.renderer = renderer;
+ this(renderer.gen, true);
+ }
+
+ /**
+ * Constructor for use without a PSRenderer instance.
+ * @param gen the PostScript generator
+ * @param clip true if the image should be clipped
+ */
+ public PSGraphics2DAdapter(PSGenerator gen, boolean clip) {
+ this.gen = gen;
+ this.clip = clip;
}
/** {@inheritDoc} */
public void paintImage(Graphics2DImagePainter painter,
RendererContext context,
int x, int y, int width, int height) throws IOException {
- PSGenerator gen = renderer.gen;
-
float fwidth = width / 1000f;
float fheight = height / 1000f;
float fx = x / 1000f;
@@ -66,10 +76,12 @@ public class PSGraphics2DAdapter implements Graphics2DAdapter {
gen.commentln("%FOPBeginGraphics2D");
gen.saveGraphicsState();
- // Clip to the image area.
- gen.writeln("newpath");
- gen.defineRect(fx, fy, fwidth, fheight);
- gen.writeln("clip");
+ if (clip) {
+ // Clip to the image area.
+ gen.writeln("newpath");
+ gen.defineRect(fx, fy, fwidth, fheight);
+ gen.writeln("clip");
+ }
// transform so that the coordinates (0,0) is from the top left
// and positive is down and to the right. (0,0) is where the
diff --git a/src/java/org/apache/fop/render/ps/PSImageUtils.java b/src/java/org/apache/fop/render/ps/PSImageUtils.java
index 0ab329077..004d5a22a 100644
--- a/src/java/org/apache/fop/render/ps/PSImageUtils.java
+++ b/src/java/org/apache/fop/render/ps/PSImageUtils.java
@@ -19,20 +19,15 @@
package org.apache.fop.render.ps;
-import java.awt.Dimension;
-import java.awt.geom.Rectangle2D;
-import java.io.IOException;
-
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.xmlgraphics.ps.PSGenerator;
+
import org.apache.fop.image.EPSImage;
import org.apache.fop.image.FopImage;
-import org.apache.fop.image.JpegImage;
-import org.apache.xmlgraphics.ps.PSGenerator;
-import org.apache.xmlgraphics.ps.PSResource;
/**
- * Utility code for rendering images in PostScript.
+ * Utility code for rendering images in PostScript.
*/
public class PSImageUtils extends org.apache.xmlgraphics.ps.PSImageUtils {
@@ -40,93 +35,6 @@ public class PSImageUtils extends org.apache.xmlgraphics.ps.PSImageUtils {
protected static Log log = LogFactory.getLog(PSImageUtils.class);
/**
- * Renders a bitmap image to PostScript.
- * @param img image to render
- * @param x x position
- * @param y y position
- * @param w width
- * @param h height
- * @param gen PS generator
- * @throws IOException In case of an I/O problem while rendering the image
- */
- public static void renderBitmapImage(FopImage img,
- float x, float y, float w, float h, PSGenerator gen)
- throws IOException {
- boolean isJPEG = (img instanceof JpegImage && (gen.getPSLevel() >= 3));
- byte[] imgmap = convertImageToRawBitmapArray(img, isJPEG);
- if (imgmap == null) {
- gen.commentln("%Image data is not available: " + img);
- return; //Image cannot be converted
- }
-
- String imgDescription = img.getMimeType() + " " + img.getOriginalURI();
- Dimension imgDim = new Dimension(img.getWidth(), img.getHeight());
- Rectangle2D targetRect = new Rectangle2D.Double(x, y, w, h);
- writeImage(imgmap, imgDim, imgDescription, targetRect, isJPEG,
- img.getColorSpace(), gen);
- }
-
- /**
- * Renders a bitmap image (as form) to PostScript.
- * @param img image to render
- * @param form the form resource
- * @param x x position
- * @param y y position
- * @param w width
- * @param h height
- * @param gen PS generator
- * @throws IOException In case of an I/O problem while rendering the image
- */
- public static void renderForm(FopImage img, PSResource form,
- float x, float y, float w, float h, PSGenerator gen)
- throws IOException {
- Rectangle2D targetRect = new Rectangle2D.Double(x, y, w, h);
- paintForm(form, targetRect, gen);
- }
-
- /**
- * Generates a form resource for a FopImage in PostScript.
- * @param img image to render
- * @param form the form resource
- * @param gen PS generator
- * @throws IOException In case of an I/O problem while rendering the image
- */
- public static void generateFormResourceForImage(FopImage img, PSResource form,
- PSGenerator gen) throws IOException {
- boolean isJPEG = (img instanceof JpegImage && (gen.getPSLevel() >= 3));
- byte[] imgmap = convertImageToRawBitmapArray(img, isJPEG);
- if (imgmap == null) {
- gen.commentln("%Image data is not available: " + img);
- return; //Image cannot be converted
- }
-
- String imgDescription = img.getMimeType() + " " + img.getOriginalURI();
- Dimension imgDim = new Dimension(img.getWidth(), img.getHeight());
- writeReusableImage(imgmap, imgDim, form.getName(), imgDescription, isJPEG,
- img.getColorSpace(), gen);
- }
-
- private static byte[] convertImageToRawBitmapArray(FopImage img, boolean allowUndecodedJPEG)
- throws IOException {
- if (img instanceof JpegImage && allowUndecodedJPEG) {
- if (!img.load(FopImage.ORIGINAL_DATA)) {
- return null;
- }
- } else {
- if (!img.load(FopImage.BITMAP)) {
- return null;
- }
- }
- byte[] imgmap;
- if (img.getBitmapsSize() > 0) {
- imgmap = img.getBitmaps();
- } else {
- imgmap = img.getRessourceBytes();
- }
- return imgmap;
- }
-
- /**
* Renders an EPS image to PostScript.
* @param img EPS image to render
* @param x x position
@@ -134,6 +42,8 @@ public class PSImageUtils extends org.apache.xmlgraphics.ps.PSImageUtils {
* @param w width
* @param h height
* @param gen PS generator
+ * @deprecated Use {@link #renderEPS(java.io.InputStream, String, java.awt.geom.Rectangle2D,
+ * java.awt.geom.Rectangle2D, PSGenerator)} instead
*/
public static void renderEPS(EPSImage img,
float x, float y, float w, float h,
diff --git a/src/java/org/apache/fop/render/ps/PSRenderer.java b/src/java/org/apache/fop/render/ps/PSRenderer.java
index 7580bd0d5..21ae85259 100644
--- a/src/java/org/apache/fop/render/ps/PSRenderer.java
+++ b/src/java/org/apache/fop/render/ps/PSRenderer.java
@@ -24,6 +24,7 @@ import java.awt.Color;
import java.awt.geom.Rectangle2D;
import java.awt.image.RenderedImage;
import java.io.File;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.LineNumberReader;
@@ -35,14 +36,28 @@ import java.util.Map;
import javax.xml.transform.Source;
-import org.w3c.dom.Document;
-
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.xmlgraphics.image.loader.ImageException;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.ImageInfo;
+import org.apache.xmlgraphics.image.loader.ImageManager;
+import org.apache.xmlgraphics.image.loader.ImageSessionContext;
+import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawCCITTFax;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawEPS;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawJPEG;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawStream;
+import org.apache.xmlgraphics.image.loader.impl.ImageRendered;
+import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM;
+import org.apache.xmlgraphics.image.loader.pipeline.ImageProviderPipeline;
+import org.apache.xmlgraphics.image.loader.util.ImageUtil;
import org.apache.xmlgraphics.ps.DSCConstants;
+import org.apache.xmlgraphics.ps.ImageEncoder;
import org.apache.xmlgraphics.ps.PSGenerator;
+import org.apache.xmlgraphics.ps.PSImageUtils;
import org.apache.xmlgraphics.ps.PSProcSets;
import org.apache.xmlgraphics.ps.PSResource;
import org.apache.xmlgraphics.ps.PSState;
@@ -69,15 +84,12 @@ import org.apache.fop.area.inline.Leader;
import org.apache.fop.area.inline.SpaceArea;
import org.apache.fop.area.inline.TextArea;
import org.apache.fop.area.inline.WordArea;
+import org.apache.fop.datatypes.URISpecification;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.extensions.ExtensionAttachment;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.LazyFont;
import org.apache.fop.fonts.Typeface;
-import org.apache.fop.image.EPSImage;
-import org.apache.fop.image.FopImage;
-import org.apache.fop.image.ImageFactory;
-import org.apache.fop.image.XMLImage;
import org.apache.fop.render.AbstractPathOrientedRenderer;
import org.apache.fop.render.Graphics2DAdapter;
import org.apache.fop.render.ImageAdapter;
@@ -108,7 +120,8 @@ import org.apache.fop.util.CharUtilities;
* @author <a href="mailto:fop-dev@xmlgraphics.apache.org">Apache FOP Development Team</a>
* @version $Id$
*/
-public class PSRenderer extends AbstractPathOrientedRenderer implements ImageAdapter {
+public class PSRenderer extends AbstractPathOrientedRenderer
+ implements ImageAdapter, PSSupportedFlavors {
/** logging instance */
private static Log log = LogFactory.getLog(PSRenderer.class);
@@ -377,58 +390,183 @@ public class PSRenderer extends AbstractPathOrientedRenderer implements ImageAda
}
}
+ /**
+ * Indicates whether an image should be inlined or added as a PostScript form.
+ * @param uri the URI of the image
+ * @return true if the image should be inlined rather than added as a form
+ */
+ protected boolean isImageInlined(String uri) {
+ return !isOptimizeResources() || uri == null || "".equals(uri);
+ }
+
+ /**
+ * Indicates whether an image should be inlined or added as a PostScript form.
+ * @param info the ImageInfo object of the image
+ * @return true if the image should be inlined rather than added as a form
+ */
+ protected boolean isImageInlined(ImageInfo info) {
+ if (isImageInlined(info.getOriginalURI())) {
+ return true;
+ }
+
+ if (!isOptimizeResources()) {
+ throw new IllegalStateException("Must not get here if form support is enabled");
+ }
+
+ //Investigate choice for inline mode
+ ImageFlavor[] inlineFlavors = getInlineFlavors();
+ ImageManager manager = getUserAgent().getFactory().getImageManager();
+ ImageProviderPipeline[] inlineCandidates
+ = manager.getPipelineFactory().determineCandidatePipelines(
+ info, inlineFlavors);
+ ImageProviderPipeline inlineChoice = manager.choosePipeline(inlineCandidates);
+ ImageFlavor inlineFlavor = (inlineChoice != null ? inlineChoice.getTargetFlavor() : null);
+
+ //Investigate choice for form mode
+ ImageFlavor[] formFlavors = getFormFlavors();
+ ImageProviderPipeline[] formCandidates
+ = manager.getPipelineFactory().determineCandidatePipelines(
+ info, formFlavors);
+ ImageProviderPipeline formChoice = manager.choosePipeline(formCandidates);
+ ImageFlavor formFlavor = (formChoice != null ? formChoice.getTargetFlavor() : null);
+
+ //Inline if form is not supported or if a better choice is available with inline mode
+ return formFlavor == null || !formFlavor.equals(inlineFlavor);
+ }
+
/** {@inheritDoc} */
protected void drawImage(String uri, Rectangle2D pos, Map foreignAttributes) {
endTextObject();
- uri = ImageFactory.getURL(uri);
- ImageFactory fact = userAgent.getFactory().getImageFactory();
- FopImage fopimage = fact.getImage(uri, userAgent);
- if (fopimage == null) {
- return;
- }
- if (!fopimage.load(FopImage.DIMENSIONS)) {
- return;
+ int x = currentIPPosition + (int)Math.round(pos.getX());
+ int y = currentBPPosition + (int)Math.round(pos.getY());
+ uri = URISpecification.getURL(uri);
+ if (log.isDebugEnabled()) {
+ log.debug("Handling image: " + uri);
}
- float x = (float)pos.getX() / 1000f;
- x += currentIPPosition / 1000f;
- float y = (float)pos.getY() / 1000f;
- y += currentBPPosition / 1000f;
- float w = (float)pos.getWidth() / 1000f;
- float h = (float)pos.getHeight() / 1000f;
+
+ ImageManager manager = getUserAgent().getFactory().getImageManager();
+ ImageInfo info = null;
try {
- String mime = fopimage.getMimeType();
- if ("text/xml".equals(mime)) {
- if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
- return;
+ ImageSessionContext sessionContext = getUserAgent().getImageSessionContext();
+ info = manager.getImageInfo(uri, sessionContext);
+ int width = (int)pos.getWidth();
+ int height = (int)pos.getHeight();
+
+ //millipoints --> points for PostScript
+ float ptx = x / 1000f;
+ float pty = y / 1000f;
+ float ptw = width / 1000f;
+ float pth = height / 1000f;
+
+ if (isImageInlined(info)) {
+ if (log.isDebugEnabled()) {
+ log.debug("Image " + info + " is inlined");
}
- Document doc = ((XMLImage) fopimage).getDocument();
- String ns = ((XMLImage) fopimage).getNameSpace();
-
- renderDocument(doc, ns, pos, foreignAttributes);
- } else if ("image/svg+xml".equals(mime)) {
- if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
- return;
+ //Only now fully load/prepare the image
+ Map hints = ImageUtil.getDefaultHints(sessionContext);
+ org.apache.xmlgraphics.image.loader.Image img = manager.getImage(
+ info, getInlineFlavors(), hints, sessionContext);
+
+ //...and embed as inline image
+ if (img instanceof ImageGraphics2D) {
+ ImageGraphics2D imageG2D = (ImageGraphics2D)img;
+ RendererContext context = createRendererContext(
+ x, y, width, height, foreignAttributes);
+ getGraphics2DAdapter().paintImage(imageG2D.getGraphics2DImagePainter(),
+ context, x, y, width, height);
+ } else if (img instanceof ImageRendered) {
+ ImageRendered imgRend = (ImageRendered)img;
+ RenderedImage ri = imgRend.getRenderedImage();
+ PSImageUtils.renderBitmapImage(ri, ptx, pty, ptw, pth, gen);
+ } else if (img instanceof ImageXMLDOM) {
+ ImageXMLDOM imgXML = (ImageXMLDOM)img;
+ renderDocument(imgXML.getDocument(), imgXML.getRootNamespace(),
+ pos, foreignAttributes);
+ } else if (img instanceof ImageRawStream) {
+ final ImageRawStream raw = (ImageRawStream)img;
+ if (raw instanceof ImageRawEPS) {
+ ImageRawEPS eps = (ImageRawEPS)raw;
+ Rectangle2D bbox = eps.getBoundingBox();
+ InputStream in = raw.createInputStream();
+ try {
+ PSImageUtils.renderEPS(in, uri,
+ new Rectangle2D.Float(ptx, pty, ptw, pth),
+ bbox,
+ gen);
+ } finally {
+ IOUtils.closeQuietly(in);
+ }
+ } else if (raw instanceof ImageRawCCITTFax) {
+ final ImageRawCCITTFax ccitt = (ImageRawCCITTFax)raw;
+ ImageEncoder encoder = new ImageEncoderCCITTFax(ccitt);
+ Rectangle2D targetRect = new Rectangle2D.Float(
+ ptx, pty, ptw, pth);
+ PSImageUtils.writeImage(encoder, info.getSize().getDimensionPx(),
+ uri, targetRect,
+ ccitt.getColorSpace(), 1, false, gen);
+ } else if (raw instanceof ImageRawJPEG) {
+ ImageRawJPEG jpeg = (ImageRawJPEG)raw;
+ ImageEncoder encoder = new ImageEncoderJPEG(jpeg);
+ Rectangle2D targetRect = new Rectangle2D.Float(
+ ptx, pty, ptw, pth);
+ PSImageUtils.writeImage(encoder, info.getSize().getDimensionPx(),
+ uri, targetRect,
+ jpeg.getColorSpace(), 8, jpeg.isInverted(), gen);
+ } else {
+ throw new UnsupportedOperationException("Unsupported raw image: " + info);
+ }
+ } else {
+ throw new UnsupportedOperationException("Unsupported image type: " + img);
}
- Document doc = ((XMLImage) fopimage).getDocument();
- String ns = ((XMLImage) fopimage).getNameSpace();
-
- renderDocument(doc, ns, pos, foreignAttributes);
- } else if (fopimage instanceof EPSImage) {
- PSImageUtils.renderEPS((EPSImage)fopimage, x, y, w, h, gen);
} else {
- if (isImageInlined(uri, fopimage)) {
- PSImageUtils.renderBitmapImage(fopimage, x, y, w, h, gen);
- } else {
- PSResource form = getFormForImage(uri, fopimage);
- PSImageUtils.renderForm(fopimage, form, x, y, w, h, gen);
+ if (log.isDebugEnabled()) {
+ log.debug("Image " + info + " is embedded as a form later");
}
+ //Don't load image at this time, just put a form placeholder in the stream
+ PSResource form = getFormForImage(uri);
+ Rectangle2D targetRect = new Rectangle2D.Double(ptx, pty, ptw, pth);
+ PSImageUtils.paintForm(form, info.getSize().getDimensionPt(), targetRect, gen);
}
+
+ } catch (ImageException ie) {
+ log.error("Error while processing image: "
+ + (info != null ? info.toString() : uri), ie);
+ } catch (FileNotFoundException fe) {
+ log.error(fe.getMessage());
} catch (IOException ioe) {
handleIOTrouble(ioe);
}
}
- protected PSResource getFormForImage(String uri, FopImage fopimage) {
+ private ImageFlavor[] getInlineFlavors() {
+ ImageFlavor[] flavors;
+ if (gen.getPSLevel() >= 3) {
+ flavors = LEVEL_3_FLAVORS_INLINE;
+ } else {
+ flavors = LEVEL_2_FLAVORS_INLINE;
+ }
+ return flavors;
+ }
+
+ private ImageFlavor[] getFormFlavors() {
+ ImageFlavor[] flavors;
+ if (gen.getPSLevel() >= 3) {
+ flavors = LEVEL_3_FLAVORS_FORM;
+ } else {
+ flavors = LEVEL_2_FLAVORS_FORM;
+ }
+ return flavors;
+ }
+
+ /**
+ * Returns a PSResource instance representing a image as a PostScript form.
+ * @param uri the image URI
+ * @return a PSResource instance
+ */
+ protected PSResource getFormForImage(String uri) {
+ if (uri == null || "".equals(uri)) {
+ throw new IllegalArgumentException("uri must not be empty or null");
+ }
if (this.formResources == null) {
this.formResources = new java.util.HashMap();
}
@@ -439,11 +577,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer implements ImageAda
}
return form;
}
-
- protected boolean isImageInlined(String uri, FopImage image) {
- return !isOptimizeResources();
- }
-
+
/** {@inheritDoc} */
public void paintImage(RenderedImage image, RendererContext context,
int x, int y, int width, int height) throws IOException {
diff --git a/src/java/org/apache/fop/render/ps/PSSupportedFlavors.java b/src/java/org/apache/fop/render/ps/PSSupportedFlavors.java
new file mode 100644
index 000000000..8ccfa8e26
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/PSSupportedFlavors.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps;
+
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+
+/**
+ * Defines the set of supported ImageFlavors for the PostScript renderer.
+ */
+public interface PSSupportedFlavors {
+
+ /** The flavors supported inline with PostScript level 2. */
+ ImageFlavor[] LEVEL_2_FLAVORS_INLINE = new ImageFlavor[]
+ {ImageFlavor.RAW_EPS,
+ ImageFlavor.RAW_CCITTFAX,
+ ImageFlavor.GRAPHICS2D,
+ ImageFlavor.BUFFERED_IMAGE,
+ ImageFlavor.RENDERED_IMAGE,
+ ImageFlavor.XML_DOM};
+
+ /** The flavors supported inline with PostScript level 3 and higher. */
+ ImageFlavor[] LEVEL_3_FLAVORS_INLINE = new ImageFlavor[]
+ {ImageFlavor.RAW_EPS,
+ ImageFlavor.RAW_JPEG,
+ ImageFlavor.RAW_CCITTFAX,
+ ImageFlavor.GRAPHICS2D,
+ ImageFlavor.BUFFERED_IMAGE,
+ ImageFlavor.RENDERED_IMAGE,
+ ImageFlavor.XML_DOM};
+
+ /** The flavors supported as forms with PostScript level 2. */
+ ImageFlavor[] LEVEL_2_FLAVORS_FORM = new ImageFlavor[]
+ {//ImageFlavor.RAW_EPS,
+ ImageFlavor.RAW_CCITTFAX,
+ ImageFlavor.GRAPHICS2D,
+ ImageFlavor.BUFFERED_IMAGE,
+ ImageFlavor.RENDERED_IMAGE/*,
+ ImageFlavor.XML_DOM*/};
+
+ /** The flavors supported as forms with PostScript level 3 or higher. */
+ ImageFlavor[] LEVEL_3_FLAVORS_FORM = new ImageFlavor[]
+ {//ImageFlavor.RAW_EPS,
+ ImageFlavor.RAW_JPEG,
+ ImageFlavor.RAW_CCITTFAX,
+ ImageFlavor.GRAPHICS2D,
+ ImageFlavor.BUFFERED_IMAGE,
+ ImageFlavor.RENDERED_IMAGE/*,
+ ImageFlavor.XML_DOM*/};
+
+}
diff --git a/src/java/org/apache/fop/render/ps/ResourceHandler.java b/src/java/org/apache/fop/render/ps/ResourceHandler.java
index a0762e6e0..0dfb8029f 100644
--- a/src/java/org/apache/fop/render/ps/ResourceHandler.java
+++ b/src/java/org/apache/fop/render/ps/ResourceHandler.java
@@ -19,6 +19,8 @@
package org.apache.fop.render.ps;
+import java.awt.geom.Dimension2D;
+import java.awt.image.RenderedImage;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.io.InputStream;
@@ -27,8 +29,25 @@ import java.util.Iterator;
import java.util.Map;
import java.util.Set;
+import org.apache.xmlgraphics.image.loader.ImageException;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.ImageInfo;
+import org.apache.xmlgraphics.image.loader.ImageManager;
+import org.apache.xmlgraphics.image.loader.ImageSessionContext;
+import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawCCITTFax;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawEPS;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawJPEG;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawStream;
+import org.apache.xmlgraphics.image.loader.impl.ImageRendered;
+import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM;
+import org.apache.xmlgraphics.image.loader.util.ImageUtil;
import org.apache.xmlgraphics.ps.DSCConstants;
+import org.apache.xmlgraphics.ps.FormGenerator;
+import org.apache.xmlgraphics.ps.ImageEncoder;
+import org.apache.xmlgraphics.ps.ImageFormGenerator;
import org.apache.xmlgraphics.ps.PSGenerator;
+import org.apache.xmlgraphics.ps.PSProcSets;
import org.apache.xmlgraphics.ps.dsc.DSCException;
import org.apache.xmlgraphics.ps.dsc.DSCFilter;
import org.apache.xmlgraphics.ps.dsc.DSCParser;
@@ -50,8 +69,6 @@ import org.apache.xmlgraphics.ps.dsc.tools.DSCTools;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.fonts.FontInfo;
-import org.apache.fop.image.FopImage;
-import org.apache.fop.image.ImageFactory;
/**
* This class is used when two-pass production is used to generate the PostScript file (setting
@@ -59,7 +76,7 @@ import org.apache.fop.image.ImageFactory;
* temporary file generated by the PSRenderer and adds all used fonts and images as resources
* to the PostScript file.
*/
-public class ResourceHandler implements DSCParserConstants {
+public class ResourceHandler implements DSCParserConstants, PSSupportedFlavors {
/**
* Rewrites the temporary PostScript file generated by PSRenderer adding all needed resources
@@ -208,13 +225,129 @@ public class ResourceHandler implements DSCParserConstants {
Iterator iter = formResources.values().iterator();
while (iter.hasNext()) {
PSImageFormResource form = (PSImageFormResource)iter.next();
- ImageFactory fact = userAgent.getFactory().getImageFactory();
- FopImage image = fact.getImage(form.getImageURI(), userAgent);
- if (image == null) {
- throw new NullPointerException("Image not found: " + form.getImageURI());
+ final String uri = form.getImageURI();
+
+ ImageManager manager = userAgent.getFactory().getImageManager();
+ ImageInfo info = null;
+ try {
+ ImageSessionContext sessionContext = userAgent.getImageSessionContext();
+ info = manager.getImageInfo(uri, sessionContext);
+
+ ImageFlavor[] flavors;
+ if (gen.getPSLevel() >= 3) {
+ flavors = LEVEL_3_FLAVORS_FORM;
+ } else {
+ flavors = LEVEL_2_FLAVORS_FORM;
+ }
+ Map hints = ImageUtil.getDefaultHints(sessionContext);
+ org.apache.xmlgraphics.image.loader.Image img = manager.getImage(
+ info, flavors, hints, sessionContext);
+
+ String imageDescription = info.getMimeType() + " " + info.getOriginalURI();
+ final Dimension2D dimensionsPt = info.getSize().getDimensionPt();
+ final Dimension2D dimensionsMpt = info.getSize().getDimensionMpt();
+
+ if (img instanceof ImageGraphics2D) {
+ final ImageGraphics2D imageG2D = (ImageGraphics2D)img;
+ FormGenerator formGen = new FormGenerator(
+ form.getName(), imageDescription, dimensionsPt) {
+
+ protected void generatePaintProc(PSGenerator gen)
+ throws IOException {
+ gen.getResourceTracker().notifyResourceUsageOnPage(
+ PSProcSets.EPS_PROCSET);
+ gen.writeln("BeginEPSF");
+ PSGraphics2DAdapter adapter = new PSGraphics2DAdapter(gen, false);
+ adapter.paintImage(imageG2D.getGraphics2DImagePainter(),
+ null,
+ 0, 0,
+ (int)Math.round(dimensionsMpt.getWidth()),
+ (int)Math.round(dimensionsMpt.getHeight()));
+ gen.writeln("EndEPSF");
+ }
+
+ };
+ formGen.generate(gen);
+ } else if (img instanceof ImageRendered) {
+ ImageRendered imgRend = (ImageRendered)img;
+ RenderedImage ri = imgRend.getRenderedImage();
+ FormGenerator formGen = new ImageFormGenerator(
+ form.getName(), imageDescription,
+ info.getSize().getDimensionPt(),
+ ri, false);
+ formGen.generate(gen);
+ } else if (img instanceof ImageXMLDOM) {
+ throw new UnsupportedOperationException(
+ "Embedding an ImageXMLDOM as a form isn't supported, yet");
+ } else if (img instanceof ImageRawStream) {
+ final ImageRawStream raw = (ImageRawStream)img;
+ if (raw instanceof ImageRawEPS) {
+ final ImageRawEPS eps = (ImageRawEPS)raw;
+ throw new UnsupportedOperationException(
+ "Embedding EPS as forms isn't supported, yet");
+ /*
+ InputStream in = eps.createInputStream();
+ try {
+ FormGenerator formGen = new EPSFormGenerator(form.getName(),
+ imageDescription, dimensions, in);
+ formGen.generate(gen);
+ } finally {
+ IOUtils.closeQuietly(in);
+ }*/
+ } else if (raw instanceof ImageRawCCITTFax) {
+ ImageRawCCITTFax jpeg = (ImageRawCCITTFax)raw;
+ ImageEncoder encoder = new ImageEncoderCCITTFax(jpeg);
+ FormGenerator formGen = new ImageFormGenerator(
+ form.getName(), imageDescription,
+ info.getSize().getDimensionPt(),
+ info.getSize().getDimensionPx(),
+ encoder,
+ jpeg.getColorSpace(), 1, false);
+ formGen.generate(gen);
+ } else if (raw instanceof ImageRawJPEG) {
+ ImageRawJPEG jpeg = (ImageRawJPEG)raw;
+ ImageEncoder encoder = new ImageEncoderJPEG(jpeg);
+ FormGenerator formGen = new ImageFormGenerator(
+ form.getName(), imageDescription,
+ info.getSize().getDimensionPt(),
+ info.getSize().getDimensionPx(),
+ encoder,
+ jpeg.getColorSpace(), jpeg.isInverted());
+ formGen.generate(gen);
+ } else {
+ throw new UnsupportedOperationException("Unsupported raw image: " + info);
+ }
+ } else {
+ throw new UnsupportedOperationException("Unsupported image type: " + img);
+ }
+ } catch (ImageException ie) {
+ throw new IOException("Error while generating form for image: " + ie.getMessage());
}
- PSImageUtils.generateFormResourceForImage(image, form, gen);
}
}
+ private static FormGenerator createMissingForm(String formName, final Dimension2D dimensions) {
+ FormGenerator formGen = new FormGenerator(formName, null, dimensions) {
+
+ protected void generatePaintProc(PSGenerator gen) throws IOException {
+ gen.writeln("0 setgray");
+ gen.writeln("0 setlinewidth");
+ String w = gen.formatDouble(dimensions.getWidth());
+ String h = gen.formatDouble(dimensions.getHeight());
+ gen.writeln(w + " " + h + " scale");
+ gen.writeln("0 0 1 1 rectstroke");
+ gen.writeln("newpath");
+ gen.writeln("0 0 moveto");
+ gen.writeln("1 1 lineto");
+ gen.writeln("stroke");
+ gen.writeln("newpath");
+ gen.writeln("0 1 moveto");
+ gen.writeln("1 0 lineto");
+ gen.writeln("stroke");
+ }
+
+ };
+ return formGen;
+ }
+
}
diff --git a/src/java/org/apache/fop/render/rtf/RTFHandler.java b/src/java/org/apache/fop/render/rtf/RTFHandler.java
index eb50a3ea7..333e4ab95 100644
--- a/src/java/org/apache/fop/render/rtf/RTFHandler.java
+++ b/src/java/org/apache/fop/render/rtf/RTFHandler.java
@@ -20,26 +20,33 @@
package org.apache.fop.render.rtf;
// Java
-import java.awt.color.ColorSpace;
import java.awt.geom.Point2D;
-import java.awt.image.BufferedImage;
-import java.awt.image.ColorModel;
-import java.awt.image.ComponentColorModel;
-import java.awt.image.DataBuffer;
-import java.awt.image.DataBufferByte;
-import java.awt.image.PixelInterleavedSampleModel;
-import java.awt.image.Raster;
-import java.awt.image.SampleModel;
-import java.awt.image.WritableRaster;
import java.io.IOException;
+import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.Iterator;
+import java.util.Map;
-import org.apache.batik.dom.svg.SVGDOMImplementation;
-import org.apache.commons.io.output.ByteArrayOutputStream;
+import org.w3c.dom.Document;
+
+import org.xml.sax.SAXException;
+
+import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
+import org.apache.xmlgraphics.image.loader.Image;
+import org.apache.xmlgraphics.image.loader.ImageException;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.ImageInfo;
+import org.apache.xmlgraphics.image.loader.ImageManager;
+import org.apache.xmlgraphics.image.loader.ImageSessionContext;
+import org.apache.xmlgraphics.image.loader.ImageSize;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawStream;
+import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM;
+import org.apache.xmlgraphics.image.loader.util.ImageUtil;
+
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.datatypes.LengthBase;
@@ -79,9 +86,6 @@ import org.apache.fop.fo.pagination.SimplePageMaster;
import org.apache.fop.fo.pagination.StaticContent;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
import org.apache.fop.fonts.FontSetup;
-import org.apache.fop.image.FopImage;
-import org.apache.fop.image.ImageFactory;
-import org.apache.fop.image.XMLImage;
import org.apache.fop.render.DefaultFontResolver;
import org.apache.fop.render.rtf.rtflib.rtfdoc.IRtfAfterContainer;
import org.apache.fop.render.rtf.rtflib.rtfdoc.IRtfBeforeContainer;
@@ -108,10 +112,6 @@ import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfTextrun;
import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfListItem.RtfListItemLabel;
import org.apache.fop.render.rtf.rtflib.tools.BuilderContext;
import org.apache.fop.render.rtf.rtflib.tools.TableContext;
-import org.apache.xmlgraphics.image.writer.ImageWriter;
-import org.apache.xmlgraphics.image.writer.ImageWriterRegistry;
-import org.w3c.dom.Document;
-import org.xml.sax.SAXException;
/**
* RTF Handler: generates RTF output using the structure events from
@@ -1104,24 +1104,18 @@ public class RTFHandler extends FOEventHandler {
}
try {
- String url = eg.getURL();
+ String uri = eg.getURL();
//set image data
FOUserAgent userAgent = eg.getUserAgent();
- ImageFactory fact = userAgent.getFactory().getImageFactory();
- FopImage fopimage = fact.getImage(url, userAgent);
- if (fopimage == null) {
- log.error("Image could not be found: " + url);
+ ImageManager manager = userAgent.getFactory().getImageManager();
+ ImageInfo info = manager.getImageInfo(uri, userAgent.getImageSessionContext());
+ if (info == null) {
+ log.error("Image could not be found: " + uri);
return;
}
- if ("image/gif".equals(fopimage.getMimeType())) {
- //GIF is not directly supported by RTF, so it must be converted to PNG
- fopimage.load(FopImage.BITMAP);
- } else {
- fopimage.load(FopImage.ORIGINAL_DATA);
- }
- putGraphic(eg, fopimage);
+ putGraphic(eg, info);
} catch (Exception e) {
log.error("Error while handling an external-graphic: " + e.getMessage(), e);
}
@@ -1140,85 +1134,83 @@ public class RTFHandler extends FOEventHandler {
Document doc = child.getDOMDocument();
String ns = child.getNamespaceURI();
- if (SVGDOMImplementation.SVG_NAMESPACE_URI.equals(ns)) {
- // Build the image info.
- FopImage.ImageInfo info = new FopImage.ImageInfo();
- info.mimeType = "image/svg+xml";
- info.str = SVGDOMImplementation.SVG_NAMESPACE_URI;
- info.originalURI = "";
- info.data = doc;
-
- // Set the resolution to that of the FOUserAgent
- FOUserAgent ua = ifo.getUserAgent();
- info.dpiHorizontal = 25.4f / ua.getSourcePixelUnitToMillimeter();
- info.dpiVertical = info.dpiHorizontal;
-
- // Set the image size to the size of the svg.
- Point2D csize = new Point2D.Float(-1, -1);
- Point2D intrinsicDimensions = child.getDimension(csize);
- info.width = (int) intrinsicDimensions.getX();
- info.height = (int) intrinsicDimensions.getY();
-
- FopImage fopImage = new XMLImage(info);
- fopImage.load(FopImage.ORIGINAL_DATA);
-
- putGraphic(ifo, fopImage);
- } else {
- log.warn("The namespace " + ns
- + " for instream-foreign-objects is not supported.");
- }
+ ImageInfo info = new ImageInfo(null, null);
+ // Set the resolution to that of the FOUserAgent
+ FOUserAgent ua = ifo.getUserAgent();
+ ImageSize size = new ImageSize();
+ size.setResolution(ua.getSourceResolution());
+
+ // Set the image size to the size of the svg.
+ Point2D csize = new Point2D.Float(-1, -1);
+ Point2D intrinsicDimensions = child.getDimension(csize);
+ size.setSizeInMillipoints(
+ (int)Math.round(intrinsicDimensions.getX() * 1000),
+ (int)Math.round(intrinsicDimensions.getY() * 1000));
+ size.calcPixelsFromSize();
+ info.setSize(size);
+
+ ImageXMLDOM image = new ImageXMLDOM(info, doc, ns);
+ FOUserAgent userAgent = ifo.getUserAgent();
+ ImageManager manager = userAgent.getFactory().getImageManager();
+ Image converted = manager.convertImage(image, FLAVORS);
+ putGraphic(ifo, converted);
} catch (Exception e) {
log.error("Error while handling an instream-foreign-object: " + e.getMessage(), e);
}
}
- private BufferedImage createBufferedImageFromBitmaps(FopImage image) {
- // TODO Hardcoded color and sample models, FIX ME!
- ColorModel cm = new ComponentColorModel(
- ColorSpace.getInstance(ColorSpace.CS_sRGB),
- new int[] {8, 8, 8},
- false, false,
- ColorModel.OPAQUE, DataBuffer.TYPE_BYTE);
- SampleModel sampleModel = new PixelInterleavedSampleModel(
- DataBuffer.TYPE_BYTE, image.getWidth(), image.getHeight(), 3, image.getWidth() * 3,
- new int[] {0, 1, 2});
- DataBuffer dbuf = new DataBufferByte(image.getBitmaps(),
- image.getWidth() * image.getHeight() * 3);
-
- WritableRaster raster = Raster.createWritableRaster(sampleModel,
- dbuf, null);
-
- // Combine the color model and raster into a buffered image
- return new BufferedImage(cm, raster, false, null);
+ private static final ImageFlavor[] FLAVORS = new ImageFlavor[] {
+ ImageFlavor.RAW_EMF, ImageFlavor.RAW_PNG, ImageFlavor.RAW_JPEG
+ };
+
+ /**
+ * Puts a graphic/image into the generated RTF file.
+ * @param abstractGraphic the graphic (external-graphic or instream-foreign-object)
+ * @param info the image info object
+ * @throws IOException In case of an I/O error
+ */
+ private void putGraphic(AbstractGraphics abstractGraphic, ImageInfo info)
+ throws IOException {
+ try {
+ FOUserAgent userAgent = abstractGraphic.getUserAgent();
+ ImageManager manager = userAgent.getFactory().getImageManager();
+ ImageSessionContext sessionContext = userAgent.getImageSessionContext();
+ Map hints = ImageUtil.getDefaultHints(sessionContext);
+ Image image = manager.getImage(info, FLAVORS, hints, sessionContext);
+
+ putGraphic(abstractGraphic, image);
+ } catch (ImageException ie) {
+ log.error("Error while loading/processing image: " + info.getOriginalURI(), ie);
+ }
}
/**
* Puts a graphic/image into the generated RTF file.
* @param abstractGraphic the graphic (external-graphic or instream-foreign-object)
- * @param fopImage the image
+ * @param image the image
* @throws IOException In case of an I/O error
*/
- private void putGraphic(AbstractGraphics abstractGraphic, FopImage fopImage)
+ private void putGraphic(AbstractGraphics abstractGraphic, Image image)
throws IOException {
- byte[] rawData;
- if ("image/svg+xml".equals(fopImage.getMimeType())) {
- rawData = SVGConverter.convertToJPEG((XMLImage) fopImage);
- } else if (fopImage.getRessourceBytes() != null) {
- rawData = fopImage.getRessourceBytes();
- } else {
- //TODO Revisit after the image library redesign!!!
- //Convert the decoded bitmaps to a BufferedImage
- BufferedImage bufImage = createBufferedImageFromBitmaps(fopImage);
- ImageWriter writer = ImageWriterRegistry.getInstance().getWriterFor("image/png");
- ByteArrayOutputStream baout = new ByteArrayOutputStream();
- writer.writeImage(bufImage, baout);
- rawData = baout.toByteArray();
+ byte[] rawData = null;
+
+ ImageInfo info = image.getInfo();
+
+ if (image instanceof ImageRawStream) {
+ ImageRawStream rawImage = (ImageRawStream)image;
+ InputStream in = rawImage.createInputStream();
+ try {
+ rawData = IOUtils.toByteArray(in);
+ } finally {
+ IOUtils.closeQuietly(in);
+ }
}
+
if (rawData == null) {
log.warn(FONode.decorateWithContextInfo("Image could not be embedded: "
- + fopImage.getOriginalURI(), abstractGraphic));
+ + image, abstractGraphic));
return;
}
@@ -1229,7 +1221,9 @@ public class RTFHandler extends FOEventHandler {
final RtfExternalGraphic rtfGraphic = c.getTextrun().newImage();
//set URL
- rtfGraphic.setURL(fopImage.getOriginalURI());
+ if (info.getOriginalURI() != null) {
+ rtfGraphic.setURL(info.getOriginalURI());
+ }
rtfGraphic.setImageData(rawData);
//set scaling
@@ -1240,7 +1234,7 @@ public class RTFHandler extends FOEventHandler {
//get width
int width = 0;
if (abstractGraphic.getWidth().getEnum() == Constants.EN_AUTO) {
- width = fopImage.getIntrinsicWidth();
+ width = info.getSize().getWidthMpt();
} else {
width = abstractGraphic.getWidth().getValue();
}
@@ -1248,7 +1242,7 @@ public class RTFHandler extends FOEventHandler {
//get height
int height = 0;
if (abstractGraphic.getWidth().getEnum() == Constants.EN_AUTO) {
- height = fopImage.getIntrinsicHeight();
+ height = info.getSize().getHeightMpt();
} else {
height = abstractGraphic.getHeight().getValue();
}
@@ -1257,7 +1251,7 @@ public class RTFHandler extends FOEventHandler {
int contentwidth = 0;
if (abstractGraphic.getContentWidth().getEnum()
== Constants.EN_AUTO) {
- contentwidth = fopImage.getIntrinsicWidth();
+ contentwidth = info.getSize().getWidthMpt();
} else if (abstractGraphic.getContentWidth().getEnum()
== Constants.EN_SCALE_TO_FIT) {
contentwidth = width;
@@ -1271,7 +1265,7 @@ public class RTFHandler extends FOEventHandler {
if (abstractGraphic.getContentHeight().getEnum()
== Constants.EN_AUTO) {
- contentheight = fopImage.getIntrinsicHeight();
+ contentheight = info.getSize().getHeightMpt();
} else if (abstractGraphic.getContentHeight().getEnum()
== Constants.EN_SCALE_TO_FIT) {
diff --git a/src/java/org/apache/fop/render/rtf/rtflib/rtfdoc/RtfExternalGraphic.java b/src/java/org/apache/fop/render/rtf/rtflib/rtfdoc/RtfExternalGraphic.java
index a79679450..932198676 100644
--- a/src/java/org/apache/fop/render/rtf/rtflib/rtfdoc/RtfExternalGraphic.java
+++ b/src/java/org/apache/fop/render/rtf/rtflib/rtfdoc/RtfExternalGraphic.java
@@ -26,19 +26,17 @@ package org.apache.fop.render.rtf.rtflib.rtfdoc;
* the FOP project.
*/
-import org.apache.commons.io.IOUtils;
-import org.apache.fop.render.rtf.rtflib.tools.ImageConstants;
-import org.apache.fop.render.rtf.rtflib.tools.ImageUtil;
-//import org.apache.fop.render.rtf.rtflib.tools.jpeg.Encoder;
-//import org.apache.fop.render.rtf.rtflib.tools.jpeg.JPEGException;
-
+import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
-
-import java.io.File;
-import java.net.URL;
import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.apache.commons.io.IOUtils;
+
+import org.apache.fop.render.rtf.rtflib.tools.ImageConstants;
+import org.apache.fop.render.rtf.rtflib.tools.ImageUtil;
/**
* Creates an RTF image from an external graphic file.
@@ -342,13 +340,13 @@ public class RtfExternalGraphic extends RtfElement {
}
- if (url == null) {
- throw new ExternalGraphicException("The attribute 'url' of "
- + "<fo:external-graphic> is null.");
+ if (url == null && imagedata == null) {
+ throw new ExternalGraphicException(
+ "No image data is available (neither URL, nor in-memory)");
}
String linkToRoot = System.getProperty("jfor_link_to_root");
- if (linkToRoot != null) {
+ if (url != null && linkToRoot != null) {
writer.write("{\\field {\\* \\fldinst { INCLUDEPICTURE \"");
writer.write(linkToRoot);
File urlFile = new File(url.getFile());
@@ -380,7 +378,7 @@ public class RtfExternalGraphic extends RtfElement {
}
// Determine image file format
- String file = url.getFile ();
+ String file = (url != null ? url.getFile() : "<unknown>");
imageformat = FormatBase.determineFormat(imagedata);
if (imageformat != null) {
imageformat = imageformat.convert(imageformat, imagedata);
diff --git a/src/java/org/apache/fop/svg/PDFBridgeContext.java b/src/java/org/apache/fop/svg/PDFBridgeContext.java
index 3ffad3335..9f235b6a9 100644
--- a/src/java/org/apache/fop/svg/PDFBridgeContext.java
+++ b/src/java/org/apache/fop/svg/PDFBridgeContext.java
@@ -26,6 +26,10 @@ import org.apache.batik.bridge.Bridge;
import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.DocumentLoader;
import org.apache.batik.bridge.UserAgent;
+
+import org.apache.xmlgraphics.image.loader.ImageManager;
+import org.apache.xmlgraphics.image.loader.ImageSessionContext;
+
import org.apache.fop.fonts.FontInfo;
/**
@@ -36,6 +40,9 @@ public class PDFBridgeContext extends BridgeContext {
/** The font list. */
private final FontInfo fontInfo;
+ private final ImageManager imageManager;
+ private final ImageSessionContext imageSessionContext;
+
private AffineTransform linkTransform;
/**
@@ -50,9 +57,13 @@ public class PDFBridgeContext extends BridgeContext {
public PDFBridgeContext(UserAgent userAgent,
DocumentLoader loader,
FontInfo fontInfo,
+ ImageManager imageManager,
+ ImageSessionContext imageSessionContext,
AffineTransform linkTransform) {
super(userAgent, loader);
this.fontInfo = fontInfo;
+ this.imageManager = imageManager;
+ this.imageSessionContext = imageSessionContext;
this.linkTransform = linkTransform;
}
@@ -66,9 +77,13 @@ public class PDFBridgeContext extends BridgeContext {
*/
public PDFBridgeContext(UserAgent userAgent,
FontInfo fontInfo,
+ ImageManager imageManager,
+ ImageSessionContext imageSessionContext,
AffineTransform linkTransform) {
super(userAgent);
this.fontInfo = fontInfo;
+ this.imageManager = imageManager;
+ this.imageSessionContext = imageSessionContext;
this.linkTransform = linkTransform;
}
@@ -78,10 +93,29 @@ public class PDFBridgeContext extends BridgeContext {
* @param fontInfo the font list for the text painter, may be null
* in which case text is painted as shapes
*/
- public PDFBridgeContext(UserAgent userAgent, FontInfo fontInfo) {
- this(userAgent, fontInfo, null);
+ public PDFBridgeContext(UserAgent userAgent,
+ FontInfo fontInfo,
+ ImageManager imageManager,
+ ImageSessionContext imageSessionContext) {
+ this(userAgent, fontInfo, imageManager, imageSessionContext, null);
}
+ /**
+ * Returns the ImageManager to be used by the ImageElementBridge.
+ * @return the image manager
+ */
+ public ImageManager getImageManager() {
+ return this.imageManager;
+ }
+
+ /**
+ * Returns the ImageSessionContext to be used by the ImageElementBridge.
+ * @return the image session context
+ */
+ public ImageSessionContext getImageSessionContext() {
+ return this.imageSessionContext;
+ }
+
private void putPDFElementBridgeConditional(String className, String testFor) {
try {
Class.forName(testFor);
@@ -136,7 +170,10 @@ public class PDFBridgeContext extends BridgeContext {
//TODO There's no matching method in the super-class here
public BridgeContext createBridgeContext() {
return new PDFBridgeContext(getUserAgent(), getDocumentLoader(),
- fontInfo, linkTransform);
+ fontInfo,
+ getImageManager(),
+ getImageSessionContext(),
+ linkTransform);
}
-
+
}
diff --git a/src/java/org/apache/fop/svg/PDFGraphics2D.java b/src/java/org/apache/fop/svg/PDFGraphics2D.java
index 2436e1a10..c3c336000 100644
--- a/src/java/org/apache/fop/svg/PDFGraphics2D.java
+++ b/src/java/org/apache/fop/svg/PDFGraphics2D.java
@@ -62,13 +62,17 @@ import org.apache.batik.ext.awt.RadialGradientPaint;
import org.apache.batik.ext.awt.RenderingHintsKeyExt;
import org.apache.batik.gvt.GraphicsNode;
import org.apache.batik.gvt.PatternPaint;
+
+import org.apache.xmlgraphics.image.loader.impl.ImageRawJPEG;
+import org.apache.xmlgraphics.java2d.AbstractGraphics2D;
+import org.apache.xmlgraphics.java2d.GraphicContext;
+
import org.apache.fop.fonts.CIDFont;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.fonts.FontSetup;
import org.apache.fop.fonts.FontTriplet;
import org.apache.fop.fonts.LazyFont;
-import org.apache.fop.image.JpegImage;
import org.apache.fop.pdf.BitmapImage;
import org.apache.fop.pdf.PDFAnnotList;
import org.apache.fop.pdf.PDFColor;
@@ -76,6 +80,7 @@ import org.apache.fop.pdf.PDFConformanceException;
import org.apache.fop.pdf.PDFDeviceColorSpace;
import org.apache.fop.pdf.PDFDocument;
import org.apache.fop.pdf.PDFGState;
+import org.apache.fop.pdf.PDFImage;
import org.apache.fop.pdf.PDFImageXObject;
import org.apache.fop.pdf.PDFLink;
import org.apache.fop.pdf.PDFName;
@@ -86,10 +91,8 @@ import org.apache.fop.pdf.PDFResources;
import org.apache.fop.pdf.PDFState;
import org.apache.fop.pdf.PDFText;
import org.apache.fop.pdf.PDFXObject;
-import org.apache.fop.render.pdf.FopPDFImage;
+import org.apache.fop.render.pdf.ImageRawJPEGAdapter;
import org.apache.fop.util.ColorExt;
-import org.apache.xmlgraphics.java2d.AbstractGraphics2D;
-import org.apache.xmlgraphics.java2d.GraphicContext;
/**
* PDF Graphics 2D.
@@ -397,7 +400,7 @@ public class PDFGraphics2D extends AbstractGraphics2D {
* @param width the width to draw the image
* @param height the height to draw the image
*/
- public void addJpegImage(JpegImage jpeg, float x, float y,
+ public void addJpegImage(ImageRawJPEG jpeg, float x, float y,
float width, float height) {
preparePainting();
// Need to include hash code as when invoked from FO you
@@ -405,9 +408,9 @@ public class PDFGraphics2D extends AbstractGraphics2D {
// count is not enough.
String key = "__AddJPEG_" + hashCode() + "_" + jpegCount[0];
jpegCount[0]++;
- FopPDFImage fopimage = new FopPDFImage(jpeg, key);
+ PDFImage pdfimage = new ImageRawJPEGAdapter(jpeg, key);
PDFName imageName = this.pdfDoc.addImage(resourceContext,
- fopimage).getName();
+ pdfimage).getName();
AffineTransform at = getTransform();
double[] matrix = new double[6];
at.getMatrix(matrix);
diff --git a/src/java/org/apache/fop/svg/PDFImageElementBridge.java b/src/java/org/apache/fop/svg/PDFImageElementBridge.java
index 1545fc475..a2542e86f 100644
--- a/src/java/org/apache/fop/svg/PDFImageElementBridge.java
+++ b/src/java/org/apache/fop/svg/PDFImageElementBridge.java
@@ -19,24 +19,28 @@
package org.apache.fop.svg;
-import org.apache.batik.bridge.SVGImageElementBridge;
-
-import java.awt.Shape;
import java.awt.Graphics2D;
+import java.awt.Shape;
import java.awt.geom.Rectangle2D;
-import java.io.BufferedInputStream;
-import java.io.InputStream;
import org.w3c.dom.Element;
+import org.w3c.dom.svg.SVGDocument;
import org.apache.batik.bridge.BridgeContext;
+import org.apache.batik.bridge.SVGImageElementBridge;
import org.apache.batik.gvt.AbstractGraphicsNode;
import org.apache.batik.gvt.GraphicsNode;
import org.apache.batik.util.ParsedURL;
-import org.apache.fop.image.JpegImage;
-import org.apache.fop.image.FopImage;
-import org.apache.fop.image.analyser.ImageReaderFactory;
+import org.apache.xmlgraphics.image.loader.Image;
+import org.apache.xmlgraphics.image.loader.ImageException;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.ImageInfo;
+import org.apache.xmlgraphics.image.loader.ImageManager;
+import org.apache.xmlgraphics.image.loader.ImageSessionContext;
+import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawJPEG;
+import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM;
/**
* Bridge class for the &lt;image> element when jpeg images.
@@ -50,6 +54,10 @@ public class PDFImageElementBridge extends SVGImageElementBridge {
*/
public PDFImageElementBridge() { }
+ private final ImageFlavor[] supportedFlavors = new ImageFlavor[]
+ {ImageFlavor.RAW_JPEG,
+ ImageFlavor.GRAPHICS2D,
+ ImageFlavor.XML_DOM};
/**
* Create the raster image node.
* THis checks if it is a jpeg file and creates a jpeg node
@@ -60,51 +68,68 @@ public class PDFImageElementBridge extends SVGImageElementBridge {
* @return a new graphics node
*/
protected GraphicsNode createImageGraphicsNode
- (BridgeContext ctx, Element imageElement, ParsedURL purl) {
+ (BridgeContext ctx, Element imageElement, ParsedURL purl) {
+ PDFBridgeContext pdfCtx = (PDFBridgeContext)ctx;
+
+ ImageManager manager = pdfCtx.getImageManager();
+ ImageSessionContext sessionContext = pdfCtx.getImageSessionContext();
try {
- InputStream is = purl.openStream();
- if (!is.markSupported()) {
- is = new BufferedInputStream(is, 1024);
- }
+ ImageInfo info = manager.getImageInfo(purl.toString(), sessionContext);
+ Image image = manager.getImage(info, supportedFlavors, sessionContext);
- is.mark(3);
- byte [] data = new byte[3];
- is.read(data);
- is.reset();
- if ((data[0] == (byte)0xFF)
- && (data[1] == (byte)0xD8)
- && (data[2] == (byte)0xFF)) {
- FopImage.ImageInfo ii = ImageReaderFactory.make
- (purl.toString(), is, null);
- JpegImage jpeg = new JpegImage(ii);
- jpeg.load(FopImage.ORIGINAL_DATA);
- PDFJpegNode node = new PDFJpegNode(jpeg, ctx, imageElement, purl);
-
- Rectangle2D imgBounds = getImageBounds(ctx, imageElement);
- Rectangle2D bounds = node.getPrimitiveBounds();
- float [] vb = new float[4];
- vb[0] = 0; // x
- vb[1] = 0; // y
- vb[2] = (float) bounds.getWidth(); // width
- vb[3] = (float) bounds.getHeight(); // height
-
- // handles the 'preserveAspectRatio', 'overflow' and 'clip'
- // and sets the appropriate AffineTransform to the image node
- initializeViewport(ctx, imageElement, node, vb, imgBounds);
- return node;
+ AbstractGraphicsNode specializedNode = null;
+ if (image instanceof ImageXMLDOM) {
+ ImageXMLDOM xmlImage = (ImageXMLDOM)image;
+ if (xmlImage.getDocument() instanceof SVGDocument) {
+ return createSVGImageNode(ctx, imageElement,
+ (SVGDocument)xmlImage.getDocument());
+ } else {
+ //Convert image to Graphics2D
+ image = manager.convertImage(xmlImage,
+ new ImageFlavor[] {ImageFlavor.GRAPHICS2D});
+ }
}
- } catch (Exception ex) {
- //TODO Handle this exception
- }
+ if (image instanceof ImageRawJPEG) {
+ ImageRawJPEG jpegImage = (ImageRawJPEG)image;
+ specializedNode = new PDFJpegNode(jpegImage, ctx, imageElement, purl);
+
+ } else if (image instanceof ImageGraphics2D) {
+ ImageGraphics2D g2dImage = (ImageGraphics2D)image;
+ specializedNode = new Graphics2DNode(g2dImage);
+ } else {
+ ctx.getUserAgent().displayError(
+ new ImageException("Cannot convert an image to a usable format: " + purl));
+ }
+
+ Rectangle2D imgBounds = getImageBounds(ctx, imageElement);
+ Rectangle2D bounds = specializedNode.getPrimitiveBounds();
+ float [] vb = new float[4];
+ vb[0] = 0; // x
+ vb[1] = 0; // y
+ vb[2] = (float) bounds.getWidth(); // width
+ vb[3] = (float) bounds.getHeight(); // height
+ // handles the 'preserveAspectRatio', 'overflow' and 'clip'
+ // and sets the appropriate AffineTransform to the image node
+ initializeViewport(ctx, imageElement, specializedNode, vb, imgBounds);
+ return specializedNode;
+ } catch (Exception e) {
+ ctx.getUserAgent().displayError(e);
+ }
+
return superCreateGraphicsNode(ctx, imageElement, purl);
}
/**
+ * Calls the superclass' createImageGraphicNode() method to create the normal GraphicsNode.
+ * @param ctx the bridge context
+ * @param imageElement the image element
+ * @param purl the parsed URL
+ * @return the newly created graphics node
* @see org.apache.batik.bridge.SVGImageElementBridge#createGraphicsNode(BridgeContext, Element)
*/
protected GraphicsNode superCreateGraphicsNode
- (BridgeContext ctx, Element imageElement, ParsedURL purl) {
+ (BridgeContext ctx, Element imageElement, ParsedURL purl) {
return super.createImageGraphicsNode(ctx, imageElement, purl);
}
@@ -116,21 +141,21 @@ public class PDFImageElementBridge extends SVGImageElementBridge {
*/
public class PDFJpegNode extends AbstractGraphicsNode {
- private JpegImage jpeg;
+ private ImageRawJPEG jpeg;
private BridgeContext ctx;
private Element imageElement;
private ParsedURL purl;
private GraphicsNode origGraphicsNode = null;
/**
- * Create a new pdf jpeg node for drawing jpeg images
+ * Create a new PDF JPEG node for drawing JPEG images
* into pdf graphics.
- * @param j the jpeg image
+ * @param j the JPEG image
* @param ctx the bridge context
* @param imageElement the SVG image element
* @param purl the URL to the image
*/
- public PDFJpegNode(JpegImage j, BridgeContext ctx,
+ public PDFJpegNode(ImageRawJPEG j, BridgeContext ctx,
Element imageElement, ParsedURL purl) {
this.jpeg = j;
this.ctx = ctx;
@@ -138,32 +163,23 @@ public class PDFImageElementBridge extends SVGImageElementBridge {
this.purl = purl;
}
- /**
- * Get the outline of this image.
- * @return the outline shape which is the primitive bounds
- */
+ /** {@inheritDoc} */
public Shape getOutline() {
return getPrimitiveBounds();
}
- /**
- * Paint this jpeg image.
- * As this is used for inserting jpeg into pdf
- * it adds the jpeg image to the PDFGraphics2D.
- * @param g2d the graphics to draw the image on
- */
+ /** {@inheritDoc} */
public void primitivePaint(Graphics2D g2d) {
if (g2d instanceof PDFGraphics2D) {
PDFGraphics2D pdfg = (PDFGraphics2D) g2d;
float x = 0;
float y = 0;
try {
- float width = jpeg.getWidth();
- float height = jpeg.getHeight();
+ float width = jpeg.getSize().getWidthPx();
+ float height = jpeg.getSize().getHeightPx();
pdfg.addJpegImage(jpeg, x, y, width, height);
} catch (Exception e) {
- //TODO Handle this exception properly
- e.printStackTrace();
+ ctx.getUserAgent().displayError(e);
}
} else {
// Not going directly into PDF so use
@@ -179,40 +195,71 @@ public class PDFImageElementBridge extends SVGImageElementBridge {
}
}
- /**
- * Get the geometrix bounds of the image.
- * @return the primitive bounds
- */
+ /** {@inheritDoc} */
public Rectangle2D getGeometryBounds() {
return getPrimitiveBounds();
}
- /**
- * Get the primitive bounds of this bridge element.
- * @return the bounds of the jpeg image
- */
+ /** {@inheritDoc} */
public Rectangle2D getPrimitiveBounds() {
- try {
- return new Rectangle2D.Double(0, 0, jpeg.getWidth(),
- jpeg.getHeight());
- } catch (Exception e) {
- //TODO Handle this exception properly
- e.printStackTrace();
- }
- return null;
+ return new Rectangle2D.Double(0, 0,
+ jpeg.getSize().getWidthPx(),
+ jpeg.getSize().getHeightPx());
+ }
+
+ /** {@inheritDoc} */
+ public Rectangle2D getSensitiveBounds() {
+ //No interactive features, just return primitive bounds
+ return getPrimitiveBounds();
}
+ }
+
+ /**
+ * A node that holds a Graphics2D image.
+ */
+ public class Graphics2DNode extends AbstractGraphicsNode {
+
+ private ImageGraphics2D image;
+
/**
- * Returns the bounds of the sensitive area covered by this node,
- * This includes the stroked area but does not include the effects
- * of clipping, masking or filtering.
- * @return the bounds of the sensitive area
+ * Create a new Graphics2D node.
+ * @param g2d the Graphics2D image
*/
+ public Graphics2DNode(ImageGraphics2D g2d) {
+ this.image = g2d;
+ }
+
+ /** {@inheritDoc} */
+ public Shape getOutline() {
+ return getPrimitiveBounds();
+ }
+
+ /** {@inheritDoc} */
+ public void primitivePaint(Graphics2D g2d) {
+ int width = image.getSize().getWidthPx();
+ int height = image.getSize().getHeightPx();
+ Rectangle2D area = new Rectangle2D.Double(0, 0, width, height);
+ image.getGraphics2DImagePainter().paint(g2d, area);
+ }
+
+ /** {@inheritDoc} */
+ public Rectangle2D getGeometryBounds() {
+ return getPrimitiveBounds();
+ }
+
+ /** {@inheritDoc} */
+ public Rectangle2D getPrimitiveBounds() {
+ return new Rectangle2D.Double(0, 0,
+ image.getSize().getWidthPx(),
+ image.getSize().getHeightPx());
+ }
+
+ /** {@inheritDoc} */
public Rectangle2D getSensitiveBounds() {
//No interactive features, just return primitive bounds
return getPrimitiveBounds();
}
}
-
}
diff --git a/src/java/org/apache/fop/svg/PDFTranscoder.java b/src/java/org/apache/fop/svg/PDFTranscoder.java
index 417b82097..281df1b1d 100644
--- a/src/java/org/apache/fop/svg/PDFTranscoder.java
+++ b/src/java/org/apache/fop/svg/PDFTranscoder.java
@@ -23,6 +23,10 @@ import java.awt.Color;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.io.InputStream;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamSource;
import org.w3c.dom.Document;
import org.w3c.dom.svg.SVGLength;
@@ -41,6 +45,12 @@ import org.apache.batik.transcoder.TranscodingHints;
import org.apache.batik.transcoder.image.ImageTranscoder;
import org.apache.batik.transcoder.keys.BooleanKey;
import org.apache.batik.transcoder.keys.FloatKey;
+import org.apache.batik.util.ParsedURL;
+
+import org.apache.xmlgraphics.image.loader.ImageContext;
+import org.apache.xmlgraphics.image.loader.ImageManager;
+import org.apache.xmlgraphics.image.loader.ImageSessionContext;
+import org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext;
import org.apache.fop.Version;
import org.apache.fop.fonts.FontInfo;
@@ -99,6 +109,9 @@ public class PDFTranscoder extends AbstractFOPTranscoder
/** Graphics2D instance that is used to paint to */
protected PDFDocumentGraphics2D graphics = null;
+ private ImageManager imageManager;
+ private ImageSessionContext imageSessionContext;
+
/**
* Constructs a new <tt>PDFTranscoder</tt>.
*/
@@ -141,6 +154,11 @@ public class PDFTranscoder extends AbstractFOPTranscoder
graphics.getPDFDocument().getInfo().setProducer("Apache FOP Version "
+ Version.getVersion()
+ ": PDF Transcoder for Batik");
+ if (hints.containsKey(KEY_DEVICE_RESOLUTION)) {
+ graphics.setDeviceDPI(((Float)hints.get(KEY_DEVICE_RESOLUTION)).floatValue());
+ }
+
+ setupImageInfrastructure(uri);
try {
Configuration effCfg = this.cfg;
@@ -196,9 +214,6 @@ public class PDFTranscoder extends AbstractFOPTranscoder
//int h = (int)(height + 0.5);
try {
- if (hints.containsKey(KEY_DEVICE_RESOLUTION)) {
- graphics.setDeviceDPI(((Float)hints.get(KEY_DEVICE_RESOLUTION)).floatValue());
- }
OutputStream out = output.getOutputStream();
if (!(out instanceof BufferedOutputStream)) {
out = new BufferedOutputStream(out);
@@ -227,6 +242,39 @@ public class PDFTranscoder extends AbstractFOPTranscoder
}
}
+ private void setupImageInfrastructure(final String baseURI) {
+ final ImageContext imageContext = new ImageContext() {
+ public float getSourceResolution() {
+ return 25.4f / userAgent.getPixelUnitToMillimeter();
+ }
+ };
+ this.imageManager = new ImageManager(imageContext);
+ this.imageSessionContext = new AbstractImageSessionContext() {
+
+ public ImageContext getParentContext() {
+ return imageContext;
+ }
+
+ public float getTargetResolution() {
+ return graphics.getDeviceDPI();
+ }
+
+ public Source resolveURI(String uri) {
+ System.out.println("resolve " + uri);
+ try {
+ ParsedURL url = new ParsedURL(baseURI, uri);
+ InputStream in = url.openStream();
+ StreamSource source = new StreamSource(in, url.toString());
+ return source;
+ } catch (IOException ioe) {
+ userAgent.displayError(ioe);
+ return null;
+ }
+ }
+
+ };
+ }
+
/** {@inheritDoc} */
protected BridgeContext createBridgeContext() {
//For compatibility with Batik 1.6
@@ -239,7 +287,8 @@ public class PDFTranscoder extends AbstractFOPTranscoder
if (isTextStroked()) {
fontInfo = null;
}
- BridgeContext ctx = new PDFBridgeContext(userAgent, fontInfo);
+ BridgeContext ctx = new PDFBridgeContext(userAgent, fontInfo,
+ this.imageManager, this.imageSessionContext);
return ctx;
}
diff --git a/src/java/org/apache/fop/util/dijkstra/DefaultEdgeDirectory.java b/src/java/org/apache/fop/util/dijkstra/DefaultEdgeDirectory.java
new file mode 100644
index 000000000..381b356a2
--- /dev/null
+++ b/src/java/org/apache/fop/util/dijkstra/DefaultEdgeDirectory.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.util.dijkstra;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Default implementation of an edge directory for the {@link DijkstraAlgorithm}.
+ */
+public class DefaultEdgeDirectory implements EdgeDirectory {
+
+ /** The directory of edges */
+ private Map edges = new java.util.HashMap();
+ //Map<Vertex,Map<Vertex,Edge>>
+
+ /**
+ * Adds a new edge between two vertices.
+ * @param edge the new edge
+ */
+ public void addEdge(Edge edge) {
+ Map directEdges = (Map)edges.get(edge.getStart());
+ if (directEdges == null) {
+ directEdges = new java.util.HashMap();
+ edges.put(edge.getStart(), directEdges);
+ }
+ directEdges.put(edge.getEnd(), edge);
+ }
+
+ /** {@inheritDoc} */
+ public int getPenalty(Vertex start, Vertex end) {
+ Map edgeMap = (Map)edges.get(start);
+ if (edgeMap != null) {
+ Edge route = (Edge)edgeMap.get(end);
+ if (route != null) {
+ int penalty = route.getPenalty();
+ if (penalty < 0) {
+ throw new IllegalStateException("Penalty must not be negative");
+ }
+ return penalty;
+ }
+ }
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ public Iterator getDestinations(Vertex origin) {
+ Map directRoutes = (Map)edges.get(origin);
+ if (directRoutes != null) {
+ Iterator iter = directRoutes.keySet().iterator();
+ return iter;
+ }
+ return Collections.EMPTY_LIST.iterator();
+ }
+
+ /**
+ * Returns an iterator over all edges with the given origin.
+ * @param origin the origin
+ * @return an iterator over Edge instances
+ */
+ public Iterator getEdges(Vertex origin) {
+ Map directRoutes = (Map)edges.get(origin);
+ if (directRoutes != null) {
+ Iterator iter = directRoutes.values().iterator();
+ return iter;
+ }
+ return Collections.EMPTY_LIST.iterator();
+ }
+
+ /**
+ * Returns the best edge (the edge with the lowest penalty) between two given vertices.
+ * @param start the start vertex
+ * @param end the end vertex
+ * @return the best vertex or null if none is found
+ */
+ public Edge getBestEdge(Vertex start, Vertex end) {
+ Edge best = null;
+ Iterator iter = getEdges(start);
+ while (iter.hasNext()) {
+ Edge edge = (Edge)iter.next();
+ if (edge.getEnd().equals(end)) {
+ if (best == null || edge.getPenalty() < best.getPenalty()) {
+ best = edge;
+ }
+ }
+ }
+ return best;
+ }
+
+
+}
diff --git a/src/java/org/apache/fop/util/dijkstra/DijkstraAlgorithm.java b/src/java/org/apache/fop/util/dijkstra/DijkstraAlgorithm.java
new file mode 100644
index 000000000..a5b2fe202
--- /dev/null
+++ b/src/java/org/apache/fop/util/dijkstra/DijkstraAlgorithm.java
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.util.dijkstra;
+
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * This is an implementation of Dijkstra's algorithm to find the shortest path for a directed
+ * graph with non-negative edge weights.
+ * @see <a href="http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm">WikiPedia on Dijkstra's
+ * Algorithm</a>
+ */
+public class DijkstraAlgorithm {
+
+ /** Infinity value for distances. */
+ public static final int INFINITE = Integer.MAX_VALUE;
+
+ /** Compares penalties between two possible destinations. */
+ private final Comparator penaltyComparator = new Comparator() {
+ public int compare(Object left, Object right) {
+ int leftPenalty = getLowestPenalty((Vertex)left);
+ int rightPenalty = getLowestPenalty((Vertex)right);
+ if (leftPenalty < rightPenalty) {
+ return -1;
+ } else if (leftPenalty == rightPenalty) {
+ return ((Comparable)left).compareTo(right);
+ } else {
+ return 1;
+ }
+ }
+ };
+
+ /** The directory of edges */
+ private EdgeDirectory edgeDirectory;
+
+ /** The priority queue for all vertices under inspection, ordered by penalties/distances. */
+ private TreeSet priorityQueue = new TreeSet(penaltyComparator);
+ //Set<Vertex>
+
+ /** The set of vertices for which the lowest penalty has been found. */
+ private Set finishedVertices = new java.util.HashSet();
+ //Set<Vertex>
+
+ /** The currently known lowest penalties for all vertices. */
+ private Map lowestPenalties = new java.util.HashMap();
+ //Map<Vertex,Integer>
+
+ /** Map of all predecessors in the spanning tree of best routes. */
+ private Map predecessors = new java.util.HashMap();
+ //Map<Vertex,Vertex>
+
+ /**
+ * Main Constructor.
+ * @param edgeDirectory the edge directory this instance should work on
+ */
+ public DijkstraAlgorithm(EdgeDirectory edgeDirectory) {
+ this.edgeDirectory = edgeDirectory;
+ }
+
+ /**
+ * Returns the penalty between two vertices.
+ * @param start the start vertex
+ * @param end the end vertex
+ * @return the penalty between two vertices, or 0 if no single edge between the two vertices
+ * exists.
+ */
+ protected int getPenalty(Vertex start, Vertex end) {
+ return this.edgeDirectory.getPenalty(start, end);
+ }
+
+ /**
+ * Returns an iterator over all valid destinations for a given vertex.
+ * @param origin the origin from which to search for destinations
+ * @return the iterator over all valid destinations for a given vertex
+ */
+ protected Iterator getDestinations(Vertex origin) {
+ return this.edgeDirectory.getDestinations(origin);
+ }
+
+ private void reset() {
+ finishedVertices.clear();
+ priorityQueue.clear();
+
+ lowestPenalties.clear();
+ predecessors.clear();
+ }
+
+ /**
+ * Run Dijkstra's shortest path algorithm. After this method is finished you can use
+ * {@link #getPredecessor(Vertex)} to reconstruct the best/shortest path starting from the
+ * destination backwards.
+ * @param start the starting vertex
+ * @param destination the destination vertex.
+ */
+ public void execute(Vertex start, Vertex destination) {
+ if (start == null || destination == null) {
+ throw new NullPointerException("start and destination may not be null");
+ }
+
+ reset();
+ setShortestDistance(start, 0);
+ priorityQueue.add(start);
+
+ // the current node
+ Vertex u;
+
+ // extract the vertex with the shortest distance
+ while (priorityQueue.size() > 0) {
+ u = (Vertex)priorityQueue.first();
+ priorityQueue.remove(u);
+
+ if (destination.equals(u)) {
+ //Destination reached
+ break;
+ }
+
+ finishedVertices.add(u);
+ relax(u);
+ }
+ }
+
+ /**
+ * Compute new lowest penalties for neighboring vertices. Update the lowest penalties and the
+ * predecessor map if a better solution is found.
+ * @param u the vertex to process
+ */
+ private void relax(Vertex u) {
+ Iterator iter = getDestinations(u);
+ while (iter.hasNext()) {
+ Vertex v = (Vertex)iter.next();
+ // skip node already settled
+ if (isFinished(v)) {
+ continue;
+ }
+
+ int shortDist = getLowestPenalty(u) + getPenalty(u, v);
+
+ if (shortDist < getLowestPenalty(v)) {
+ // assign new shortest distance and mark unsettled
+ setShortestDistance(v, shortDist);
+
+ // assign predecessor in shortest path
+ setPredecessor(v, u);
+ }
+ }
+ }
+
+ private void setPredecessor(Vertex a, Vertex b) {
+ predecessors.put(a, b);
+ }
+
+ /**
+ * Indicates whether a shortest route to a vertex has been found.
+ * @param v the vertex
+ * @return true if the shortest route to this vertex has been found.
+ */
+ private boolean isFinished(Vertex v) {
+ return finishedVertices.contains(v);
+ }
+
+ private void setShortestDistance(Vertex vertex, int distance) {
+ //Remove so it is inserted at the right position after the lowest penalty changes for this
+ //vertex.
+ priorityQueue.remove(vertex);
+
+ //Update the lowest penalty.
+ lowestPenalties.put(vertex, new Integer(distance));
+
+ //Insert the vertex again at the new position based on the lowest penalty
+ priorityQueue.add(vertex);
+ }
+
+ /**
+ * Returns the lowest penalty from the start point to a given vertex.
+ * @param vertex the vertex
+ * @return the lowest penalty or {@link DijkstraAlgorithm#INFINITE} if there is no route to
+ * the destination.
+ */
+ public int getLowestPenalty(Vertex vertex) {
+ Integer d = ((Integer)lowestPenalties.get(vertex));
+ return (d == null) ? INFINITE : d.intValue();
+ }
+
+ /**
+ * Returns the vertex's predecessor on the shortest path.
+ * @param vertex the vertex for which to find the predecessor
+ * @return the vertex's predecessor on the shortest path, or
+ * <code>null</code> if there is no route to the destination.
+ */
+ public Vertex getPredecessor(Vertex vertex) {
+ return (Vertex)predecessors.get(vertex);
+ }
+
+}
diff --git a/src/java/org/apache/fop/util/dijkstra/Edge.java b/src/java/org/apache/fop/util/dijkstra/Edge.java
new file mode 100644
index 000000000..f709b9e26
--- /dev/null
+++ b/src/java/org/apache/fop/util/dijkstra/Edge.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.util.dijkstra;
+
+/**
+ * Represents an edge (or direct route between two points) for the {@link DijkstraAlgorithm}.
+ * Implement this class to hold the start and end vertex for an edge and implement the
+ * <code>getPenalty()</code> method.
+ */
+public interface Edge {
+
+ /**
+ * Returns the start vertex of the edge.
+ * @return the start vertex
+ */
+ Vertex getStart();
+
+ /**
+ * Returns the end vertex of the edge.
+ * @return the end vertex
+ */
+ Vertex getEnd();
+
+ /**
+ * Returns the penalty (or distance) for this edge.
+ * @return the penalty value (must be non-negative)
+ */
+ int getPenalty();
+
+}
diff --git a/src/java/org/apache/fop/util/dijkstra/EdgeDirectory.java b/src/java/org/apache/fop/util/dijkstra/EdgeDirectory.java
new file mode 100644
index 000000000..6a2dc848b
--- /dev/null
+++ b/src/java/org/apache/fop/util/dijkstra/EdgeDirectory.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.util.dijkstra;
+
+import java.util.Iterator;
+
+/**
+ * Represents a directory of edges for use by the {@link DijkstraAlgorithm}.
+ */
+public interface EdgeDirectory {
+
+ /**
+ * Returns the penalty between two vertices.
+ * @param start the start vertex
+ * @param end the end vertex
+ * @return the penalty between two vertices, or 0 if no single edge between the two vertices
+ * exists.
+ */
+ int getPenalty(Vertex start, Vertex end);
+
+ /**
+ * Returns an iterator over all valid destinations for a given vertex.
+ * @param origin the origin from which to search for destinations
+ * @return the iterator over all valid destinations for a given vertex
+ */
+ Iterator getDestinations(Vertex origin);
+
+} \ No newline at end of file
diff --git a/src/java/org/apache/fop/util/dijkstra/Vertex.java b/src/java/org/apache/fop/util/dijkstra/Vertex.java
new file mode 100644
index 000000000..40c41b0b7
--- /dev/null
+++ b/src/java/org/apache/fop/util/dijkstra/Vertex.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.util.dijkstra;
+
+/**
+ * Represents a vertex to be used by {@link DijkstraAlgorithm}. If you want to represent a city,
+ * you can do "public class City implements Vertex". The purpose of this interface is to make
+ * sure the Vertex implementation implements the Comparable interface so the sorting order is
+ * well-defined even when two vertices have the same penalty/distance from an origin point.
+ * Therefore, make sure you implement the <code>compareTo(Object)</code> and
+ * <code>equals(Object)</code> methods.
+ */
+public interface Vertex extends Comparable {
+
+}