From: Jeremias Maerki Date: Tue, 9 Dec 2008 15:00:35 +0000 (+0000) Subject: Merge from Trunk revisions 719662 - 724689. X-Git-Tag: fop-1_0~115^2~119 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=a6ddced309701a1ca8d79a9e7bec3288afba2cc8;p=xmlgraphics-fop.git Merge from Trunk revisions 719662 - 724689. Conflict for ImageHandler interface resolved by renaming Trunk's ImageHandler to ImageHandlerBase and extending the other ImageHandler from that. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_AreaTreeNewDesign@724729 13f79535-47bb-0310-9956-ffa450edef68 --- a6ddced309701a1ca8d79a9e7bec3288afba2cc8 diff --cc build.xml index cc000e4d8,2d92b2e11..27dc3566f --- a/build.xml +++ b/build.xml @@@ -612,8 -611,8 +612,9 @@@ list of possible build targets + + diff --cc src/java/org/apache/fop/apps/MimeConstants.java index b8a9637a8,87048fa4d..851690db1 --- a/src/java/org/apache/fop/apps/MimeConstants.java +++ b/src/java/org/apache/fop/apps/MimeConstants.java @@@ -73,9 -30,5 +30,6 @@@ public interface MimeConstants extends 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"; - } diff --cc src/java/org/apache/fop/render/AbstractImageHandlerRegistry.java index 000000000,e8001f2fa..4196a1b19 mode 000000,100644..100644 --- a/src/java/org/apache/fop/render/AbstractImageHandlerRegistry.java +++ b/src/java/org/apache/fop/render/AbstractImageHandlerRegistry.java @@@ -1,0 -1,208 +1,209 @@@ + /* + * 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 h1 = (ImageHandler)o1; - ImageHandler h2 = (ImageHandler)o2; ++ ImageHandlerBase h1 = (ImageHandlerBase)o1; ++ ImageHandlerBase h2 = (ImageHandlerBase)o2; + return h1.getPriority() - h2.getPriority(); + } + }; + + /** Map containing image handlers for various MIME types */ + private final Map/**/ handlers + = new java.util.HashMap/**/(); + + /** List containing the same handlers as above but ordered by priority */ + private final 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. + */ + 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 { - ImageHandler handlerInstance - = (ImageHandler)Class.forName(classname).newInstance(); ++ 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 + */ - public synchronized void addHandler(ImageHandler handler) { ++ public synchronized void addHandler(ImageHandlerBase handler) { + this.handlers.put(handler.getSupportedImageClass(), handler); + + //Sorted insert + ListIterator iter = this.handlerList.listIterator(); + while (iter.hasNext()) { - ImageHandler h = (ImageHandler)iter.next(); ++ 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 ImageHandler getHandler(Image img) { ++ 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 + */ - public synchronized ImageHandler getHandler(Class imageClass) { - ImageHandler handler = null; ++ public synchronized ImageHandlerBase getHandler(Class imageClass) { ++ ImageHandlerBase handler = null; + Class cl = imageClass; + while (cl != null) { - handler = (ImageHandler)handlers.get(cl); ++ 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()) { - ImageFlavor[] f = ((ImageHandler)iter.next()).getSupportedImageFlavors(); ++ 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()) { - ImageHandler handler = (ImageHandler)providers.next(); ++ 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(); + } diff --cc src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java index c57a9d566,e74a8f319..2e04f2d85 --- a/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java +++ b/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java @@@ -467,11 -470,9 +470,10 @@@ public abstract class AbstractPathOrien , 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} */ diff --cc src/java/org/apache/fop/render/ImageHandler.java index 42ae5fd49,6a44c01a9..ec5f576aa --- a/src/java/org/apache/fop/render/ImageHandler.java +++ b/src/java/org/apache/fop/render/ImageHandler.java @@@ -19,60 -19,27 +19,39 @@@ 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; + } diff --cc src/java/org/apache/fop/render/ImageHandlerBase.java index 000000000,000000000..f07c89671 new file mode 100644 --- /dev/null +++ b/src/java/org/apache/fop/render/ImageHandlerBase.java @@@ -1,0 -1,0 +1,50 @@@ ++/* ++ * 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(); ++} diff --cc src/java/org/apache/fop/render/PrintRenderer.java index 818e31568,e4ec41e0b..76f727e84 --- a/src/java/org/apache/fop/render/PrintRenderer.java +++ b/src/java/org/apache/fop/render/PrintRenderer.java @@@ -24,9 -24,6 +24,7 @@@ import java.awt.geom.Rectangle2D 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; diff --cc src/java/org/apache/fop/render/afp/AFPEventProducer.xml index b0eeeb202,000000000..23bd9a182 mode 100644,000000..100644 --- a/src/java/org/apache/fop/render/afp/AFPEventProducer.xml +++ b/src/java/org/apache/fop/render/afp/AFPEventProducer.xml @@@ -1,3 -1,0 +1,1 @@@ - - No AFP fonts configured. Using default setup. - ++ diff --cc src/java/org/apache/fop/render/afp/AFPImageHandler.java index 000000000,3ec3ea0b1..a6d2d613d mode 000000,100644..100644 --- a/src/java/org/apache/fop/render/afp/AFPImageHandler.java +++ b/src/java/org/apache/fop/render/afp/AFPImageHandler.java @@@ -1,0 -1,104 +1,104 @@@ + /* + * 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; -import org.apache.fop.render.ImageHandler; ++import org.apache.fop.render.ImageHandlerBase; + + /** + * A base abstract AFP image handler + */ -public abstract class AFPImageHandler implements ImageHandler { ++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(); + } diff --cc src/java/org/apache/fop/render/pdf/PDFContentGenerator.java index 70e0f7eb5,000000000..4b0f35bec mode 100644,000000..100644 --- a/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java +++ b/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java @@@ -1,331 -1,0 +1,331 @@@ +/* + * 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; - import org.apache.fop.pdf.PDFState; +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 */ - protected PDFState currentState = null; ++ 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); + } + }; + - this.currentState = new PDFState(); ++ 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 + */ - public PDFState getState() { ++ 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.push(); ++ 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.pop(); ++ 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(); + } + + +} diff --cc src/java/org/apache/fop/render/pdf/PDFGraphics2DAdapter.java index 4da7f13cb,01d863e6a..102c1ab45 --- a/src/java/org/apache/fop/render/pdf/PDFGraphics2DAdapter.java +++ b/src/java/org/apache/fop/render/pdf/PDFGraphics2DAdapter.java @@@ -97,8 -95,8 +97,8 @@@ public class PDFGraphics2DAdapter exten 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) { diff --cc src/java/org/apache/fop/render/pdf/PDFImageHandler.java index f93ee5a97,6343d0c50..934d306b9 --- a/src/java/org/apache/fop/render/pdf/PDFImageHandler.java +++ b/src/java/org/apache/fop/render/pdf/PDFImageHandler.java @@@ -23,36 -23,15 +23,16 @@@ 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.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 diff --cc src/java/org/apache/fop/render/pdf/PDFImageHandlerGraphics2D.java index 610fa274f,3e4a9b354..18717809d --- a/src/java/org/apache/fop/render/pdf/PDFImageHandlerGraphics2D.java +++ b/src/java/org/apache/fop/render/pdf/PDFImageHandlerGraphics2D.java @@@ -67,58 -49,6 +67,58 @@@ public class PDFImageHandlerGraphics2D return null; } + /** {@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.setPDFState(generator.getState()); ++ 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; diff --cc src/java/org/apache/fop/render/pdf/PDFImageHandlerSVG.java index 3764486b7,000000000..d1b7aa986 mode 100644,000000..100644 --- a/src/java/org/apache/fop/render/pdf/PDFImageHandlerSVG.java +++ b/src/java/org/apache/fop/render/pdf/PDFImageHandlerSVG.java @@@ -1,200 -1,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; +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 - generator.getState().push(); ++ 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()); + - graphics.setPDFState(generator.getState()); ++ 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().pop(); ++ 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; + } + +} diff --cc src/java/org/apache/fop/render/pdf/PDFRenderer.java index 730acb540,e31f1eaea..e0e1bab69 --- a/src/java/org/apache/fop/render/pdf/PDFRenderer.java +++ b/src/java/org/apache/fop/render/pdf/PDFRenderer.java @@@ -73,27 -74,49 +73,28 @@@ import org.apache.fop.fonts.Typeface 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. @@@ -189,41 -240,243 +190,41 @@@ public class PDFRenderer extends Abstra /** 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); } /** @@@ -259,14 -514,13 +260,11 @@@ 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(); @@@ -504,8 -817,7 +502,7 @@@ (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} */ @@@ -515,12 -827,10 +512,7 @@@ /** {@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"); - }*/ } /** @@@ -582,10 -1069,10 +574,10 @@@ /** * {@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"); } } @@@ -607,11 -1094,12 +599,12 @@@ * @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) { @@@ -628,11 -1116,12 +621,12 @@@ * @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); @@@ -1126,12 -1654,12 +1120,13 @@@ 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()); @@@ -1167,9 -1695,10 +1162,10 @@@ // output new data try { - this.pdfDoc.output(ostream); + this.generator.flushPDFDoc(); } catch (IOException ioe) { // ioexception will be caught later + log.error(ioe.getMessage()); } } @@@ -1198,7 -1727,7 +1194,6 @@@ 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); diff --cc src/java/org/apache/fop/render/pdf/PDFRendererConfigurator.java index d416f1147,8d1042f7f..fccba1c25 --- a/src/java/org/apache/fop/render/pdf/PDFRendererConfigurator.java +++ b/src/java/org/apache/fop/render/pdf/PDFRendererConfigurator.java @@@ -71,82 -61,78 +73,83 @@@ public class PDFRendererConfigurator ex 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); } + } 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)); + } - Configuration encryptionParamsConfig = cfg.getChild(PDFRenderer.ENCRYPTION_PARAMS, false); + 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 userPasswordConfig = encryptionParamsConfig.getChild( - PDFRenderer.USER_PASSWORD, false); - if (userPasswordConfig != null) { - String userPassword = userPasswordConfig.getValue(null); - if (userPassword != null) { - encryptionParams.setUserPassword(userPassword); - } + 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 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); + } - Configuration disableColorSpaceConfig - = cfg.getChild(PDFRenderer.KEY_DISABLE_SRGB_COLORSPACE, false); + Configuration disableColorSpaceConfig = cfg.getChild( + PDFRenderer.KEY_DISABLE_SRGB_COLORSPACE, false); - if (disableColorSpaceConfig != null) { - pdfRenderer.disableSRGBColorSpace - = disableColorSpaceConfig.getValueAsBoolean(false); - } + if (disableColorSpaceConfig != null) { + pdfUtil.setDisableSRGBColorSpace( + disableColorSpaceConfig.getValueAsBoolean(false)); } } diff --cc src/java/org/apache/fop/render/pdf/PDFRendererContextConstants.java index de51aabc7,33888d442..11380bf59 --- a/src/java/org/apache/fop/render/pdf/PDFRendererContextConstants.java +++ b/src/java/org/apache/fop/render/pdf/PDFRendererContextConstants.java @@@ -29,9 -29,9 +29,6 @@@ public interface PDFRendererContextCons /** 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"; diff --cc src/java/org/apache/fop/render/pdf/PDFSVGHandler.java index 11d9b1c3f,41f48df55..8f7aad300 --- a/src/java/org/apache/fop/render/pdf/PDFSVGHandler.java +++ b/src/java/org/apache/fop/render/pdf/PDFSVGHandler.java @@@ -36,14 -36,14 +36,12 @@@ import org.apache.batik.util.SVGConstan 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; @@@ -105,8 -105,8 +103,6 @@@ public class PDFSVGHandler extends Abst 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 */ @@@ -244,19 -245,19 +242,19 @@@ 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); @@@ -266,9 -267,9 +264,9 @@@ 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} */