<include name="org/apache/fop/render/pdf/**"/>
<exclude name="org/apache/fop/render/pdf/PDFRenderer.class"/>
<exclude name="org/apache/fop/render/pdf/PDFXMLHandler*"/>
+ <include name="org/apache/fop/render/intermediate/IFPainterConfigurator.class"/>
<include name="org/apache/fop/render/*RendererConfigurator**"/>
+ <include name="org/apache/fop/util/AbstractPaintingState**"/>
<include name="org/apache/fop/pdf/**"/>
</patternset>
<!-- PS transcoder -->
String MIME_FOP_PRINT = "application/X-fop-print";
/** Apache FOP's area tree XML */
String MIME_FOP_AREA_TREE = "application/X-fop-areatree";
-
+ /** Apache FOP's intermediate format XML */
+ String MIME_FOP_IF = "application/X-fop-intermediate-format";
- /** Proposed but non-registered MIME type for XSL-FO */
- String MIME_XSL_FO = "text/xsl";
-
}
--- /dev/null
- ImageHandler h1 = (ImageHandler)o1;
- ImageHandler h2 = (ImageHandler)o2;
+ /*
+ * 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;
+
+ 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;
+
+ /**
+ * This class holds references to various image handlers used by the renderers. It also
+ * supports automatic discovery of additional handlers available through
+ * the class path.
+ */
+ public abstract class AbstractImageHandlerRegistry {
+
+ /** the logger */
+ private static Log log = LogFactory.getLog(AbstractImageHandlerRegistry.class);
+
+ private static final Comparator HANDLER_COMPARATOR = new Comparator() {
+ public int compare(Object o1, Object o2) {
- ImageHandler handlerInstance
- = (ImageHandler)Class.forName(classname).newInstance();
++ ImageHandlerBase h1 = (ImageHandlerBase)o1;
++ ImageHandlerBase h2 = (ImageHandlerBase)o2;
+ return h1.getPriority() - h2.getPriority();
+ }
+ };
+
+ /** Map containing image handlers for various MIME types */
+ private final Map/*<Class, ImageHandler>*/ handlers
+ = new java.util.HashMap/*<Class, ImageHandler>*/();
+
+ /** List containing the same handlers as above but ordered by priority */
+ private final List/*<ImageHandler>*/ handlerList
+ = new java.util.LinkedList/*<ImageHandler>*/();
+
+ /** Sorted Set of registered handlers */
+ private ImageFlavor[] supportedFlavors = new ImageFlavor[0];
+
+ private int handlerRegistrations;
+ private int lastSync;
+
+ /**
+ * Default constructor.
+ */
+ public AbstractImageHandlerRegistry() {
+ discoverHandlers();
+ }
+
+ /**
+ * Add an ImageHandler. The handler itself is inspected to find out what it supports.
+ * @param classname the fully qualified class name
+ */
+ public void addHandler(String classname) {
+ try {
- public synchronized void addHandler(ImageHandler handler) {
++ ImageHandlerBase handlerInstance
++ = (ImageHandlerBase)Class.forName(classname).newInstance();
+ addHandler(handlerInstance);
+ } catch (ClassNotFoundException e) {
+ throw new IllegalArgumentException("Could not find "
+ + classname);
+ } catch (InstantiationException e) {
+ throw new IllegalArgumentException("Could not instantiate "
+ + classname);
+ } catch (IllegalAccessException e) {
+ throw new IllegalArgumentException("Could not access "
+ + classname);
+ } catch (ClassCastException e) {
+ throw new IllegalArgumentException(classname
+ + " is not an "
+ + getHandlerClass().getName());
+ }
+ }
+
+ /**
+ * Add an image handler. The handler itself is inspected to find out what it supports.
+ * @param handler the ImageHandler instance
+ */
- ImageHandler h = (ImageHandler)iter.next();
++ public synchronized void addHandler(ImageHandlerBase handler) {
+ this.handlers.put(handler.getSupportedImageClass(), handler);
+
+ //Sorted insert
+ ListIterator iter = this.handlerList.listIterator();
+ while (iter.hasNext()) {
- public ImageHandler getHandler(Image img) {
++ ImageHandlerBase h = (ImageHandlerBase)iter.next();
+ if (getHandlerComparator().compare(handler, h) < 0) {
+ iter.previous();
+ break;
+ }
+ }
+ iter.add(handler);
+ this.handlerRegistrations++;
+ }
+
+ /**
+ * Returns an ImageHandler which handles an specific image type given the MIME type
+ * of the image.
+ * @param img the Image to be handled
+ * @return the ImageHandler responsible for handling the image or null if none is available
+ */
- public synchronized ImageHandler getHandler(Class imageClass) {
- ImageHandler handler = null;
++ public ImageHandlerBase getHandler(Image img) {
+ return getHandler(img.getClass());
+ }
+
+ /**
+ * Returns an ImageHandler 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 ImageHandler responsible for handling the image or null if none is available
+ */
- handler = (ImageHandler)handlers.get(cl);
++ public synchronized ImageHandlerBase getHandler(Class imageClass) {
++ ImageHandlerBase handler = null;
+ Class cl = imageClass;
+ while (cl != null) {
- ImageFlavor[] f = ((ImageHandler)iter.next()).getSupportedImageFlavors();
++ handler = (ImageHandlerBase)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()) {
- ImageHandler handler = (ImageHandler)providers.next();
++ ImageFlavor[] f = ((ImageHandlerBase)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 ImageHandler implementations through the classpath and dynamically
+ * registers them.
+ */
+ private void discoverHandlers() {
+ // add mappings from available services
+ Class imageHandlerClass = getHandlerClass();
+ Iterator providers = Service.providers(imageHandlerClass);
+ if (providers != null) {
+ while (providers.hasNext()) {
++ ImageHandlerBase handler = (ImageHandlerBase)providers.next();
+ try {
+ if (log.isDebugEnabled()) {
+ log.debug("Dynamically adding ImageHandler: "
+ + handler.getClass().getName());
+ }
+ addHandler(handler);
+ } catch (IllegalArgumentException e) {
+ log.error("Error while adding ImageHandler", e);
+ }
+
+ }
+ }
+ }
+
+ /**
+ * Returns the ImageHandler comparator
+ *
+ * @return the ImageHandler comparator
+ */
+ public Comparator getHandlerComparator() {
+ return HANDLER_COMPARATOR;
+ }
+
+ /**
+ * Returns the ImageHandler implementing class
+ *
+ * @return the ImageHandler implementing class
+ */
+ public abstract Class getHandlerClass();
+ }
, width + bpwidth
, height + bpheight);
}
-
}
- private static final QName FOX_TRANSFORM
+ /** Constant for the fox:transform extension attribute */
+ protected static final QName FOX_TRANSFORM
= new QName(ExtensionElementMapping.URI, "fox:transform");
/** {@inheritDoc} */
package org.apache.fop.render;
-import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import java.awt.Rectangle;
+import java.io.IOException;
-public interface ImageHandler {
+import org.apache.xmlgraphics.image.loader.Image;
- import org.apache.xmlgraphics.image.loader.ImageFlavor;
- /**
- * 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.
- * The built-in handlers use priorities between 100 and 999.
- * @return a positive integer (>0) indicating the priority
- */
- int getPriority();
+/**
- * This interface is used for handling all sorts of image types for PDF output.
++ * This interface is a service provider interface for image handlers.
+ */
- public interface ImageHandler {
-
- /**
- * 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.
- * 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();
++public interface ImageHandler extends ImageHandlerBase {
/**
- * Returns the {@link ImageFlavor}s supported by this instance
- * @return the supported image flavors
+ * Indicates whether the image handler is compatible with the indicated target represented
+ * by the rendering context object and with the image to be processed. The image is also
+ * passed as a parameter because a handler might not support every subtype of image that is
+ * presented. For example: in the case of {@code ImageXMLDOM}, the image might carry an SVG
+ * or some other XML format. One handler might only handle SVG but no other XML format.
+ * @param targetContext the target rendering context
+ * @param image the image to be processed (or null if only to check based on the rendering
+ * context)
+ * @return true if this handler is compatible with the target rendering context
*/
- ImageFlavor[] getSupportedImageFlavors();
+ boolean isCompatible(RenderingContext targetContext, Image image);
- /**
- * Returns the {@link Image} subclass supported by this instance.
- * @return the Image type
- */
- Class getSupportedImageClass();
-
/**
- * Returns the {@link Class} subclass supported by this instance.
- * @return the image Class type
+ * Handles the given {@link Image} instance painting it at the indicated position in the
+ * output format being generated.
+ * @param context the rendering context
+ * @param image the image to be handled
+ * @param pos the position and scaling of the image relative to the origin point of the
+ * current viewport (in millipoints)
+ * @throws IOException if an I/O error occurs
*/
- Class getSupportedImageClass();
+ void handleImage(RenderingContext context, Image image,
+ Rectangle pos) throws IOException;
+
}
--- /dev/null
--- /dev/null
++/*
++ * 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;
++
++import org.apache.xmlgraphics.image.loader.ImageFlavor;
++
++/**
++ * This interface is a service provider base interface for image handlers. It only contains
++ * methods necessary for registration and is extended by sub-interfaces with the actual
++ * image handling contract.
++ */
++public interface ImageHandlerBase {
++
++ /**
++ * 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.
++ * 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 Class} subclass supported by this instance.
++ * @return the image Class type
++ */
++ Class getSupportedImageClass();
++}
import java.util.List;
import java.util.Map;
- import org.w3c.dom.Document;
-
+import org.apache.fop.apps.FOPException;
import org.apache.fop.area.Area;
import org.apache.fop.area.Trait;
import org.apache.fop.fonts.CustomFontCollection;
--- /dev/null
- <?xml version="1.0" encoding="UTF-8"?><catalogue xml:lang="en">
- <message key="org.apache.fop.render.afp.AFPEventProducer.warnDefaultFontSetup">No AFP fonts configured. Using default setup.</message>
- </catalogue>
++<?xml version="1.0" encoding="UTF-8"?><catalogue xml:lang="en"/>
--- /dev/null
-import org.apache.fop.render.ImageHandler;
+ /*
+ * 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.afp;
+
+ import java.awt.Point;
+ import java.awt.geom.Rectangle2D;
+ import java.io.IOException;
+ import java.util.Map;
+
+ import org.apache.fop.afp.AFPDataObjectInfo;
+ import org.apache.fop.afp.AFPObjectAreaInfo;
+ import org.apache.fop.afp.AFPPaintingState;
+ import org.apache.fop.afp.AFPResourceInfo;
+ import org.apache.fop.afp.AFPUnitConverter;
-public abstract class AFPImageHandler implements ImageHandler {
++import org.apache.fop.render.ImageHandlerBase;
+
+ /**
+ * A base abstract AFP image handler
+ */
++public abstract class AFPImageHandler implements ImageHandlerBase {
+ private static final int X = 0;
+ private static final int Y = 1;
+
+ /** foreign attribute reader */
+ private final AFPForeignAttributeReader foreignAttributeReader
+ = new AFPForeignAttributeReader();
+
+ /**
+ * Generates an intermediate AFPDataObjectInfo that is later used to construct
+ * the appropriate data object in the AFP DataStream.
+ *
+ * @param rendererImageInfo the renderer image info
+ * @return a data object info object
+ * @throws IOException thrown if an I/O exception of some sort has occurred.
+ */
+ public AFPDataObjectInfo generateDataObjectInfo(
+ AFPRendererImageInfo rendererImageInfo) throws IOException {
+ AFPDataObjectInfo dataObjectInfo = createDataObjectInfo();
+
+ // set resource information
+ Map foreignAttributes = rendererImageInfo.getForeignAttributes();
+ AFPResourceInfo resourceInfo
+ = foreignAttributeReader.getResourceInfo(foreignAttributes);
+ resourceInfo.setUri(rendererImageInfo.getURI());
+ dataObjectInfo.setResourceInfo(resourceInfo);
+
+ // set object area
+ AFPObjectAreaInfo objectAreaInfo = new AFPObjectAreaInfo();
+
+ Point origin = rendererImageInfo.getOrigin();
+ Rectangle2D position = rendererImageInfo.getPosition();
+ float srcX = origin.x + (float)position.getX();
+ float srcY = origin.y + (float)position.getY();
+
+ AFPRendererContext rendererContext
+ = (AFPRendererContext)rendererImageInfo.getRendererContext();
+ AFPInfo afpInfo = rendererContext.getInfo();
+ AFPPaintingState paintingState = afpInfo.getPaintingState();
+ AFPUnitConverter unitConv = paintingState.getUnitConverter();
+ int[] coords = unitConv.mpts2units(new float[] {srcX, srcY});
+ objectAreaInfo.setX(coords[X]);
+ objectAreaInfo.setY(coords[Y]);
+
+ int width = Math.round(unitConv.mpt2units((float)position.getWidth()));
+ objectAreaInfo.setWidth(width);
+
+ int height = Math.round(unitConv.mpt2units((float)position.getHeight()));
+ objectAreaInfo.setHeight(height);
+
+ int resolution = paintingState.getResolution();
+ objectAreaInfo.setHeightRes(resolution);
+ objectAreaInfo.setWidthRes(resolution);
+
+ objectAreaInfo.setRotation(paintingState.getRotation());
+
+ dataObjectInfo.setObjectAreaInfo(objectAreaInfo);
+
+ return dataObjectInfo;
+ }
+
+ /**
+ * Creates the data object information object
+ *
+ * @return the data object information object
+ */
+ protected abstract AFPDataObjectInfo createDataObjectInfo();
+ }
--- /dev/null
- import org.apache.fop.pdf.PDFState;
+/*
+ * 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;
+import java.awt.Rectangle;
+import java.awt.geom.AffineTransform;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.fop.pdf.PDFColor;
+import org.apache.fop.pdf.PDFDocument;
+import org.apache.fop.pdf.PDFFilterList;
+import org.apache.fop.pdf.PDFNumber;
++import org.apache.fop.pdf.PDFPaintingState;
+import org.apache.fop.pdf.PDFResourceContext;
- protected PDFState currentState = null;
+import org.apache.fop.pdf.PDFStream;
+import org.apache.fop.pdf.PDFTextUtil;
+import org.apache.fop.pdf.PDFXObject;
+
+/**
+ * Generator class encapsulating all object references and state necessary to generate a
+ * PDF content stream.
+ */
+public class PDFContentGenerator {
+
+ /** Controls whether comments are written to the PDF stream. */
+ protected static final boolean WRITE_COMMENTS = true;
+
+ private PDFDocument document;
+ private OutputStream outputStream;
+ private PDFResourceContext resourceContext;
+
+ /** the current stream to add PDF commands to */
+ private PDFStream currentStream;
+
+ /** drawing state */
- this.currentState = new PDFState();
++ protected PDFPaintingState currentState = null;
+ /** Text generation utility holding the current font status */
+ protected PDFTextUtil textutil;
+
+
+ /**
+ * Main constructor. Creates a new PDF stream and additional helper classes for text painting
+ * and state management.
+ * @param document the PDF document
+ * @param out the output stream the PDF document is generated to
+ * @param resourceContext the resource context
+ */
+ public PDFContentGenerator(PDFDocument document, OutputStream out,
+ PDFResourceContext resourceContext) {
+ this.document = document;
+ this.outputStream = out;
+ this.resourceContext = resourceContext;
+ this.currentStream = document.getFactory()
+ .makeStream(PDFFilterList.CONTENT_FILTER, false);
+ this.textutil = new PDFTextUtil() {
+ protected void write(String code) {
+ currentStream.add(code);
+ }
+ };
+
- public PDFState getState() {
++ this.currentState = new PDFPaintingState();
+ }
+
+ /**
+ * Returns the applicable resource context for the generator.
+ * @return the resource context
+ */
+ public PDFDocument getDocument() {
+ return this.document;
+ }
+
+ /**
+ * Returns the output stream the PDF document is written to.
+ * @return the output stream
+ */
+ public OutputStream getOutputStream() {
+ return this.outputStream;
+ }
+
+ /**
+ * Returns the applicable resource context for the generator.
+ * @return the resource context
+ */
+ public PDFResourceContext getResourceContext() {
+ return this.resourceContext;
+ }
+
+ /**
+ * Returns the {@code PDFStream} associated with this instance.
+ * @return the PDF stream
+ */
+ public PDFStream getStream() {
+ return this.currentStream;
+ }
+
+ /**
+ * Returns the {@code PDFState} associated with this instance.
+ * @return the PDF state
+ */
- currentState.push();
++ public PDFPaintingState getState() {
+ return this.currentState;
+ }
+
+ /**
+ * Returns the {@code PDFTextUtil} associated with this instance.
+ * @return the text utility
+ */
+ public PDFTextUtil getTextUtil() {
+ return this.textutil;
+ }
+
+ /**
+ * Flushes all queued PDF objects ready to be written to the output stream.
+ * @throws IOException if an error occurs while flushing the PDF objects
+ */
+ public void flushPDFDoc() throws IOException {
+ this.document.output(this.outputStream);
+ }
+
+ /**
+ * Writes out a comment.
+ * @param text text for the comment
+ */
+ protected void comment(String text) {
+ if (WRITE_COMMENTS) {
+ currentStream.add("% " + text + "\n");
+ }
+ }
+
+ /** {@inheritDoc} */
+ protected void saveGraphicsState() {
+ endTextObject();
- currentState.pop();
++ currentState.save();
+ currentStream.add("q\n");
+ }
+
+ /**
+ * Restored the graphics state valid before the previous {@code #saveGraphicsState()}.
+ * @param popState true if the state should also be popped, false if only the PDF command
+ * should be issued
+ */
+ protected void restoreGraphicsState(boolean popState) {
+ endTextObject();
+ currentStream.add("Q\n");
+ if (popState) {
++ currentState.restore();
+ }
+ }
+
+ /** {@inheritDoc} */
+ protected void restoreGraphicsState() {
+ restoreGraphicsState(true);
+ }
+
+ /** Indicates the beginning of a text object. */
+ protected void beginTextObject() {
+ if (!textutil.isInTextObject()) {
+ textutil.beginTextObject();
+ }
+ }
+
+ /** Indicates the end of a text object. */
+ protected void endTextObject() {
+ if (textutil.isInTextObject()) {
+ textutil.endTextObject();
+ }
+ }
+
+ /**
+ * Concatenates the given transformation matrix with the current one.
+ * @param transform the transformation matrix (in points)
+ */
+ public void concatenate(AffineTransform transform) {
+ if (!transform.isIdentity()) {
+ currentState.concatenate(transform);
+ currentStream.add(CTMHelper.toPDFString(transform, false) + " cm\n");
+ }
+ }
+
+ /**
+ * Intersects the current clip region with the given rectangle.
+ * @param rect the clip rectangle
+ */
+ public void clipRect(Rectangle rect) {
+ StringBuffer sb = new StringBuffer();
+ sb.append(format(rect.x / 1000f)).append(' ');
+ sb.append(format(rect.y / 1000f)).append(' ');
+ sb.append(format(rect.width / 1000f)).append(' ');
+ sb.append(format(rect.height / 1000f)).append(" re W n\n");
+ add(sb.toString());
+ }
+
+ /**
+ * Adds content to the stream.
+ * @param content the PDF content
+ */
+ public void add(String content) {
+ currentStream.add(content);
+ }
+
+ /**
+ * Formats a float value (normally coordinates in points) as Strings.
+ * @param value the value
+ * @return the formatted value
+ */
+ public static final String format(float value) {
+ return PDFNumber.doubleOut(value);
+ }
+
+ /**
+ * Sets the current line width in points.
+ * @param width line width in points
+ */
+ public void updateLineWidth(float width) {
+ if (currentState.setLineWidth(width)) {
+ //Only write if value has changed WRT the current line width
+ currentStream.add(format(width) + " w\n");
+ }
+ }
+
+ /**
+ * Establishes a new foreground or fill color. In contrast to updateColor
+ * this method does not check the PDFState for optimization possibilities.
+ * @param col the color to apply
+ * @param fill true to set the fill color, false for the foreground color
+ * @param pdf StringBuffer to write the PDF code to
+ *//*
+ public void setColor(Color col, boolean fill, StringBuffer pdf) {
+ assert pdf != null;
+ }*/
+
+ /**
+ * Establishes a new foreground or fill color.
+ * @param col the color to apply
+ * @param fill true to set the fill color, false for the foreground color
+ * @param stream the PDFStream to write the PDF code to
+ */
+ public void setColor(Color col, boolean fill, PDFStream stream) {
+ assert stream != null;
+ PDFColor color = new PDFColor(this.document, col);
+ stream.add(color.getColorSpaceOut(fill));
+ }
+
+ /**
+ * Establishes a new foreground or fill color.
+ * @param col the color to apply
+ * @param fill true to set the fill color, false for the foreground color
+ */
+ public void setColor(Color col, boolean fill) {
+ setColor(col, fill, getStream());
+ }
+
+ /**
+ * Establishes a new foreground or fill color. In contrast to updateColor
+ * this method does not check the PDFState for optimization possibilities.
+ * @param col the color to apply
+ * @param fill true to set the fill color, false for the foreground color
+ * @param pdf StringBuffer to write the PDF code to, if null, the code is
+ * written to the current stream.
+ */
+ protected void setColor(Color col, boolean fill, StringBuffer pdf) {
+ if (pdf != null) {
+ PDFColor color = new PDFColor(this.document, col);
+ pdf.append(color.getColorSpaceOut(fill));
+ } else {
+ setColor(col, fill, this.currentStream);
+ }
+ }
+
+ /**
+ * Establishes a new foreground or fill color.
+ * @param col the color to apply (null skips this operation)
+ * @param fill true to set the fill color, false for the foreground color
+ * @param pdf StringBuffer to write the PDF code to, if null, the code is
+ * written to the current stream.
+ */
+ public void updateColor(Color col, boolean fill, StringBuffer pdf) {
+ if (col == null) {
+ return;
+ }
+ boolean update = false;
+ if (fill) {
+ update = getState().setBackColor(col);
+ } else {
+ update = getState().setColor(col);
+ }
+
+ if (update) {
+ setColor(col, fill, pdf);
+ }
+ }
+
+ /**
+ * Places a previously registered image at a certain place on the page.
+ * @param x X coordinate
+ * @param y Y coordinate
+ * @param w width for image
+ * @param h height for image
+ * @param xobj the image XObject
+ */
+ public void placeImage(float x, float y, float w, float h, PDFXObject xobj) {
+ saveGraphicsState();
+ add(format(w) + " 0 0 "
+ + format(-h) + " "
+ + format(x) + " "
+ + format(y + h)
+ + " cm\n" + xobj.getName() + " Do\n");
+ restoreGraphicsState();
+ }
+
+
+}
AffineTransform transform = new AffineTransform();
transform.translate(fx, fy);
- pdfInfo.pdfPaintingState.concatenate(transform);
- graphics.setPaintingState(pdfInfo.pdfPaintingState);
+ generator.getState().concatenate(transform);
- graphics.setPDFState(generator.getState());
++ graphics.setPaintingState(generator.getState());
graphics.setOutputStream(pdfInfo.outputStream);
if (pdfInfo.paintAsBitmap) {
import java.awt.Rectangle;
import java.io.IOException;
- import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.Image;
+
import org.apache.fop.pdf.PDFXObject;
-import org.apache.fop.render.ImageHandler;
++import org.apache.fop.render.ImageHandlerBase;
import org.apache.fop.render.RendererContext;
-import org.apache.xmlgraphics.image.loader.Image;
/**
* This interface is used for handling all sorts of image type for PDF output.
*/
- public interface PDFImageHandler {
-
- /**
- * 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
- */
- Class getSupportedImageClass();
-public interface PDFImageHandler extends ImageHandler {
++public interface PDFImageHandler extends ImageHandlerBase {
/**
* Generates the PDF objects for the given {@link Image} instance. If the handler generates
return null;
}
- graphics.setPDFState(generator.getState());
+ /** {@inheritDoc} */
+ public void handleImage(RenderingContext context, Image image, Rectangle pos)
+ throws IOException {
+ PDFRenderingContext pdfContext = (PDFRenderingContext)context;
+ PDFContentGenerator generator = pdfContext.getGenerator();
+ ImageGraphics2D imageG2D = (ImageGraphics2D)image;
+ float fwidth = pos.width / 1000f;
+ float fheight = pos.height / 1000f;
+ float fx = pos.x / 1000f;
+ float fy = pos.y / 1000f;
+
+ // get the 'width' and 'height' attributes of the SVG document
+ Dimension dim = image.getInfo().getSize().getDimensionMpt();
+ float imw = (float)dim.getWidth() / 1000f;
+ float imh = (float)dim.getHeight() / 1000f;
+
+ float sx = fwidth / (float)imw;
+ float sy = fheight / (float)imh;
+
+ generator.comment("G2D start");
+ generator.saveGraphicsState();
+ generator.updateColor(Color.black, false, null);
+ generator.updateColor(Color.black, true, null);
+
+ //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.
+ generator.add(sx + " 0 0 " + sy + " " + fx + " " + fy + " cm\n");
+
+ final boolean textAsShapes = false;
+ PDFGraphics2D graphics = new PDFGraphics2D(textAsShapes,
+ pdfContext.getFontInfo(), generator.getDocument(),
+ generator.getResourceContext(), pdfContext.getPage().referencePDF(),
+ "", 0.0f);
+ graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext());
+
+ AffineTransform transform = new AffineTransform();
+ transform.translate(fx, fy);
+ generator.getState().concatenate(transform);
++ graphics.setPaintingState(generator.getState());
+ graphics.setOutputStream(generator.getOutputStream());
+
+ Rectangle2D area = new Rectangle2D.Double(0.0, 0.0, imw, imh);
+ imageG2D.getGraphics2DImagePainter().paint(graphics, area);
+
+ generator.add(graphics.getString());
+ generator.restoreGraphicsState();
+ generator.comment("G2D end");
+ }
+
/** {@inheritDoc} */
public int getPriority() {
return 200;
--- /dev/null
- generator.getState().push();
+/*
+ * 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;
+import java.awt.Rectangle;
+import java.awt.geom.AffineTransform;
+import java.io.IOException;
+
+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.xmlgraphics.image.loader.Image;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.image.loader.batik.BatikImageFlavors;
+import org.apache.fop.render.ImageHandler;
+import org.apache.fop.render.RenderingContext;
+import org.apache.fop.svg.PDFAElementBridge;
+import org.apache.fop.svg.PDFBridgeContext;
+import org.apache.fop.svg.PDFGraphics2D;
+import org.apache.fop.svg.SVGEventProducer;
+import org.apache.fop.svg.SVGUserAgent;
+
+/**
+ * Image Handler implementation which handles SVG images.
+ */
+public class PDFImageHandlerSVG implements ImageHandler {
+
+ /** logging instance */
+ private static Log log = LogFactory.getLog(PDFImageHandlerSVG.class);
+
+ /** {@inheritDoc} */
+ public void handleImage(RenderingContext context, Image image, Rectangle pos)
+ throws IOException {
+ PDFRenderingContext pdfContext = (PDFRenderingContext)context;
+ PDFContentGenerator generator = pdfContext.getGenerator();
+ ImageXMLDOM imageSVG = (ImageXMLDOM)image;
+
+ FOUserAgent userAgent = context.getUserAgent();
+ final float deviceResolution = userAgent.getTargetResolution();
+ if (log.isDebugEnabled()) {
+ log.debug("Generating SVG at " + deviceResolution + "dpi.");
+ }
+
+ final float uaResolution = userAgent.getSourceResolution();
+ SVGUserAgent ua = new SVGUserAgent(userAgent, new AffineTransform());
+
+ //Scale for higher resolution on-the-fly images from Batik
+ double s = uaResolution / deviceResolution;
+ AffineTransform resolutionScaling = new AffineTransform();
+ resolutionScaling.scale(s, s);
+
+ GVTBuilder builder = new GVTBuilder();
+
+ //Controls whether text painted by Batik is generated using text or path operations
+ boolean strokeText = false;
+ //TODO connect with configuration elsewhere.
+
+ BridgeContext ctx = new PDFBridgeContext(ua,
+ (strokeText ? null : pdfContext.getFontInfo()),
+ userAgent.getFactory().getImageManager(),
+ userAgent.getImageSessionContext(),
+ new AffineTransform());
+
+ GraphicsNode root;
+ try {
+ root = builder.build(ctx, imageSVG.getDocument());
+ builder = null;
+ } catch (Exception e) {
+ SVGEventProducer eventProducer = SVGEventProducer.Provider.get(
+ context.getUserAgent().getEventBroadcaster());
+ eventProducer.svgNotBuilt(this, e, image.getInfo().getOriginalURI());
+ return;
+ }
+ // get the 'width' and 'height' attributes of the SVG document
+ float w = (float)ctx.getDocumentSize().getWidth() * 1000f;
+ float h = (float)ctx.getDocumentSize().getHeight() * 1000f;
+
+ float sx = pos.width / (float)w;
+ float sy = pos.height / (float)h;
+
+ //Scaling and translation for the bounding box of the image
+ AffineTransform scaling = new AffineTransform(
+ sx, 0, 0, sy, pos.x / 1000f, pos.y / 1000f);
+
+ //Transformation matrix that establishes the local coordinate system for the SVG graphic
+ //in relation to the current coordinate system
+ AffineTransform imageTransform = new AffineTransform();
+ imageTransform.concatenate(scaling);
+ imageTransform.concatenate(resolutionScaling);
+
+ /*
+ * Clip to the svg area.
+ * Note: To have the svg overlay (under) a text area then use
+ * an fo:block-container
+ */
+ generator.comment("SVG setup");
+ generator.saveGraphicsState();
+ generator.setColor(Color.black, false);
+ generator.setColor(Color.black, true);
+
+ if (!scaling.isIdentity()) {
+ generator.comment("viewbox");
+ generator.add(CTMHelper.toPDFString(scaling, false) + " cm\n");
+ }
+
+ //SVGSVGElement svg = ((SVGDocument)doc).getRootElement();
+
+ PDFGraphics2D graphics = new PDFGraphics2D(true, pdfContext.getFontInfo(),
+ generator.getDocument(),
+ generator.getResourceContext(), pdfContext.getPage().referencePDF(),
+ "", 0);
+ graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext());
+
+ if (!resolutionScaling.isIdentity()) {
+ generator.comment("resolution scaling for " + uaResolution
+ + " -> " + deviceResolution + "\n");
+ generator.add(
+ CTMHelper.toPDFString(resolutionScaling, false) + " cm\n");
+ graphics.scale(1 / s, 1 / s);
+ }
+
+ generator.comment("SVG start");
+
+ //Save state and update coordinate system for the SVG image
- graphics.setPDFState(generator.getState());
++ generator.getState().save();
+ generator.getState().concatenate(imageTransform);
+
+ //Now that we have the complete transformation matrix for the image, we can update the
+ //transformation matrix for the AElementBridge.
+ PDFAElementBridge aBridge = (PDFAElementBridge)ctx.getBridge(
+ SVGDOMImplementation.SVG_NAMESPACE_URI, SVGConstants.SVG_A_TAG);
+ aBridge.getCurrentTransform().setTransform(generator.getState().getTransform());
+
- generator.getState().pop();
++ graphics.setPaintingState(generator.getState());
+ graphics.setOutputStream(generator.getOutputStream());
+ try {
+ root.paint(graphics);
+ generator.add(graphics.getString());
+ } catch (Exception e) {
+ SVGEventProducer eventProducer = SVGEventProducer.Provider.get(
+ context.getUserAgent().getEventBroadcaster());
+ eventProducer.svgRenderingError(this, e, image.getInfo().getOriginalURI());
+ }
++ generator.getState().restore();
+ generator.restoreGraphicsState();
+ generator.comment("SVG end");
+ }
+
+ /** {@inheritDoc} */
+ public int getPriority() {
+ return 400;
+ }
+
+ /** {@inheritDoc} */
+ public Class getSupportedImageClass() {
+ return ImageXMLDOM.class;
+ }
+
+ /** {@inheritDoc} */
+ public ImageFlavor[] getSupportedImageFlavors() {
+ return new ImageFlavor[] {
+ BatikImageFlavors.SVG_DOM
+ };
+ }
+
+ /** {@inheritDoc} */
+ public boolean isCompatible(RenderingContext targetContext, Image image) {
+ return (image == null
+ || (image instanceof ImageXMLDOM
+ && image.getFlavor().isCompatible(BatikImageFlavors.SVG_DOM)))
+ && targetContext instanceof PDFRenderingContext;
+ }
+
+}
import org.apache.fop.pdf.PDFAMode;
import org.apache.fop.pdf.PDFAction;
import org.apache.fop.pdf.PDFAnnotList;
-import org.apache.fop.pdf.PDFColor;
-import org.apache.fop.pdf.PDFConformanceException;
-import org.apache.fop.pdf.PDFDictionary;
import org.apache.fop.pdf.PDFDocument;
-import org.apache.fop.pdf.PDFEncryptionManager;
import org.apache.fop.pdf.PDFEncryptionParams;
import org.apache.fop.pdf.PDFFactory;
-import org.apache.fop.pdf.PDFFilterList;
import org.apache.fop.pdf.PDFGoTo;
-import org.apache.fop.pdf.PDFICCBasedColorSpace;
-import org.apache.fop.pdf.PDFICCStream;
import org.apache.fop.pdf.PDFInfo;
import org.apache.fop.pdf.PDFLink;
-import org.apache.fop.pdf.PDFMetadata;
import org.apache.fop.pdf.PDFNumber;
-import org.apache.fop.pdf.PDFNumsArray;
import org.apache.fop.pdf.PDFOutline;
-import org.apache.fop.pdf.PDFOutputIntent;
import org.apache.fop.pdf.PDFPage;
-import org.apache.fop.pdf.PDFPageLabels;
+ import org.apache.fop.pdf.PDFPaintingState;
import org.apache.fop.pdf.PDFResourceContext;
import org.apache.fop.pdf.PDFResources;
- import org.apache.fop.pdf.PDFState;
-import org.apache.fop.pdf.PDFStream;
import org.apache.fop.pdf.PDFTextUtil;
import org.apache.fop.pdf.PDFXMode;
import org.apache.fop.pdf.PDFXObject;
import org.apache.fop.render.AbstractPathOrientedRenderer;
import org.apache.fop.render.Graphics2DAdapter;
import org.apache.fop.render.RendererContext;
+import org.apache.fop.traits.RuleStyle;
+ import org.apache.fop.util.AbstractPaintingState;
import org.apache.fop.util.CharUtilities;
-import org.apache.fop.util.ColorProfileUtil;
--import org.apache.fop.util.ColorUtil;
+ import org.apache.fop.util.AbstractPaintingState.AbstractData;
-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;
/**
* Renderer that renders areas to PDF.
/** page height */
protected int pageHeight;
- /** Registry of PDF filters */
- protected Map filterMap;
-
/** Image handler registry */
- private PDFImageHandlerRegistry imageHandlerRegistry = new PDFImageHandlerRegistry();
+ private final PDFImageHandlerRegistry imageHandlerRegistry = new PDFImageHandlerRegistry();
+
/**
* create the PDF renderer
*/
public PDFRenderer() {
}
- private boolean booleanValueOf(Object obj) {
- if (obj instanceof Boolean) {
- return ((Boolean)obj).booleanValue();
- } else if (obj instanceof String) {
- return Boolean.valueOf((String)obj).booleanValue();
- } else {
- throw new IllegalArgumentException("Boolean or \"true\" or \"false\" expected.");
- }
- }
-
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void setUserAgent(FOUserAgent agent) {
super.setUserAgent(agent);
- PDFEncryptionParams params
- = (PDFEncryptionParams)agent.getRendererOptions().get(ENCRYPTION_PARAMS);
- if (params != null) {
- this.encryptionParams = params; //overwrite if available
- }
- String pwd;
- pwd = (String)agent.getRendererOptions().get(USER_PASSWORD);
- if (pwd != null) {
- if (encryptionParams == null) {
- this.encryptionParams = new PDFEncryptionParams();
- }
- this.encryptionParams.setUserPassword(pwd);
- }
- pwd = (String)agent.getRendererOptions().get(OWNER_PASSWORD);
- if (pwd != null) {
- if (encryptionParams == null) {
- this.encryptionParams = new PDFEncryptionParams();
- }
- this.encryptionParams.setOwnerPassword(pwd);
- }
- Object setting;
- setting = agent.getRendererOptions().get(NO_PRINT);
- if (setting != null) {
- if (encryptionParams == null) {
- this.encryptionParams = new PDFEncryptionParams();
- }
- this.encryptionParams.setAllowPrint(!booleanValueOf(setting));
- }
- setting = agent.getRendererOptions().get(NO_COPY_CONTENT);
- if (setting != null) {
- if (encryptionParams == null) {
- this.encryptionParams = new PDFEncryptionParams();
- }
- this.encryptionParams.setAllowCopyContent(!booleanValueOf(setting));
- }
- setting = agent.getRendererOptions().get(NO_EDIT_CONTENT);
- if (setting != null) {
- if (encryptionParams == null) {
- this.encryptionParams = new PDFEncryptionParams();
- }
- this.encryptionParams.setAllowEditContent(!booleanValueOf(setting));
- }
- setting = agent.getRendererOptions().get(NO_ANNOTATIONS);
- if (setting != null) {
- if (encryptionParams == null) {
- this.encryptionParams = new PDFEncryptionParams();
- }
- this.encryptionParams.setAllowEditAnnotations(!booleanValueOf(setting));
- }
- String s = (String)agent.getRendererOptions().get(PDF_A_MODE);
- if (s != null) {
- this.pdfAMode = PDFAMode.valueOf(s);
- }
- s = (String)agent.getRendererOptions().get(PDF_X_MODE);
- if (s != null) {
- this.pdfXMode = PDFXMode.valueOf(s);
- }
- s = (String)agent.getRendererOptions().get(KEY_OUTPUT_PROFILE);
- if (s != null) {
- this.outputProfileURI = s;
- }
- setting = agent.getRendererOptions().get(KEY_DISABLE_SRGB_COLORSPACE);
- if (setting != null) {
- this.disableSRGBColorSpace = booleanValueOf(setting);
- }
+ this.pdfUtil = new PDFRenderingUtil(getUserAgent());
}
- /**
- * {@inheritDoc}
- */
- public void startRenderer(OutputStream stream) throws IOException {
- if (userAgent == null) {
- throw new IllegalStateException("UserAgent must be set before starting the renderer");
- }
- ostream = stream;
- this.pdfDoc = new PDFDocument(
- userAgent.getProducer() != null ? userAgent.getProducer() : "");
- this.pdfDoc.getProfile().setPDFAMode(this.pdfAMode);
- this.pdfDoc.getProfile().setPDFXMode(this.pdfXMode);
- this.pdfDoc.getInfo().setCreator(userAgent.getCreator());
- this.pdfDoc.getInfo().setCreationDate(userAgent.getCreationDate());
- this.pdfDoc.getInfo().setAuthor(userAgent.getAuthor());
- this.pdfDoc.getInfo().setTitle(userAgent.getTitle());
- this.pdfDoc.getInfo().setKeywords(userAgent.getKeywords());
- this.pdfDoc.setFilterMap(filterMap);
- this.pdfDoc.outputHeader(ostream);
-
- //Setup encryption if necessary
- PDFEncryptionManager.setupPDFEncryption(encryptionParams, this.pdfDoc);
-
- addsRGBColorSpace();
- if (this.outputProfileURI != null) {
- addDefaultOutputProfile();
- }
- if (pdfXMode != PDFXMode.DISABLED) {
- log.debug(pdfXMode + " is active.");
- log.warn("Note: " + pdfXMode
- + " support is work-in-progress and not fully implemented, yet!");
- addPDFXOutputIntent();
- }
- if (pdfAMode.isPDFA1LevelB()) {
- log.debug("PDF/A is active. Conformance Level: " + pdfAMode);
- addPDFA1OutputIntent();
- }
-
+ PDFRenderingUtil getPDFUtil() {
+ return this.pdfUtil;
}
- private void addsRGBColorSpace() throws IOException {
- if (disableSRGBColorSpace) {
- if (this.pdfAMode != PDFAMode.DISABLED
- || this.pdfXMode != PDFXMode.DISABLED
- || this.outputProfileURI != null) {
- throw new IllegalStateException("It is not possible to disable the sRGB color"
- + " space if PDF/A or PDF/X functionality is enabled or an"
- + " output profile is set!");
- }
- } else {
- if (this.sRGBColorSpace != null) {
- return;
- }
- //Map sRGB as default RGB profile for DeviceRGB
- this.sRGBColorSpace = PDFICCBasedColorSpace.setupsRGBAsDefaultRGBColorSpace(pdfDoc);
- }
+ PDFContentGenerator getGenerator() {
+ return this.generator;
}
- PDFState getState() {
- private void addDefaultOutputProfile() throws IOException {
- if (this.outputProfile != null) {
- return;
- }
- ICC_Profile profile;
- InputStream in = null;
- if (this.outputProfileURI != null) {
- this.outputProfile = pdfDoc.getFactory().makePDFICCStream();
- Source src = userAgent.resolveURI(this.outputProfileURI);
- if (src == null) {
- throw new IOException("Output profile not found: " + this.outputProfileURI);
- }
- if (src instanceof StreamSource) {
- in = ((StreamSource)src).getInputStream();
- } else {
- in = new URL(src.getSystemId()).openStream();
- }
- try {
- profile = ICC_Profile.getInstance(in);
- } finally {
- IOUtils.closeQuietly(in);
- }
- this.outputProfile.setColorSpace(profile, null);
- } else {
- //Fall back to sRGB profile
- outputProfile = sRGBColorSpace.getICCStream();
- }
++ PDFPaintingState getState() {
+ return getGenerator().getState();
}
- /**
- * Adds an OutputIntent to the PDF as mandated by PDF/A-1 when uncalibrated color spaces
- * are used (which is true if we use DeviceRGB to represent sRGB colors).
- * @throws IOException in case of an I/O problem
- */
- private void addPDFA1OutputIntent() throws IOException {
- addDefaultOutputProfile();
-
- String desc = ColorProfileUtil.getICCProfileDescription(this.outputProfile.getICCProfile());
- PDFOutputIntent outputIntent = pdfDoc.getFactory().makeOutputIntent();
- outputIntent.setSubtype(PDFOutputIntent.GTS_PDFA1);
- outputIntent.setDestOutputProfile(this.outputProfile);
- outputIntent.setOutputConditionIdentifier(desc);
- outputIntent.setInfo(outputIntent.getOutputConditionIdentifier());
- pdfDoc.getRoot().addOutputIntent(outputIntent);
- }
-
- /**
- * Adds an OutputIntent to the PDF as mandated by PDF/X when uncalibrated color spaces
- * are used (which is true if we use DeviceRGB to represent sRGB colors).
- * @throws IOException in case of an I/O problem
- */
- private void addPDFXOutputIntent() throws IOException {
- addDefaultOutputProfile();
-
- String desc = ColorProfileUtil.getICCProfileDescription(this.outputProfile.getICCProfile());
- int deviceClass = this.outputProfile.getICCProfile().getProfileClass();
- if (deviceClass != ICC_Profile.CLASS_OUTPUT) {
- throw new PDFConformanceException(pdfDoc.getProfile().getPDFXMode() + " requires that"
- + " the DestOutputProfile be an Output Device Profile. "
- + desc + " does not match that requirement.");
+ /** {@inheritDoc} */
+ public void startRenderer(OutputStream stream) throws IOException {
+ if (userAgent == null) {
+ throw new IllegalStateException("UserAgent must be set before starting the renderer");
}
- PDFOutputIntent outputIntent = pdfDoc.getFactory().makeOutputIntent();
- outputIntent.setSubtype(PDFOutputIntent.GTS_PDFX);
- outputIntent.setDestOutputProfile(this.outputProfile);
- outputIntent.setOutputConditionIdentifier(desc);
- outputIntent.setInfo(outputIntent.getOutputConditionIdentifier());
- pdfDoc.getRoot().addOutputIntent(outputIntent);
+ ostream = stream;
+ this.pdfDoc = pdfUtil.setupPDFDocument(stream);
}
/**
pages = null;
pageReferences.clear();
- pvReferences.clear();
+ //pvReferences.clear();
pdfResources = null;
- currentStream = null;
+ this.generator = null;
- //currentStream = null;
currentContext = null;
currentPage = null;
- //currentState = null;
- //this.textutil = null;
- paintingState = null;
- this.textutil = null;
idPositions.clear();
idGoTos.clear();
(float)clippingRect.getHeight() / 1000f);
}
// multiply with current CTM
- currentStream.add(CTMHelper.toPDFString(ctm) + " cm\n");
+ generator.concatenate(new AffineTransform(CTMHelper.toPDFArray(ctm)));
- //currentStream.add(CTMHelper.toPDFString(ctm) + " cm\n");
}
/** {@inheritDoc} */
/** {@inheritDoc} */
protected void concatenateTransformationMatrix(AffineTransform at) {
- if (!at.isIdentity()) {
- paintingState.concatenate(at);
- currentStream.add(CTMHelper.toPDFString(at, false) + " cm\n");
- }
+ generator.concatenate(at);
- /*
- if (!at.isIdentity()) {
- currentState.concatenate(at);
- currentStream.add(CTMHelper.toPDFString(at, false) + " cm\n");
- }*/
}
/**
/**
* {@inheritDoc}
*/
- protected void fillRect(float x, float y, float w, float h) {
- if (w != 0 && h != 0) {
+ protected void fillRect(float x, float y, float width, float height) {
+ if (width > 0 && height > 0) {
- currentStream.add(format(x) + " " + format(y) + " "
+ generator.add(format(x) + " " + format(y) + " "
- + format(w) + " " + format(h) + " re f\n");
+ + format(width) + " " + format(height) + " re f\n");
}
}
* @return the saved state stack to recreate later
*/
protected List breakOutOfStateStack() {
-// return currentState.popAll();
++ PDFPaintingState paintingState = getState();
List breakOutList = new java.util.ArrayList();
- PDFState.Data data;
+ AbstractPaintingState.AbstractData data;
while (true) {
- data = getState().getData();
- if (getState().pop() == null) {
+ data = paintingState.getData();
+ if (paintingState.restore() == null) {
break;
}
if (breakOutList.size() == 0) {
* @param breakOutList the state stack to restore.
*/
protected void restoreStateStackAfterBreakOut(List breakOutList) {
- comment("------ restoring context after break-out...");
+ generator.comment("------ restoring context after break-out...");
- PDFState.Data data;
+ // currentState.pushAll(breakOutList);
+ AbstractData data;
Iterator i = breakOutList.iterator();
while (i.hasNext()) {
- data = (PDFState.Data)i.next();
+ data = (AbstractData)i.next();
saveGraphicsState();
AffineTransform at = data.getTransform();
concatenateTransformationMatrix(at);
info = manager.getImageInfo(uri, sessionContext);
Map hints = ImageUtil.getDefaultHints(sessionContext);
+ ImageFlavor[] supportedFlavors = imageHandlerRegistry.getSupportedFlavors();
org.apache.xmlgraphics.image.loader.Image img = manager.getImage(
- info, imageHandlerRegistry.getSupportedFlavors(), hints, sessionContext);
+ info, supportedFlavors, hints, sessionContext);
//First check for a dynamically registered handler
- PDFImageHandler handler = imageHandlerRegistry.getHandler(img.getClass());
+ PDFImageHandler handler
+ = (PDFImageHandler)imageHandlerRegistry.getHandler(img.getClass());
if (handler != null) {
if (log.isDebugEnabled()) {
log.debug("Using PDFImageHandler: " + handler.getClass().getName());
// output new data
try {
- this.pdfDoc.output(ostream);
+ this.generator.flushPDFDoc();
} catch (IOException ioe) {
// ioexception will be caught later
+ log.error(ioe.getMessage());
}
}
x, y, width, height, foreignAttributes);
context.setProperty(PDFRendererContextConstants.PDF_DOCUMENT, pdfDoc);
context.setProperty(PDFRendererContextConstants.OUTPUT_STREAM, ostream);
- context.setProperty(PDFRendererContextConstants.PDF_STATE, getState());
- context.setProperty(PDFRendererContextConstants.PDF_PAINTING_STATE, paintingState);
context.setProperty(PDFRendererContextConstants.PDF_PAGE, currentPage);
context.setProperty(PDFRendererContextConstants.PDF_CONTEXT,
currentContext == null ? currentPage : currentContext);
Configuration cfg = super.getRendererConfig(renderer);
if (cfg != null) {
PDFRenderer pdfRenderer = (PDFRenderer)renderer;
- //PDF filters
- try {
- Map filterMap = buildFilterMapFromConfiguration(cfg);
- if (filterMap != null) {
- pdfRenderer.setFilterMap(filterMap);
- }
- } catch (ConfigurationException e) {
- LogUtil.handleException(log, e, false);
- }
-
super.configure(renderer);
- String s = cfg.getChild(PDFRenderer.PDF_A_MODE, true).getValue(null);
- if (s != null) {
- pdfRenderer.setAMode(PDFAMode.valueOf(s));
- }
- s = cfg.getChild(PDFRenderer.PDF_X_MODE, true).getValue(null);
- if (s != null) {
- pdfRenderer.setXMode(PDFXMode.valueOf(s));
+ PDFRenderingUtil pdfUtil = pdfRenderer.getPDFUtil();
+ configure(cfg, pdfUtil);
+ }
+ }
+
+ private void configure(Configuration cfg, PDFRenderingUtil pdfUtil) throws FOPException {
+ //PDF filters
+ try {
+ Map filterMap = buildFilterMapFromConfiguration(cfg);
+ if (filterMap != null) {
+ pdfUtil.setFilterMap(filterMap);
}
- Configuration encryptionParamsConfig = cfg.getChild(PDFRenderer.ENCRYPTION_PARAMS, false);
+ } catch (ConfigurationException e) {
+ LogUtil.handleException(log, e, false);
+ }
+
+ String s = cfg.getChild(PDFRenderer.PDF_A_MODE, true).getValue(null);
+ if (s != null) {
+ pdfUtil.setAMode(PDFAMode.valueOf(s));
+ }
+ s = cfg.getChild(PDFRenderer.PDF_X_MODE, true).getValue(null);
+ if (s != null) {
+ pdfUtil.setXMode(PDFXMode.valueOf(s));
+ }
- if (encryptionParamsConfig != null) {
- PDFEncryptionParams encryptionParams = new PDFEncryptionParams();
- Configuration ownerPasswordConfig = encryptionParamsConfig.getChild(
- PDFRenderer.OWNER_PASSWORD, false);
- if (ownerPasswordConfig != null) {
- String ownerPassword = ownerPasswordConfig.getValue(null);
- if (ownerPassword != null) {
- encryptionParams.setOwnerPassword(ownerPassword);
- }
- }
- Configuration userPasswordConfig = encryptionParamsConfig.getChild(
- PDFRenderer.USER_PASSWORD, false);
- if (userPasswordConfig != null) {
- String userPassword = userPasswordConfig.getValue(null);
- if (userPassword != null) {
- encryptionParams.setUserPassword(userPassword);
- }
+ Configuration encryptionParamsConfig
+ = cfg.getChild(PDFRenderer.ENCRYPTION_PARAMS, false);
+ if (encryptionParamsConfig != null) {
+ PDFEncryptionParams encryptionParams = new PDFEncryptionParams();
+ Configuration ownerPasswordConfig = encryptionParamsConfig.getChild(
+ PDFRenderer.OWNER_PASSWORD, false);
+ if (ownerPasswordConfig != null) {
+ String ownerPassword = ownerPasswordConfig.getValue(null);
+ if (ownerPassword != null) {
+ encryptionParams.setOwnerPassword(ownerPassword);
}
- Configuration noPrintConfig = encryptionParamsConfig.getChild(
- PDFRenderer.NO_PRINT, false);
- if (noPrintConfig != null) {
- encryptionParams.setAllowPrint(false);
- }
- Configuration noCopyContentConfig = encryptionParamsConfig.getChild(
- PDFRenderer.NO_COPY_CONTENT, false);
- if (noCopyContentConfig != null) {
- encryptionParams.setAllowCopyContent(false);
- }
- Configuration noEditContentConfig = encryptionParamsConfig.getChild(
- PDFRenderer.NO_EDIT_CONTENT, false);
- if (noEditContentConfig != null) {
- encryptionParams.setAllowEditContent(false);
- }
- Configuration noAnnotationsConfig = encryptionParamsConfig.getChild(
- PDFRenderer.NO_ANNOTATIONS, false);
- if (noAnnotationsConfig != null) {
- encryptionParams.setAllowEditAnnotations(false);
+ }
+ Configuration userPasswordConfig = encryptionParamsConfig.getChild(
+ PDFRenderer.USER_PASSWORD, false);
+ if (userPasswordConfig != null) {
+ String userPassword = userPasswordConfig.getValue(null);
+ if (userPassword != null) {
+ encryptionParams.setUserPassword(userPassword);
}
- pdfRenderer.setEncryptionParams(encryptionParams);
}
- s = cfg.getChild(PDFRenderer.KEY_OUTPUT_PROFILE, true).getValue(null);
- if (s != null) {
- pdfRenderer.setOutputProfileURI(s);
+ Configuration noPrintConfig = encryptionParamsConfig.getChild(
+ PDFRenderer.NO_PRINT, false);
+ if (noPrintConfig != null) {
+ encryptionParams.setAllowPrint(false);
}
- Configuration disableColorSpaceConfig
- = cfg.getChild(PDFRenderer.KEY_DISABLE_SRGB_COLORSPACE, false);
+ Configuration noCopyContentConfig = encryptionParamsConfig.getChild(
+ PDFRenderer.NO_COPY_CONTENT, false);
+ if (noCopyContentConfig != null) {
+ encryptionParams.setAllowCopyContent(false);
+ }
+ Configuration noEditContentConfig = encryptionParamsConfig.getChild(
+ PDFRenderer.NO_EDIT_CONTENT, false);
+ if (noEditContentConfig != null) {
+ encryptionParams.setAllowEditContent(false);
+ }
+ Configuration noAnnotationsConfig = encryptionParamsConfig.getChild(
+ PDFRenderer.NO_ANNOTATIONS, false);
+ if (noAnnotationsConfig != null) {
+ encryptionParams.setAllowEditAnnotations(false);
+ }
+ pdfUtil.setEncryptionParams(encryptionParams);
+ }
+ s = cfg.getChild(PDFRenderer.KEY_OUTPUT_PROFILE, true).getValue(null);
+ if (s != null) {
+ pdfUtil.setOutputProfileURI(s);
+ }
- if (disableColorSpaceConfig != null) {
- pdfRenderer.disableSRGBColorSpace
- = disableColorSpaceConfig.getValueAsBoolean(false);
- }
+ Configuration disableColorSpaceConfig = cfg.getChild(
+ PDFRenderer.KEY_DISABLE_SRGB_COLORSPACE, false);
+ if (disableColorSpaceConfig != null) {
+ pdfUtil.setDisableSRGBColorSpace(
+ disableColorSpaceConfig.getValueAsBoolean(false));
}
}
/** The PDF document that this image is being drawn into. */
String PDF_DOCUMENT = "pdfDoc";
- /** The current pdf state. */
- String PDF_STATE = "pdfState";
- /** The current PDF painting state. */
- String PDF_PAINTING_STATE = "pdfPaintingState";
--
/** The current PDF page for page renference and as a resource context. */
String PDF_PAGE = "pdfPage";
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
- import org.apache.xmlgraphics.util.QName;
-
import org.apache.fop.apps.FOUserAgent;
- import org.apache.fop.fo.extensions.ExtensionElementMapping;
import org.apache.fop.fonts.FontInfo;
+ import org.apache.fop.image.loader.batik.BatikUtil;
import org.apache.fop.pdf.PDFDocument;
import org.apache.fop.pdf.PDFPage;
-import org.apache.fop.pdf.PDFPaintingState;
import org.apache.fop.pdf.PDFResourceContext;
-import org.apache.fop.pdf.PDFStream;
import org.apache.fop.render.AbstractGenericSVGHandler;
import org.apache.fop.render.Renderer;
import org.apache.fop.render.RendererContext;
public PDFDocument pdfDoc;
/** see OUTPUT_STREAM */
public OutputStream outputStream;
-- /** see PDF_STATE */
- //public PDFState pdfState;
- public PDFPaintingState pdfPaintingState;
/** see PDF_PAGE */
public PDFPage pdfPage;
/** see PDF_CONTEXT */
graphics.scale(1 / s, 1 / s);
}
- pdfInfo.currentStream.add("%SVG start\n");
+ generator.comment("SVG start");
//Save state and update coordinate system for the SVG image
- generator.getState().push();
- pdfInfo.pdfPaintingState.save();
- pdfInfo.pdfPaintingState.concatenate(imageTransform);
++ generator.getState().save();
+ generator.getState().concatenate(imageTransform);
//Now that we have the complete transformation matrix for the image, we can update the
//transformation matrix for the AElementBridge.
PDFAElementBridge aBridge = (PDFAElementBridge)ctx.getBridge(
SVGDOMImplementation.SVG_NAMESPACE_URI, SVGConstants.SVG_A_TAG);
- aBridge.getCurrentTransform().setTransform(pdfInfo.pdfPaintingState.getTransform());
+ aBridge.getCurrentTransform().setTransform(generator.getState().getTransform());
- graphics.setPDFState(generator.getState());
- graphics.setPaintingState(pdfInfo.pdfPaintingState);
++ graphics.setPaintingState(generator.getState());
graphics.setOutputStream(pdfInfo.outputStream);
try {
root.paint(graphics);
context.getUserAgent().getEventBroadcaster());
eventProducer.svgRenderingError(this, e, getDocumentURI(doc));
}
- generator.getState().pop();
- pdfInfo.pdfPaintingState.restore();
- renderer.restoreGraphicsState();
- pdfInfo.currentStream.add("%SVG end\n");
++ generator.getState().restore();
+ generator.restoreGraphicsState();
+ generator.comment("SVG end");
}
/** {@inheritDoc} */