diff options
author | Jeremias Maerki <jeremias@apache.org> | 2008-08-05 15:31:00 +0000 |
---|---|---|
committer | Jeremias Maerki <jeremias@apache.org> | 2008-08-05 15:31:00 +0000 |
commit | 83abc0b9b18852ffed4533d18f27e1b27df081b5 (patch) | |
tree | 7659b6be4f8f6183f8625a343caed2f7606f6909 /src/java | |
parent | 46631569e9ccd7e2a179b0c6d1349fa7451ccc70 (diff) | |
download | xmlgraphics-fop-83abc0b9b18852ffed4533d18f27e1b27df081b5.tar.gz xmlgraphics-fop-83abc0b9b18852ffed4533d18f27e1b27df081b5.zip |
First steps at unified image handling as proposed on http://wiki.apache.org/xmlgraphics-fop/ImageSupport/ImageHandler. This shall serve as a preview to motivate early feedback if anyone is interested.
Basic external-graphic support now available for PDF and SVG painters (for PDF: RenderedImage, SVG, JPEG, CCITT and Java2D, for SVG: embedded SVG and RFC2397 data URLs containing PNG and JPEG images)
Change to IFPainter: added support for foreign attributes for the "image" tag.
PDFContentGenerator introduced to hold the most important objects for PDF generation (unification for renderer and painter).
Re-routed most image handling through the new image handling code (not fully done, yet, some code duplication remains).
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_AreaTreeNewDesign@682757 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/java')
29 files changed, 1448 insertions, 432 deletions
diff --git a/src/java/META-INF/services/org.apache.fop.render.ImageHandler b/src/java/META-INF/services/org.apache.fop.render.ImageHandler new file mode 100644 index 000000000..8f4354630 --- /dev/null +++ b/src/java/META-INF/services/org.apache.fop.render.ImageHandler @@ -0,0 +1,4 @@ +org.apache.fop.render.pdf.PDFImageHandlerGraphics2D
+org.apache.fop.render.pdf.PDFImageHandlerRenderedImage
+org.apache.fop.render.pdf.PDFImageHandlerRawJPEG
+org.apache.fop.render.pdf.PDFImageHandlerRawCCITTFax
diff --git a/src/java/META-INF/services/org.apache.fop.render.intermediate.IFPainter b/src/java/META-INF/services/org.apache.fop.render.intermediate.IFPainter new file mode 100644 index 000000000..9bb645ebb --- /dev/null +++ b/src/java/META-INF/services/org.apache.fop.render.intermediate.IFPainter @@ -0,0 +1 @@ +org.apache.fop.render.pdf.PDFPainterMaker
\ No newline at end of file diff --git a/src/java/org/apache/fop/render/AbstractImageHandlerGraphics2D.java b/src/java/org/apache/fop/render/AbstractImageHandlerGraphics2D.java new file mode 100644 index 000000000..a79734d49 --- /dev/null +++ b/src/java/org/apache/fop/render/AbstractImageHandlerGraphics2D.java @@ -0,0 +1,141 @@ +/* + * 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.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.RenderingHints; +import java.awt.Transparency; +import java.awt.color.ColorSpace; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.ComponentColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; + +import org.apache.fop.util.UnitConv; + +/** + * Abstract base class for ImageHandler implementations that process Java2D images through + * the Graphics2DImagePainter interface. + */ +public abstract class AbstractImageHandlerGraphics2D implements ImageHandler { + + /** + * Paints the image to a BufferedImage and returns that. + * @param painter the painter which will paint the actual image + * @param context the renderer context for the current renderer + * @param targetDimension the target dimensions of the image to be converted to a bitmap + * @param resolution the requested bitmap resolution + * @param gray true if the generated image should be in grayscales + * @param withAlpha true if an alpha channel should be created + * @return the generated BufferedImage + */ + protected BufferedImage paintToBufferedImage( + org.apache.xmlgraphics.java2d.Graphics2DImagePainter painter, + Dimension targetDimension, + int resolution, boolean gray, boolean withAlpha) { + int bmw = (int)Math.ceil(UnitConv.mpt2px(targetDimension.getWidth(), resolution)); + int bmh = (int)Math.ceil(UnitConv.mpt2px(targetDimension.getHeight(), resolution)); + BufferedImage bi; + if (gray) { + if (withAlpha) { + bi = createGrayBufferedImageWithAlpha(bmw, bmh); + } else { + bi = new BufferedImage(bmw, bmh, BufferedImage.TYPE_BYTE_GRAY); + } + } else { + if (withAlpha) { + bi = new BufferedImage(bmw, bmh, BufferedImage.TYPE_INT_ARGB); + } else { + bi = new BufferedImage(bmw, bmh, BufferedImage.TYPE_INT_RGB); + } + } + Graphics2D g2d = bi.createGraphics(); + try { + g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, + RenderingHints.VALUE_FRACTIONALMETRICS_ON); + setRenderingHintsForBufferedImage(g2d); + + g2d.setBackground(Color.white); + g2d.setColor(Color.black); + if (!withAlpha) { + g2d.clearRect(0, 0, bmw, bmh); + } + /* debug code + int off = 2; + g2d.drawLine(off, 0, off, bmh); + g2d.drawLine(bmw - off, 0, bmw - off, bmh); + g2d.drawLine(0, off, bmw, off); + g2d.drawLine(0, bmh - off, bmw, bmh - off); + */ + double sx = (double)bmw / targetDimension.getWidth(); + double sy = (double)bmh / targetDimension.getHeight(); + g2d.scale(sx, sy); + + //Paint the image on the BufferedImage + Rectangle2D area = new Rectangle2D.Double( + 0.0, 0.0, targetDimension.getWidth(), targetDimension.getHeight()); + painter.paint(g2d, area); + } finally { + g2d.dispose(); + } + return bi; + } + + private static BufferedImage createGrayBufferedImageWithAlpha(int width, int height) { + BufferedImage bi; + boolean alphaPremultiplied = true; + int bands = 2; + int[] bits = new int[bands]; + for (int i = 0; i < bands; i++) { + bits[i] = 8; + } + ColorModel cm = new ComponentColorModel( + ColorSpace.getInstance(ColorSpace.CS_GRAY), + bits, + true, alphaPremultiplied, + Transparency.TRANSLUCENT, + DataBuffer.TYPE_BYTE); + WritableRaster wr = Raster.createInterleavedRaster( + DataBuffer.TYPE_BYTE, + width, height, bands, + new Point(0, 0)); + bi = new BufferedImage(cm, wr, alphaPremultiplied, null); + return bi; + } + + /** + * Sets rendering hints on the Graphics2D created for painting to a BufferedImage. Subclasses + * can modify the settings to customize the behavior. + * @param g2d the Graphics2D instance + */ + protected void setRenderingHintsForBufferedImage(Graphics2D g2d) { + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + } + +} diff --git a/src/java/org/apache/fop/render/AbstractRenderingContext.java b/src/java/org/apache/fop/render/AbstractRenderingContext.java new file mode 100644 index 000000000..7bacac58d --- /dev/null +++ b/src/java/org/apache/fop/render/AbstractRenderingContext.java @@ -0,0 +1,49 @@ +/* + * 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.fop.apps.FOUserAgent; + +/** + * Abstract base class for RenderingContext implementations. + */ +public abstract class AbstractRenderingContext implements RenderingContext { + + private FOUserAgent userAgent; + + /** + * Main constructor. + * @param userAgent the user agent + */ + public AbstractRenderingContext(FOUserAgent userAgent) { + this.userAgent = userAgent; + } + + /** + * Returns the user agent. + * + * @return The user agent + */ + public FOUserAgent getUserAgent() { + return userAgent; + } + +} + diff --git a/src/java/org/apache/fop/render/ImageAdapter.java b/src/java/org/apache/fop/render/ImageAdapter.java index a67d43bdc..757be43b1 100644 --- a/src/java/org/apache/fop/render/ImageAdapter.java +++ b/src/java/org/apache/fop/render/ImageAdapter.java @@ -34,10 +34,10 @@ public interface ImageAdapter { * Paints an image at the given position. * @param image the image which will be painted * @param context the renderer context for the current renderer - * @param x X position of the image - * @param y Y position of the image - * @param width width of the image - * @param height height of the image + * @param x X position of the image (in millipoints) + * @param y Y position of the image (in millipoints) + * @param width width of the image (in millipoints) + * @param height height of the image (in millipoints) * @throws IOException In case of an I/O error while writing the output format */ void paintImage(RenderedImage image, diff --git a/src/java/org/apache/fop/render/ImageHandler.java b/src/java/org/apache/fop/render/ImageHandler.java new file mode 100644 index 000000000..42ae5fd49 --- /dev/null +++ b/src/java/org/apache/fop/render/ImageHandler.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render; + +import java.awt.Rectangle; +import java.io.IOException; + +import org.apache.xmlgraphics.image.loader.Image; +import org.apache.xmlgraphics.image.loader.ImageFlavor; + +/** + * This interface is used for handling all sorts of image types for PDF output. + */ +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(); + + /** + * 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 + */ + boolean isCompatible(RenderingContext targetContext, Image image); + + /** + * Returns the {@link Image} subclass supported by this instance. + * @return the Image type + */ + Class getSupportedImageClass(); + + /** + * 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 + */ + void handleImage(RenderingContext context, Image image, + Rectangle pos) throws IOException; + +} diff --git a/src/java/org/apache/fop/render/ImageHandlerRegistry.java b/src/java/org/apache/fop/render/ImageHandlerRegistry.java new file mode 100644 index 000000000..3a241138a --- /dev/null +++ b/src/java/org/apache/fop/render/ImageHandlerRegistry.java @@ -0,0 +1,177 @@ +/* + * 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. It also + * supports automatic discovery of additional handlers available through + * the class path. + */ +public class ImageHandlerRegistry { + + /** the logger */ + private static Log log = LogFactory.getLog(ImageHandlerRegistry.class); + + private static final Comparator HANDLER_COMPARATOR = new Comparator() { + public int compare(Object o1, Object o2) { + ImageHandler h1 = (ImageHandler)o1; + ImageHandler h2 = (ImageHandler)o2; + return h1.getPriority() - h2.getPriority(); + } + }; + + /** Map containing image handlers for various {@code Image} subclasses. */ + private Map handlers = new java.util.HashMap(); + /** List containing the same handlers as above but ordered by priority */ + private List handlerList = new java.util.LinkedList(); + + private int handlerRegistrations; + + /** + * Default constructor. + */ + public ImageHandlerRegistry() { + discoverHandlers(); + } + + /** + * Add an PDFImageHandler. 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(); + 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 " + + ImageHandler.class.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) { + Class imageClass = handler.getSupportedImageClass(); + //List + this.handlers.put(imageClass, handler); + + //Sorted insert (sort by priority) + ListIterator iter = this.handlerList.listIterator(); + while (iter.hasNext()) { + ImageHandler h = (ImageHandler)iter.next(); + if (HANDLER_COMPARATOR.compare(handler, h) < 0) { + iter.previous(); + break; + } + } + iter.add(handler); + this.handlerRegistrations++; + } + + /** + * Returns an {@code ImageHandler} which handles an specific image type given the MIME type + * of the image. + * @param targetContext the target rendering context that is used for identifying compatibility + * @param image the Image to be handled + * @return the image handler responsible for handling the image or null if none is available + */ + public ImageHandler getHandler(RenderingContext targetContext, Image image) { + ListIterator iter = this.handlerList.listIterator(); + while (iter.hasNext()) { + ImageHandler h = (ImageHandler)iter.next(); + if (h.isCompatible(targetContext, image)) { + //Return the first handler in the prioritized list that is compatible + return h; + } + } + return null; + } + + /** + * Returns the ordered array of supported image flavors. The array needs to be ordered by + * priority so the image loader framework can return the preferred image type. + * @return the array of image flavors + */ + public synchronized ImageFlavor[] getSupportedFlavors(RenderingContext context) { + //Extract all ImageFlavors into a single array + List flavors = new java.util.ArrayList(); + Iterator iter = this.handlerList.iterator(); + while (iter.hasNext()) { + ImageHandler handler = (ImageHandler)iter.next(); + if (handler.isCompatible(context, null)) { + ImageFlavor[] f = handler.getSupportedImageFlavors(); + for (int i = 0; i < f.length; i++) { + flavors.add(f[i]); + } + } + } + return (ImageFlavor[])flavors.toArray(new ImageFlavor[flavors.size()]); + } + + /** + * Discovers ImageHandler implementations through the classpath and dynamically + * registers them. + */ + private void discoverHandlers() { + // add mappings from available services + Iterator providers = Service.providers(ImageHandler.class); + if (providers != null) { + while (providers.hasNext()) { + ImageHandler handler = (ImageHandler)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); + } + + } + } + } +} diff --git a/src/java/org/apache/fop/render/RendererContext.java b/src/java/org/apache/fop/render/RendererContext.java index 08ca76957..e52588176 100644 --- a/src/java/org/apache/fop/render/RendererContext.java +++ b/src/java/org/apache/fop/render/RendererContext.java @@ -22,7 +22,6 @@ package org.apache.fop.render; //Java import java.util.Map; -//FOP import org.apache.fop.apps.FOUserAgent; /** @@ -30,7 +29,7 @@ import org.apache.fop.apps.FOUserAgent; * so that external handlers can get information to be able to render to the * render target. */ -public class RendererContext { +public class RendererContext implements RenderingContext { private String mime; private AbstractRenderer renderer; diff --git a/src/java/org/apache/fop/render/RenderingContext.java b/src/java/org/apache/fop/render/RenderingContext.java new file mode 100644 index 000000000..a6f958328 --- /dev/null +++ b/src/java/org/apache/fop/render/RenderingContext.java @@ -0,0 +1,43 @@ +/* + * 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.fop.apps.FOUserAgent; + +/** + * Implementations of this interface provide context information needed by supporting classes + * during specific tasks (like image rendering). + */ +public interface RenderingContext { + + /** + * Returns the MIME type associated with the current output format. + * @return the MIME type (ex. application/pdf) + */ + String getMimeType(); + + /** + * Returns the user agent. The user agent is used to access configuration and other information + * for the rendering process. + * @return the user agent + */ + FOUserAgent getUserAgent(); + +} diff --git a/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java b/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java index 54d5b33b4..f5e43229b 100644 --- a/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java +++ b/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java @@ -24,6 +24,7 @@ import java.awt.Rectangle; import java.awt.geom.AffineTransform; import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.render.ImageHandlerRegistry; /** * Abstract base class for IFPainter implementations. @@ -32,6 +33,9 @@ public abstract class AbstractIFPainter implements IFPainter { private FOUserAgent userAgent; + /** Image handler registry */ + protected ImageHandlerRegistry imageHandlerRegistry = new ImageHandlerRegistry(); + /** * Default constructor. */ @@ -40,6 +44,9 @@ public abstract class AbstractIFPainter implements IFPainter { /** {@inheritDoc} */ public void setUserAgent(FOUserAgent ua) { + if (this.userAgent != null) { + throw new IllegalStateException("The user agent was already set"); + } this.userAgent = ua; } diff --git a/src/java/org/apache/fop/render/intermediate/AbstractXMLWritingIFPainter.java b/src/java/org/apache/fop/render/intermediate/AbstractXMLWritingIFPainter.java index 9e0f97699..63238354b 100644 --- a/src/java/org/apache/fop/render/intermediate/AbstractXMLWritingIFPainter.java +++ b/src/java/org/apache/fop/render/intermediate/AbstractXMLWritingIFPainter.java @@ -35,6 +35,8 @@ import org.xml.sax.ContentHandler; import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; +import org.apache.xmlgraphics.util.QName; + import org.apache.fop.fonts.FontInfo; import org.apache.fop.util.DecimalFormatCache; @@ -219,6 +221,17 @@ public abstract class AbstractXMLWritingIFPainter extends AbstractIFPainter { } /** + * Adds an attribute to a given {@code AttributesImpl} instance. + * @param atts the attributes collection + * @param attribute the attribute to add + * @param value the attribute's CDATA value + */ + protected void addAttribute(AttributesImpl atts, QName attribute, String value) { + atts.addAttribute(attribute.getNamespaceURI(), + attribute.getLocalName(), attribute.getQName(), CDATA, value); + } + + /** * Converts an array of integer coordinates into a space-separated string. * @param coordinates the coordinates * @return the space-separated array of coordinates diff --git a/src/java/org/apache/fop/render/intermediate/IFConstants.java b/src/java/org/apache/fop/render/intermediate/IFConstants.java index 18de496e1..b90e77d07 100644 --- a/src/java/org/apache/fop/render/intermediate/IFConstants.java +++ b/src/java/org/apache/fop/render/intermediate/IFConstants.java @@ -19,6 +19,8 @@ package org.apache.fop.render.intermediate; +import org.apache.xmlgraphics.util.QName; + import org.apache.fop.apps.MimeConstants; /** @@ -39,6 +41,8 @@ public interface IFConstants { String XLINK_PREFIX = "xlink"; /** XML namespace for XLink */ String XLINK_NAMESPACE = "http://www.w3.org/1999/xlink"; + /** xlink:href attribute */ + QName XLINK_HREF = new QName(XLINK_NAMESPACE, XLINK_PREFIX, "href"); String EL_DOCUMENT = "document"; String EL_HEADER = "header"; @@ -49,4 +53,5 @@ public interface IFConstants { String EL_PAGE_CONTENT = "content"; String EL_VIEWPORT = "viewport"; String EL_GROUP = "g"; + String EL_IMAGE = "image"; } diff --git a/src/java/org/apache/fop/render/intermediate/IFPainter.java b/src/java/org/apache/fop/render/intermediate/IFPainter.java index 269c4b0ad..e257caec4 100644 --- a/src/java/org/apache/fop/render/intermediate/IFPainter.java +++ b/src/java/org/apache/fop/render/intermediate/IFPainter.java @@ -24,6 +24,7 @@ import java.awt.Dimension; import java.awt.Paint; import java.awt.Rectangle; import java.awt.geom.AffineTransform; +import java.util.Map; import javax.xml.transform.Result; @@ -257,7 +258,7 @@ public interface IFPainter { * @throws IFException if an error occurs while handling this event */ void drawRect(Rectangle rect, Paint fill, Color stroke) throws IFException; - void drawImage(String uri, Rectangle rect) throws IFException; //external images + void drawImage(String uri, Rectangle rect, Map foreignAttributes) throws IFException; //external images void startImage(Rectangle rect) throws IFException; //followed by a SAX stream (SVG etc.) void endImage() throws IFException; //etc. etc. diff --git a/src/java/org/apache/fop/render/intermediate/IFParser.java b/src/java/org/apache/fop/render/intermediate/IFParser.java index 09358da40..566b91a9a 100644 --- a/src/java/org/apache/fop/render/intermediate/IFParser.java +++ b/src/java/org/apache/fop/render/intermediate/IFParser.java @@ -135,6 +135,7 @@ public class IFParser implements IFConstants { elementHandlers.put("font", new FontHandler()); elementHandlers.put("text", new TextHandler()); elementHandlers.put("rect", new RectHandler()); + elementHandlers.put(EL_IMAGE, new ImageHandler()); } @@ -459,6 +460,24 @@ public class IFParser implements IFConstants { } + private class ImageHandler extends AbstractElementHandler { + + public void endElement() throws IFException { + int x = Integer.parseInt(lastAttributes.getValue("x")); + int y = Integer.parseInt(lastAttributes.getValue("y")); + int width = Integer.parseInt(lastAttributes.getValue("width")); + int height = Integer.parseInt(lastAttributes.getValue("height")); + String uri = lastAttributes.getValue( + XLINK_HREF.getNamespaceURI(), XLINK_HREF.getLocalName()); + Map foreignAttributes = null; //TODO Implement me! + painter.drawImage(uri, new Rectangle(x, y, width, height), foreignAttributes); + } + + public boolean ignoreCharacters() { + return false; + } + } + // ==================================================================== diff --git a/src/java/org/apache/fop/render/intermediate/IFRenderer.java b/src/java/org/apache/fop/render/intermediate/IFRenderer.java index a8e0208f1..e870a4263 100644 --- a/src/java/org/apache/fop/render/intermediate/IFRenderer.java +++ b/src/java/org/apache/fop/render/intermediate/IFRenderer.java @@ -62,6 +62,7 @@ import org.apache.fop.area.inline.SpaceArea; import org.apache.fop.area.inline.TextArea; import org.apache.fop.area.inline.Viewport; import org.apache.fop.area.inline.WordArea; +import org.apache.fop.datatypes.URISpecification; import org.apache.fop.fo.extensions.ExtensionAttachment; import org.apache.fop.fo.extensions.xmp.XMPMetadata; import org.apache.fop.fonts.Font; @@ -168,8 +169,8 @@ public class IFRenderer extends AbstractPathOrientedRenderer { } if (this.painter == null) { this.painter = new IFSerializer(); + this.painter.setUserAgent(getUserAgent()); } - this.painter.setUserAgent(getUserAgent()); this.painter.setFontInfo(fontInfo); this.painter.setResult(result); } @@ -695,15 +696,22 @@ public class IFRenderer extends AbstractPathOrientedRenderer { /** {@inheritDoc} */ public void renderImage(Image image, Rectangle2D pos) { - if (log.isDebugEnabled()) { - log.debug("renderImage() image=" + image + ", pos=" + pos); - } - super.renderImage(image, pos); + drawImage(image.getURL(), pos, image.getForeignAttributes()); } - protected void drawImage(String url, Rectangle2D pos, Map foreignAttributes) { - // TODO Auto-generated method stub - log.warn("drawImage() NYI"); + /** {@inheritDoc} */ + protected void drawImage(String uri, Rectangle2D pos, Map foreignAttributes) { + Rectangle posInt = new Rectangle( + currentIPPosition + (int)pos.getX(), + currentBPPosition + (int)pos.getY(), + (int)pos.getWidth(), + (int)pos.getHeight()); + uri = URISpecification.getURL(uri); + try { + painter.drawImage(uri, posInt, foreignAttributes); + } catch (IFException ife) { + handleIFException(ife); + } } protected void clip() { diff --git a/src/java/org/apache/fop/render/intermediate/IFSerializer.java b/src/java/org/apache/fop/render/intermediate/IFSerializer.java index 601732b39..0f3d20f0b 100644 --- a/src/java/org/apache/fop/render/intermediate/IFSerializer.java +++ b/src/java/org/apache/fop/render/intermediate/IFSerializer.java @@ -24,10 +24,13 @@ import java.awt.Dimension; import java.awt.Paint; import java.awt.Rectangle; import java.awt.geom.AffineTransform; +import java.util.Iterator; +import java.util.Map; import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; +import org.apache.xmlgraphics.util.QName; import org.apache.xmlgraphics.util.XMLizable; import org.apache.fop.util.ColorUtil; @@ -275,9 +278,25 @@ public class IFSerializer extends AbstractXMLWritingIFPainter implements IFConst } /** {@inheritDoc} */ - public void drawImage(String uri, Rectangle rect) throws IFException { - // TODO Auto-generated method stub - + public void drawImage(String uri, Rectangle rect, Map foreignAttributes) throws IFException { + try { + AttributesImpl atts = new AttributesImpl(); + addAttribute(atts, XLINK_HREF, uri); + atts.addAttribute("", "x", "x", CDATA, Integer.toString(rect.x)); + atts.addAttribute("", "y", "y", CDATA, Integer.toString(rect.y)); + atts.addAttribute("", "width", "width", CDATA, Integer.toString(rect.width)); + atts.addAttribute("", "height", "height", CDATA, Integer.toString(rect.height)); + if (foreignAttributes != null) { + Iterator iter = foreignAttributes.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = (Map.Entry)iter.next(); + addAttribute(atts, (QName)entry.getKey(), entry.getValue().toString()); + } + } + element(EL_IMAGE, atts); + } catch (SAXException e) { + throw new IFException("SAX error in startGroup()", e); + } } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java b/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java new file mode 100644 index 000000000..ba2a20707 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java @@ -0,0 +1,327 @@ +/* + * 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.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.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; + /** 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(); + } + + /** + * 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() { + 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(); + 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(); + } + } + + /** {@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) { + concatenate(transform, false); + } + + /** + * Concatenates the given transformation matrix with the current one. + * @param transform the transformation matrix + * @param convertMillipoints true if the coordinates are in millipoints and need to be + * converted to points + */ + public void concatenate(AffineTransform transform, boolean convertMillipoints) { + if (!transform.isIdentity()) { + currentState.concatenate(transform); + currentStream.add(CTMHelper.toPDFString(transform, convertMillipoints) + " cm\n"); + } + } + + /** + * 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 + */ + protected static 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 --git a/src/java/org/apache/fop/render/pdf/PDFGraphics2DAdapter.java b/src/java/org/apache/fop/render/pdf/PDFGraphics2DAdapter.java index b61ebc346..f69937888 100644 --- a/src/java/org/apache/fop/render/pdf/PDFGraphics2DAdapter.java +++ b/src/java/org/apache/fop/render/pdf/PDFGraphics2DAdapter.java @@ -55,6 +55,7 @@ public class PDFGraphics2DAdapter extends AbstractGraphics2DAdapter { RendererContext context, int x, int y, int width, int height) throws IOException { + PDFContentGenerator generator = renderer.getGenerator(); PDFSVGHandler.PDFInfo pdfInfo = PDFSVGHandler.getPDFInfo(context); float fwidth = width / 1000f; float fheight = height / 1000f; @@ -69,16 +70,17 @@ public class PDFGraphics2DAdapter extends AbstractGraphics2DAdapter { float sx = fwidth / (float)imw; float sy = fheight / (float)imh; - renderer.saveGraphicsState(); - renderer.setColor(Color.black, false, null); - renderer.setColor(Color.black, true, null); + 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. - renderer.currentStream.add(sx + " 0 0 " + sy + " " + fx + " " + generator.add(sx + " 0 0 " + sy + " " + fx + " " + fy + " cm\n"); @@ -95,8 +97,8 @@ public class PDFGraphics2DAdapter extends AbstractGraphics2DAdapter { AffineTransform transform = new AffineTransform(); transform.translate(fx, fy); - pdfInfo.pdfState.concatenate(transform); - graphics.setPDFState(pdfInfo.pdfState); + generator.getState().concatenate(transform); + graphics.setPDFState(generator.getState()); graphics.setOutputStream(pdfInfo.outputStream); if (pdfInfo.paintAsBitmap) { @@ -113,9 +115,9 @@ public class PDFGraphics2DAdapter extends AbstractGraphics2DAdapter { painter.paint(graphics, area); } - pdfInfo.currentStream.add(graphics.getString()); - renderer.restoreGraphicsState(); - pdfInfo.pdfState.pop(); + generator.add(graphics.getString()); + generator.restoreGraphicsState(); + generator.comment("G2D end"); } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/render/pdf/PDFImageHandlerGraphics2D.java b/src/java/org/apache/fop/render/pdf/PDFImageHandlerGraphics2D.java index a58fe5922..610fa274f 100644 --- a/src/java/org/apache/fop/render/pdf/PDFImageHandlerGraphics2D.java +++ b/src/java/org/apache/fop/render/pdf/PDFImageHandlerGraphics2D.java @@ -19,8 +19,12 @@ package org.apache.fop.render.pdf; +import java.awt.Color; +import java.awt.Dimension; import java.awt.Point; import java.awt.Rectangle; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; import java.io.IOException; import org.apache.xmlgraphics.image.loader.Image; @@ -28,12 +32,16 @@ import org.apache.xmlgraphics.image.loader.ImageFlavor; import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D; import org.apache.fop.pdf.PDFXObject; +import org.apache.fop.render.AbstractImageHandlerGraphics2D; import org.apache.fop.render.RendererContext; +import org.apache.fop.render.RenderingContext; +import org.apache.fop.svg.PDFGraphics2D; /** * PDFImageHandler implementation which handles Graphics2D images. */ -public class PDFImageHandlerGraphics2D implements PDFImageHandler { +public class PDFImageHandlerGraphics2D extends AbstractImageHandlerGraphics2D + implements PDFImageHandler { private static final ImageFlavor[] FLAVORS = new ImageFlavor[] { ImageFlavor.GRAPHICS2D, @@ -44,13 +52,74 @@ public class PDFImageHandlerGraphics2D implements PDFImageHandler { Point origin, Rectangle pos) throws IOException { PDFRenderer renderer = (PDFRenderer)context.getRenderer(); + /* ImageGraphics2D imageG2D = (ImageGraphics2D)image; renderer.getGraphics2DAdapter().paintImage(imageG2D.getGraphics2DImagePainter(), context, origin.x + pos.x, origin.y + pos.y, pos.width, pos.height); + */ + PDFRenderingContext pdfContext = new PDFRenderingContext( + context.getUserAgent(), + renderer.getGenerator(), + renderer.currentPage, + renderer.getFontInfo()); + Rectangle effPos = new Rectangle(origin.x + pos.x, origin.y + pos.y, pos.width, pos.height); + handleImage(pdfContext, image, effPos); 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.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; } @@ -65,4 +134,10 @@ public class PDFImageHandlerGraphics2D implements PDFImageHandler { return FLAVORS; } + /** {@inheritDoc} */ + public boolean isCompatible(RenderingContext targetContext, Image image) { + return (image == null || image instanceof ImageGraphics2D) + && targetContext instanceof PDFRenderingContext; + } + } diff --git a/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawCCITTFax.java b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawCCITTFax.java index 9f56ebfea..75b1d356e 100644 --- a/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawCCITTFax.java +++ b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawCCITTFax.java @@ -31,12 +31,15 @@ import org.apache.fop.pdf.PDFDocument; import org.apache.fop.pdf.PDFImage; import org.apache.fop.pdf.PDFResourceContext; import org.apache.fop.pdf.PDFXObject; +import org.apache.fop.render.ImageHandler; import org.apache.fop.render.RendererContext; +import org.apache.fop.render.RenderingContext; /** - * PDFImageHandler implementation which handles CCITT encoded images (CCITT fax group 3/4). + * Image handler implementation which handles CCITT encoded images (CCITT fax group 3/4) + * for PDF output. */ -public class PDFImageHandlerRawCCITTFax implements PDFImageHandler { +public class PDFImageHandlerRawCCITTFax implements PDFImageHandler, ImageHandler { private static final ImageFlavor[] FLAVORS = new ImageFlavor[] { ImageFlavor.RAW_CCITTFAX, @@ -66,6 +69,24 @@ public class PDFImageHandlerRawCCITTFax implements PDFImageHandler { } /** {@inheritDoc} */ + public void handleImage(RenderingContext context, Image image, Rectangle pos) + throws IOException { + PDFRenderingContext pdfContext = (PDFRenderingContext)context; + PDFContentGenerator generator = pdfContext.getGenerator(); + ImageRawCCITTFax ccitt = (ImageRawCCITTFax)image; + + PDFImage pdfimage = new ImageRawCCITTFaxAdapter(ccitt, image.getInfo().getOriginalURI()); + PDFXObject xobj = generator.getDocument().addImage( + generator.getResourceContext(), pdfimage); + + float x = (float)pos.getX() / 1000f; + float y = (float)pos.getY() / 1000f; + float w = (float)pos.getWidth() / 1000f; + float h = (float)pos.getHeight() / 1000f; + generator.placeImage(x, y, w, h, xobj); + } + + /** {@inheritDoc} */ public int getPriority() { return 100; } @@ -80,4 +101,10 @@ public class PDFImageHandlerRawCCITTFax implements PDFImageHandler { return FLAVORS; } + /** {@inheritDoc} */ + public boolean isCompatible(RenderingContext targetContext, Image image) { + return (image == null || image instanceof ImageRawCCITTFax) + && targetContext instanceof PDFRenderingContext; + } + } diff --git a/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawJPEG.java b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawJPEG.java index f971a49ae..d47d5a439 100644 --- a/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawJPEG.java +++ b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawJPEG.java @@ -31,12 +31,14 @@ import org.apache.fop.pdf.PDFDocument; import org.apache.fop.pdf.PDFImage; import org.apache.fop.pdf.PDFResourceContext; import org.apache.fop.pdf.PDFXObject; +import org.apache.fop.render.ImageHandler; import org.apache.fop.render.RendererContext; +import org.apache.fop.render.RenderingContext; /** - * PDFImageHandler implementation which handles raw JPEG images. + * Image handler implementation which handles raw JPEG images for PDF output. */ -public class PDFImageHandlerRawJPEG implements PDFImageHandler { +public class PDFImageHandlerRawJPEG implements PDFImageHandler, ImageHandler { private static final ImageFlavor[] FLAVORS = new ImageFlavor[] { ImageFlavor.RAW_JPEG, @@ -66,6 +68,24 @@ public class PDFImageHandlerRawJPEG implements PDFImageHandler { } /** {@inheritDoc} */ + public void handleImage(RenderingContext context, Image image, Rectangle pos) + throws IOException { + PDFRenderingContext pdfContext = (PDFRenderingContext)context; + PDFContentGenerator generator = pdfContext.getGenerator(); + ImageRawJPEG imageJPEG = (ImageRawJPEG)image; + + PDFImage pdfimage = new ImageRawJPEGAdapter(imageJPEG, image.getInfo().getOriginalURI()); + PDFXObject xobj = generator.getDocument().addImage( + generator.getResourceContext(), pdfimage); + + float x = (float)pos.getX() / 1000f; + float y = (float)pos.getY() / 1000f; + float w = (float)pos.getWidth() / 1000f; + float h = (float)pos.getHeight() / 1000f; + generator.placeImage(x, y, w, h, xobj); + } + + /** {@inheritDoc} */ public int getPriority() { return 100; } @@ -80,4 +100,10 @@ public class PDFImageHandlerRawJPEG implements PDFImageHandler { return FLAVORS; } + /** {@inheritDoc} */ + public boolean isCompatible(RenderingContext targetContext, Image image) { + return (image == null || image instanceof ImageRawJPEG) + && targetContext instanceof PDFRenderingContext; + } + } diff --git a/src/java/org/apache/fop/render/pdf/PDFImageHandlerRenderedImage.java b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRenderedImage.java index 783cb225c..3e57c7216 100644 --- a/src/java/org/apache/fop/render/pdf/PDFImageHandlerRenderedImage.java +++ b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRenderedImage.java @@ -31,12 +31,14 @@ import org.apache.fop.pdf.PDFDocument; import org.apache.fop.pdf.PDFImage; import org.apache.fop.pdf.PDFResourceContext; import org.apache.fop.pdf.PDFXObject; +import org.apache.fop.render.ImageHandler; import org.apache.fop.render.RendererContext; +import org.apache.fop.render.RenderingContext; /** - * PDFImageHandler implementation which handles RenderedImage instances. + * Image handler implementation which handles RenderedImage instances for PDF output. */ -public class PDFImageHandlerRenderedImage implements PDFImageHandler { +public class PDFImageHandlerRenderedImage implements PDFImageHandler, ImageHandler { private static final ImageFlavor[] FLAVORS = new ImageFlavor[] { ImageFlavor.BUFFERED_IMAGE, @@ -67,6 +69,24 @@ public class PDFImageHandlerRenderedImage implements PDFImageHandler { } /** {@inheritDoc} */ + public void handleImage(RenderingContext context, Image image, Rectangle pos) + throws IOException { + PDFRenderingContext pdfContext = (PDFRenderingContext)context; + PDFContentGenerator generator = pdfContext.getGenerator(); + ImageRendered imageRend = (ImageRendered)image; + + PDFImage pdfimage = new ImageRenderedAdapter(imageRend, image.getInfo().getOriginalURI()); + PDFXObject xobj = generator.getDocument().addImage( + generator.getResourceContext(), pdfimage); + + float x = (float)pos.getX() / 1000f; + float y = (float)pos.getY() / 1000f; + float w = (float)pos.getWidth() / 1000f; + float h = (float)pos.getHeight() / 1000f; + generator.placeImage(x, y, w, h, xobj); + } + + /** {@inheritDoc} */ public int getPriority() { return 300; } @@ -81,4 +101,10 @@ public class PDFImageHandlerRenderedImage implements PDFImageHandler { return FLAVORS; } + /** {@inheritDoc} */ + public boolean isCompatible(RenderingContext targetContext, Image image) { + return (image == null || image instanceof ImageRendered) + && targetContext instanceof PDFRenderingContext; + } + } diff --git a/src/java/org/apache/fop/render/pdf/PDFPainter.java b/src/java/org/apache/fop/render/pdf/PDFPainter.java index e75417d33..5b763b197 100644 --- a/src/java/org/apache/fop/render/pdf/PDFPainter.java +++ b/src/java/org/apache/fop/render/pdf/PDFPainter.java @@ -24,16 +24,23 @@ import java.awt.Dimension; import java.awt.Paint; import java.awt.Rectangle; import java.awt.geom.AffineTransform; +import java.io.FileNotFoundException; import java.io.IOException; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.xmlgraphics.image.loader.ImageException; +import org.apache.xmlgraphics.image.loader.ImageInfo; +import org.apache.xmlgraphics.image.loader.ImageManager; +import org.apache.xmlgraphics.image.loader.ImageSessionContext; +import org.apache.xmlgraphics.image.loader.util.ImageUtil; import org.apache.xmlgraphics.xmp.Metadata; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.apps.MimeConstants; +import org.apache.fop.events.ResourceEventProducer; import org.apache.fop.fo.extensions.xmp.XMPMetadata; import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontTriplet; @@ -42,19 +49,17 @@ import org.apache.fop.fonts.SingleByteFont; import org.apache.fop.fonts.Typeface; import org.apache.fop.pdf.PDFAnnotList; import org.apache.fop.pdf.PDFDocument; -import org.apache.fop.pdf.PDFFilterList; import org.apache.fop.pdf.PDFNumber; import org.apache.fop.pdf.PDFPage; 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.PDFXObject; +import org.apache.fop.render.ImageHandler; import org.apache.fop.render.intermediate.AbstractBinaryWritingIFPainter; import org.apache.fop.render.intermediate.IFException; import org.apache.fop.render.intermediate.IFState; import org.apache.fop.util.CharUtilities; -import org.apache.fop.util.ColorUtil; /** * IFPainter implementation that produces PDF. @@ -79,8 +84,8 @@ public class PDFPainter extends AbstractBinaryWritingIFPainter { /** the /Resources object of the PDF document being created */ protected PDFResources pdfResources; - /** the current stream to add PDF commands to */ - protected PDFStream currentStream; + /** The current content generator */ + protected PDFContentGenerator generator; /** the current annotation list to add annotations to */ protected PDFResourceContext currentContext; @@ -98,16 +103,6 @@ public class PDFPainter extends AbstractBinaryWritingIFPainter { /** the current page's PDF reference string (to avoid numerous function calls) */ protected String currentPageRef; - /** drawing state */ - protected PDFState currentState; - - /** Text generation utility holding the current font status */ - protected PDFTextUtil textutil; - - - /** Image handler registry */ - private PDFImageHandlerRegistry imageHandlerRegistry = new PDFImageHandlerRegistry(); - /** * Default constructor. */ @@ -173,11 +168,9 @@ public class PDFPainter extends AbstractBinaryWritingIFPainter { //pageReferences.clear(); pdfResources = null; - currentStream = null; + this.generator = null; currentContext = null; currentPage = null; - currentState = null; - this.textutil = null; //idPositions.clear(); //idGoTos.clear(); @@ -213,20 +206,11 @@ public class PDFPainter extends AbstractBinaryWritingIFPainter { currentPageRef = currentPage.referencePDF(); - currentStream = this.pdfDoc.getFactory() - .makeStream(PDFFilterList.CONTENT_FILTER, false); - this.textutil = new PDFTextUtil() { - protected void write(String code) { - currentStream.add(code); - } - }; - - currentState = new PDFState(); + this.generator = new PDFContentGenerator(this.pdfDoc, this.outputStream, this.currentPage); // Transform the PDF's default coordinate system (0,0 at lower left) to the PDFPainter's AffineTransform basicPageTransform = new AffineTransform(1, 0, 0, -1, 0, size.height); - currentState.concatenate(basicPageTransform); - currentStream.add(CTMHelper.toPDFString(basicPageTransform, true) + " cm\n"); + generator.concatenate(basicPageTransform, true); } /** {@inheritDoc} */ @@ -258,80 +242,136 @@ public class PDFPainter extends AbstractBinaryWritingIFPainter { /** {@inheritDoc} */ public void endPage() throws IFException { try { - this.pdfDoc.registerObject(currentStream); - currentPage.setContents(currentStream); + this.pdfDoc.registerObject(generator.getStream()); + currentPage.setContents(generator.getStream()); PDFAnnotList annots = currentPage.getAnnotations(); if (annots != null) { this.pdfDoc.addObject(annots); } this.pdfDoc.addObject(currentPage); - this.pdfDoc.output(this.outputStream); - this.textutil = null; + this.generator.flushPDFDoc(); + this.generator = null; } catch (IOException ioe) { throw new IFException("I/O error in endPage()", ioe); } } /** {@inheritDoc} */ - private void saveGraphicsState() { - //endTextObject(); - currentState.push(); - this.state = this.state.push(); - currentStream.add("q\n"); - } - - private void restoreGraphicsState(boolean popState) { - endTextObject(); - currentStream.add("Q\n"); - if (popState) { - currentState.pop(); - this.state = this.state.pop(); - } - } - - private void restoreGraphicsState() { - restoreGraphicsState(true); - } - - /** {@inheritDoc} */ public void startViewport(AffineTransform transform, Dimension size, Rectangle clipRect) throws IFException { - saveGraphicsState(); - currentStream.add(CTMHelper.toPDFString(transform, true) + " cm\n"); + generator.saveGraphicsState(); + generator.add(CTMHelper.toPDFString(transform, true) + " cm\n"); if (clipRect != null) { StringBuffer sb = new StringBuffer(); sb.append(format(clipRect.x)).append(' '); sb.append(format(clipRect.y)).append(' '); sb.append(format(clipRect.width)).append(' '); sb.append(format(clipRect.height)).append(" re W n\n"); - currentStream.add(sb.toString()); + generator.add(sb.toString()); } } /** {@inheritDoc} */ - public void startGroup(AffineTransform transform) throws IFException { - saveGraphicsState(); - currentStream.add(CTMHelper.toPDFString(transform, true) + " cm\n"); + public void endViewport() throws IFException { + generator.restoreGraphicsState(); } /** {@inheritDoc} */ - public void endGroup() throws IFException { - restoreGraphicsState(); + public void startGroup(AffineTransform transform) throws IFException { + generator.saveGraphicsState(); + generator.add(CTMHelper.toPDFString(transform, true) + " cm\n"); } /** {@inheritDoc} */ - public void endViewport() throws IFException { - restoreGraphicsState(); + public void endGroup() throws IFException { + generator.restoreGraphicsState(); } /** {@inheritDoc} */ - public void startImage(Rectangle rect) throws IFException { - // TODO Auto-generated method stub + public void drawImage(String uri, Rectangle rect, Map foreignAttributes) throws IFException { + PDFXObject xobject = pdfDoc.getXObject(uri); + if (xobject != null) { + placeImage(rect, xobject); + return; + } + + ImageManager manager = getUserAgent().getFactory().getImageManager(); + ImageInfo info = null; + try { + ImageSessionContext sessionContext = getUserAgent().getImageSessionContext(); + info = manager.getImageInfo(uri, sessionContext); + + PDFRenderingContext pdfContext = new PDFRenderingContext( + getUserAgent(), generator, currentPage, getFontInfo()); + + Map hints = ImageUtil.getDefaultHints(sessionContext); + org.apache.xmlgraphics.image.loader.Image img = manager.getImage( + info, imageHandlerRegistry.getSupportedFlavors(pdfContext), + hints, sessionContext); + + //First check for a dynamically registered handler + ImageHandler handler = imageHandlerRegistry.getHandler(pdfContext, img); + if (handler == null) { + throw new UnsupportedOperationException( + "No ImageHandler available for image: " + + info + " (" + img.getClass().getName() + ")"); + } + + if (log.isDebugEnabled()) { + log.debug("Using ImageHandler: " + handler.getClass().getName()); + } + try { + //TODO foreign attributes + handler.handleImage(pdfContext, img, rect); + } catch (IOException ioe) { + ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get( + getUserAgent().getEventBroadcaster()); + eventProducer.imageWritingError(this, ioe); + return; + } + } catch (ImageException ie) { + ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get( + getUserAgent().getEventBroadcaster()); + eventProducer.imageError(this, (info != null ? info.toString() : uri), ie, null); + } catch (FileNotFoundException fe) { + ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get( + getUserAgent().getEventBroadcaster()); + eventProducer.imageNotFound(this, (info != null ? info.toString() : uri), fe, null); + } catch (IOException ioe) { + ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get( + getUserAgent().getEventBroadcaster()); + eventProducer.imageIOError(this, (info != null ? info.toString() : uri), ioe, null); + } + // output new data + try { + generator.flushPDFDoc(); + } catch (IOException ioe) { + throw new IFException("I/O error flushing the PDF document", ioe); + } + } + + /** + * 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 + */ + private void placeImage(Rectangle rect, PDFXObject xobj) { + generator.saveGraphicsState(); + generator.add(format(rect.width) + " 0 0 " + + format(-rect.height) + " " + + format(rect.x) + " " + + format(rect.y + rect.height ) + + " cm " + xobj.getName() + " Do\n"); + generator.restoreGraphicsState(); } + /** {@inheritDoc} */ - public void drawImage(String uri, Rectangle rect) throws IFException { + public void startImage(Rectangle rect) throws IFException { // TODO Auto-generated method stub } @@ -348,14 +388,6 @@ public class PDFPainter extends AbstractBinaryWritingIFPainter { } - private static String toString(Paint paint) { - if (paint instanceof Color) { - return ColorUtil.colorToString((Color)paint); - } else { - throw new UnsupportedOperationException("Paint not supported: " + paint); - } - } - /** * Formats a integer value (normally coordinates in millipoints) to a String. * @param value the value (in millipoints) @@ -365,37 +397,16 @@ public class PDFPainter extends AbstractBinaryWritingIFPainter { return PDFNumber.doubleOut(value / 1000f); } - /** - * 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 - */ - private void updateColor(Color col, boolean fill) { - if (col == null) { - return; - } - boolean update = false; - if (fill) { - update = currentState.setBackColor(col); - } else { - update = currentState.setColor(col); - } - - if (update) { - pdfUtil.setColor(col, fill, this.currentStream); - } - } - /** {@inheritDoc} */ public void drawRect(Rectangle rect, Paint fill, Color stroke) throws IFException { if (fill == null && stroke == null) { return; } - endTextObject(); + generator.endTextObject(); if (rect.width != 0 && rect.height != 0) { if (fill != null) { if (fill instanceof Color) { - updateColor((Color)fill, true); + generator.updateColor((Color)fill, true, null); } else { throw new UnsupportedOperationException("Non-Color paints NYI"); } @@ -415,21 +426,7 @@ public class PDFPainter extends AbstractBinaryWritingIFPainter { sb.append(" S"); } sb.append('\n'); - currentStream.add(sb.toString()); - } - } - - /** Indicates the beginning of a text object. */ - private void beginTextObject() { - if (!textutil.isInTextObject()) { - textutil.beginTextObject(); - } - } - - /** Indicates the end of a text object. */ - private void endTextObject() { - if (textutil.isInTextObject()) { - textutil.endTextObject(); + generator.add(sb.toString()); } } @@ -447,14 +444,14 @@ public class PDFPainter extends AbstractBinaryWritingIFPainter { /** {@inheritDoc} */ public void drawText(int x, int y, int[] dx, int[] dy, String text) throws IFException { //Note: dy is currently ignored - beginTextObject(); + generator.beginTextObject(); FontTriplet triplet = new FontTriplet( state.getFontFamily(), state.getFontStyle(), state.getFontWeight()); //TODO Ignored: state.getFontVariant() String fontKey = fontInfo.getInternalFontKey(triplet); int sizeMillipoints = state.getFontSize(); float fontSize = sizeMillipoints / 1000f; - updateColor(state.getTextColor(), true); + generator.updateColor(state.getTextColor(), true, null); // This assumes that *all* CIDFonts use a /ToUnicode mapping Typeface tf = getTypeface(fontKey); @@ -465,6 +462,7 @@ public class PDFPainter extends AbstractBinaryWritingIFPainter { Font font = fontInfo.getFontInstance(triplet, sizeMillipoints); String fontName = font.getFontName(); + PDFTextUtil textutil = generator.getTextUtil(); textutil.updateTf(fontKey, fontSize, tf.isMultiByte()); textutil.writeTextMatrix(new AffineTransform(1, 0, 0, -1, x / 1000f, y / 1000f)); diff --git a/src/java/org/apache/fop/render/pdf/PDFPainterMaker.java b/src/java/org/apache/fop/render/pdf/PDFPainterMaker.java index b3fe42824..f1fbe48fd 100644 --- a/src/java/org/apache/fop/render/pdf/PDFPainterMaker.java +++ b/src/java/org/apache/fop/render/pdf/PDFPainterMaker.java @@ -30,7 +30,8 @@ import org.apache.fop.render.intermediate.IFPainterConfigurator; */ public class PDFPainterMaker extends AbstractIFPainterMaker { - private static final String[] MIMES = new String[] {MimeConstants.MIME_PDF}; + //TODO Revert to normal MIME after stabilization! + private static final String[] MIMES = new String[] {MimeConstants.MIME_PDF + ";mode=painter"}; /** {@inheritDoc} */ public IFPainter makePainter(FOUserAgent ua) { diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderer.java b/src/java/org/apache/fop/render/pdf/PDFRenderer.java index cfe8b9902..4ce3dfbd9 100644 --- a/src/java/org/apache/fop/render/pdf/PDFRenderer.java +++ b/src/java/org/apache/fop/render/pdf/PDFRenderer.java @@ -76,7 +76,6 @@ import org.apache.fop.pdf.PDFAnnotList; import org.apache.fop.pdf.PDFDocument; 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.PDFInfo; import org.apache.fop.pdf.PDFLink; @@ -86,7 +85,6 @@ import org.apache.fop.pdf.PDFPage; 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; @@ -167,10 +165,8 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf */ protected PDFResources pdfResources; - /** - * the current stream to add PDF commands to - */ - protected PDFStream currentStream; + /** The current content generator to produce PDF commands with */ + protected PDFContentGenerator generator; /** * the current annotation list to add annotations to @@ -187,11 +183,6 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf */ protected String currentPageRef; - /** drawing state */ - protected PDFState currentState = null; - - /** Text generation utility holding the current font status */ - protected PDFTextUtil textutil; /** page height */ protected int pageHeight; @@ -214,6 +205,14 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf return this.pdfUtil; } + PDFContentGenerator getGenerator() { + return this.generator; + } + + PDFState getState() { + return getGenerator().getState(); + } + /** {@inheritDoc} */ public void startRenderer(OutputStream stream) throws IOException { if (userAgent == null) { @@ -258,11 +257,12 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf pageReferences.clear(); //pvReferences.clear(); pdfResources = null; - currentStream = null; + this.generator = null; + //currentStream = null; currentContext = null; currentPage = null; - currentState = null; - this.textutil = null; + //currentState = null; + //this.textutil = null; idPositions.clear(); idGoTos.clear(); @@ -354,48 +354,24 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf return new PDFGraphics2DAdapter(this); } - /** - * 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(); - currentStream.add("q\n"); - } - - private void restoreGraphicsState(boolean popState) { - endTextObject(); - currentStream.add("Q\n"); - if (popState) { - currentState.pop(); - } + generator.saveGraphicsState(); } /** {@inheritDoc} */ protected void restoreGraphicsState() { - restoreGraphicsState(true); + generator.restoreGraphicsState(); } /** Indicates the beginning of a text object. */ protected void beginTextObject() { - if (!textutil.isInTextObject()) { - textutil.beginTextObject(); - } + generator.beginTextObject(); } /** Indicates the end of a text object. */ protected void endTextObject() { - if (textutil.isInTextObject()) { - textutil.endTextObject(); - } + generator.endTextObject(); } /** @@ -483,6 +459,8 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf double h = bounds.getHeight(); pageHeight = (int) h; + this.generator = new PDFContentGenerator(this.pdfDoc, this.ostream, this.currentPage); + /* currentStream = this.pdfDoc.getFactory() .makeStream(PDFFilterList.CONTENT_FILTER, false); this.textutil = new PDFTextUtil() { @@ -492,31 +470,37 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf }; currentState = new PDFState(); + */ // Transform the PDF's default coordinate system (0,0 at lower left) to the PDFRenderer's AffineTransform basicPageTransform = new AffineTransform(1, 0, 0, -1, 0, pageHeight / 1000f); + generator.concatenate(basicPageTransform); + /* currentState.concatenate(basicPageTransform); currentStream.add(CTMHelper.toPDFString(basicPageTransform, false) + " cm\n"); + */ super.renderPage(page); - this.pdfDoc.registerObject(currentStream); - currentPage.setContents(currentStream); + this.pdfDoc.registerObject(generator.getStream()); + currentPage.setContents(generator.getStream()); PDFAnnotList annots = currentPage.getAnnotations(); if (annots != null) { this.pdfDoc.addObject(annots); } this.pdfDoc.addObject(currentPage); - this.pdfDoc.output(ostream); - this.textutil = null; + this.generator.flushPDFDoc(); + this.generator = null; } /** {@inheritDoc} */ protected void startVParea(CTM ctm, Rectangle2D clippingRect) { saveGraphicsState(); // Set the given CTM in the graphics state + /* currentState.concatenate( new AffineTransform(CTMHelper.toPDFArray(ctm))); + */ if (clippingRect != null) { clipRect((float)clippingRect.getX() / 1000f, @@ -525,7 +509,8 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf (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} */ @@ -535,10 +520,12 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf /** {@inheritDoc} */ protected void concatenateTransformationMatrix(AffineTransform at) { + generator.concatenate(at); + /* if (!at.isIdentity()) { currentState.concatenate(at); currentStream.add(CTMHelper.toPDFString(at, false) + " cm\n"); - } + }*/ } /** @@ -562,7 +549,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf } switch (style) { case Constants.EN_DASHED: - setColor(col, false, null); + generator.setColor(col, false); if (horz) { float unit = Math.abs(2 * h); int rep = (int)(w / unit); @@ -570,10 +557,10 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf rep++; } unit = w / rep; - currentStream.add("[" + format(unit) + "] 0 d "); - currentStream.add(format(h) + " w\n"); + generator.add("[" + format(unit) + "] 0 d "); + generator.add(format(h) + " w\n"); float ym = y1 + (h / 2); - currentStream.add(format(x1) + " " + format(ym) + " m " + generator.add(format(x1) + " " + format(ym) + " m " + format(x2) + " " + format(ym) + " l S\n"); } else { float unit = Math.abs(2 * w); @@ -582,16 +569,16 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf rep++; } unit = h / rep; - currentStream.add("[" + format(unit) + "] 0 d "); - currentStream.add(format(w) + " w\n"); + generator.add("[" + format(unit) + "] 0 d "); + generator.add(format(w) + " w\n"); float xm = x1 + (w / 2); - currentStream.add(format(xm) + " " + format(y1) + " m " + generator.add(format(xm) + " " + format(y1) + " m " + format(xm) + " " + format(y2) + " l S\n"); } break; case Constants.EN_DOTTED: - setColor(col, false, null); - currentStream.add("1 J "); + generator.setColor(col, false); + generator.add("1 J "); if (horz) { float unit = Math.abs(2 * h); int rep = (int)(w / unit); @@ -599,10 +586,10 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf rep++; } unit = w / rep; - currentStream.add("[0 " + format(unit) + "] 0 d "); - currentStream.add(format(h) + " w\n"); + generator.add("[0 " + format(unit) + "] 0 d "); + generator.add(format(h) + " w\n"); float ym = y1 + (h / 2); - currentStream.add(format(x1) + " " + format(ym) + " m " + generator.add(format(x1) + " " + format(ym) + " m " + format(x2) + " " + format(ym) + " l S\n"); } else { float unit = Math.abs(2 * w); @@ -611,33 +598,33 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf rep++; } unit = h / rep; - currentStream.add("[0 " + format(unit) + " ] 0 d "); - currentStream.add(format(w) + " w\n"); + generator.add("[0 " + format(unit) + " ] 0 d "); + generator.add(format(w) + " w\n"); float xm = x1 + (w / 2); - currentStream.add(format(xm) + " " + format(y1) + " m " + generator.add(format(xm) + " " + format(y1) + " m " + format(xm) + " " + format(y2) + " l S\n"); } break; case Constants.EN_DOUBLE: - setColor(col, false, null); - currentStream.add("[] 0 d "); + generator.setColor(col, false); + generator.add("[] 0 d "); if (horz) { float h3 = h / 3; - currentStream.add(format(h3) + " w\n"); + generator.add(format(h3) + " w\n"); float ym1 = y1 + (h3 / 2); float ym2 = ym1 + h3 + h3; - currentStream.add(format(x1) + " " + format(ym1) + " m " + generator.add(format(x1) + " " + format(ym1) + " m " + format(x2) + " " + format(ym1) + " l S\n"); - currentStream.add(format(x1) + " " + format(ym2) + " m " + generator.add(format(x1) + " " + format(ym2) + " m " + format(x2) + " " + format(ym2) + " l S\n"); } else { float w3 = w / 3; - currentStream.add(format(w3) + " w\n"); + generator.add(format(w3) + " w\n"); float xm1 = x1 + (w3 / 2); float xm2 = xm1 + w3 + w3; - currentStream.add(format(xm1) + " " + format(y1) + " m " + generator.add(format(xm1) + " " + format(y1) + " m " + format(xm1) + " " + format(y2) + " l S\n"); - currentStream.add(format(xm2) + " " + format(y1) + " m " + generator.add(format(xm2) + " " + format(y1) + " m " + format(xm2) + " " + format(y2) + " l S\n"); } break; @@ -645,36 +632,36 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf case Constants.EN_RIDGE: { float colFactor = (style == EN_GROOVE ? 0.4f : -0.4f); - currentStream.add("[] 0 d "); + generator.add("[] 0 d "); if (horz) { Color uppercol = lightenColor(col, -colFactor); Color lowercol = lightenColor(col, colFactor); float h3 = h / 3; - currentStream.add(format(h3) + " w\n"); + generator.add(format(h3) + " w\n"); float ym1 = y1 + (h3 / 2); - setColor(uppercol, false, null); - currentStream.add(format(x1) + " " + format(ym1) + " m " + generator.setColor(uppercol, false); + generator.add(format(x1) + " " + format(ym1) + " m " + format(x2) + " " + format(ym1) + " l S\n"); - setColor(col, false, null); - currentStream.add(format(x1) + " " + format(ym1 + h3) + " m " + generator.setColor(col, false); + generator.add(format(x1) + " " + format(ym1 + h3) + " m " + format(x2) + " " + format(ym1 + h3) + " l S\n"); - setColor(lowercol, false, null); - currentStream.add(format(x1) + " " + format(ym1 + h3 + h3) + " m " + generator.setColor(lowercol, false); + generator.add(format(x1) + " " + format(ym1 + h3 + h3) + " m " + format(x2) + " " + format(ym1 + h3 + h3) + " l S\n"); } else { Color leftcol = lightenColor(col, -colFactor); Color rightcol = lightenColor(col, colFactor); float w3 = w / 3; - currentStream.add(format(w3) + " w\n"); + generator.add(format(w3) + " w\n"); float xm1 = x1 + (w3 / 2); - setColor(leftcol, false, null); - currentStream.add(format(xm1) + " " + format(y1) + " m " + generator.setColor(leftcol, false); + generator.add(format(xm1) + " " + format(y1) + " m " + format(xm1) + " " + format(y2) + " l S\n"); - setColor(col, false, null); - currentStream.add(format(xm1 + w3) + " " + format(y1) + " m " + generator.setColor(col, false); + generator.add(format(xm1 + w3) + " " + format(y1) + " m " + format(xm1 + w3) + " " + format(y2) + " l S\n"); - setColor(rightcol, false, null); - currentStream.add(format(xm1 + w3 + w3) + " " + format(y1) + " m " + generator.setColor(rightcol, false); + generator.add(format(xm1 + w3 + w3) + " " + format(y1) + " m " + format(xm1 + w3 + w3) + " " + format(y2) + " l S\n"); } break; @@ -683,21 +670,21 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf case Constants.EN_OUTSET: { float colFactor = (style == EN_OUTSET ? 0.4f : -0.4f); - currentStream.add("[] 0 d "); + generator.add("[] 0 d "); Color c = col; if (horz) { c = lightenColor(c, (startOrBefore ? 1 : -1) * colFactor); - currentStream.add(format(h) + " w\n"); + generator.add(format(h) + " w\n"); float ym1 = y1 + (h / 2); - setColor(c, false, null); - currentStream.add(format(x1) + " " + format(ym1) + " m " + generator.setColor(c, false); + generator.add(format(x1) + " " + format(ym1) + " m " + format(x2) + " " + format(ym1) + " l S\n"); } else { c = lightenColor(c, (startOrBefore ? 1 : -1) * colFactor); - currentStream.add(format(w) + " w\n"); + generator.add(format(w) + " w\n"); float xm1 = x1 + (w / 2); - setColor(c, false, null); - currentStream.add(format(xm1) + " " + format(y1) + " m " + generator.setColor(c, false); + generator.add(format(xm1) + " " + format(y1) + " m " + format(xm1) + " " + format(y2) + " l S\n"); } break; @@ -705,36 +692,25 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf case Constants.EN_HIDDEN: break; default: - setColor(col, false, null); - currentStream.add("[] 0 d "); + generator.setColor(col, false); + generator.add("[] 0 d "); if (horz) { - currentStream.add(format(h) + " w\n"); + generator.add(format(h) + " w\n"); float ym = y1 + (h / 2); - currentStream.add(format(x1) + " " + format(ym) + " m " + generator.add(format(x1) + " " + format(ym) + " m " + format(x2) + " " + format(ym) + " l S\n"); } else { - currentStream.add(format(w) + " w\n"); + generator.add(format(w) + " w\n"); float xm = x1 + (w / 2); - currentStream.add(format(xm) + " " + format(y1) + " m " + generator.add(format(xm) + " " + format(y1) + " m " + format(xm) + " " + format(y2) + " l S\n"); } } } - /** - * Sets the current line width in points. - * @param width line width in points - */ - private 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"); - } - } - /** {@inheritDoc} */ protected void clipRect(float x, float y, float width, float height) { - currentStream.add(format(x) + " " + format(y) + " " + generator.add(format(x) + " " + format(y) + " " + format(width) + " " + format(height) + " re "); clip(); } @@ -743,8 +719,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf * Clip an area. */ protected void clip() { - currentStream.add("W\n"); - currentStream.add("n\n"); + generator.add("W\n" + "n\n"); } /** @@ -753,7 +728,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf * @param y y coordinate */ protected void moveTo(float x, float y) { - currentStream.add(format(x) + " " + format(y) + " m "); + generator.add(format(x) + " " + format(y) + " m "); } /** @@ -763,7 +738,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf * @param y y coordinate */ protected void lineTo(float x, float y) { - currentStream.add(format(x) + " " + format(y) + " l "); + generator.add(format(x) + " " + format(y) + " l "); } /** @@ -771,7 +746,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf * the current point to the starting point of the subpath. */ protected void closePath() { - currentStream.add("h "); + generator.add("h "); } /** @@ -779,7 +754,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf */ protected void fillRect(float x, float y, float w, float h) { if (w != 0 && h != 0) { - currentStream.add(format(x) + " " + format(y) + " " + generator.add(format(x) + " " + format(y) + " " + format(w) + " " + format(h) + " re f\n"); } } @@ -793,8 +768,8 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf * @param endy the y end position */ private void drawLine(float startx, float starty, float endx, float endy) { - currentStream.add(format(startx) + " " + format(starty) + " m "); - currentStream.add(format(endx) + " " + format(endy) + " l S\n"); + generator.add(format(startx) + " " + format(starty) + " m "); + generator.add(format(endx) + " " + format(endy) + " l S\n"); } /** @@ -805,15 +780,15 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf List breakOutList = new java.util.ArrayList(); PDFState.Data data; while (true) { - data = currentState.getData(); - if (currentState.pop() == null) { + data = getState().getData(); + if (getState().pop() == null) { break; } if (breakOutList.size() == 0) { - comment("------ break out!"); + generator.comment("------ break out!"); } breakOutList.add(0, data); //Insert because of stack-popping - restoreGraphicsState(false); + generator.restoreGraphicsState(); } return breakOutList; } @@ -823,7 +798,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf * @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; Iterator i = breakOutList.iterator(); while (i.hasNext()) { @@ -835,7 +810,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf //Left out for now because all this painting stuff is very //inconsistent. Some values go over PDFState, some don't. } - comment("------ done."); + generator.comment("------ done."); } /** @@ -967,7 +942,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf */ protected void saveAbsolutePosition(String id, int relativeIPP, int relativeBPP) { saveAbsolutePosition(id, currentPageRef, - relativeIPP, relativeBPP, currentState.getTransform()); + relativeIPP, relativeBPP, getState().getTransform()); } /** @@ -991,8 +966,8 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf bpp += currentBPPosition; } AffineTransform tf = positioning == Block.FIXED - ? currentState.getBaseTransform() - : currentState.getTransform(); + ? getState().getBaseTransform() + : getState().getTransform(); saveAbsolutePosition(id, currentPageRef, ipp, bpp, tf); } } @@ -1055,7 +1030,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf int bpp = currentBPPosition + ip.getOffset(); ipRect = new Rectangle2D.Float(ipp / 1000f, bpp / 1000f, ip.getIPD() / 1000f, ip.getBPD() / 1000f); - AffineTransform transform = currentState.getTransform(); + AffineTransform transform = getState().getTransform(); ipRect = transform.createTransformedShape(ipRect).getBounds2D(); factory = pdfDoc.getFactory(); @@ -1131,6 +1106,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf // This assumes that *all* CIDFonts use a /ToUnicode mapping Typeface tf = getTypeface(fontName); + PDFTextUtil textutil = generator.getTextUtil(); textutil.updateTf(fontName, size / 1000f, tf.isMultiByte()); @@ -1174,7 +1150,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf if (tws != 0) { float adjust = tws / (font.getFontSize() / 1000f); - textutil.adjustGlyphTJ(adjust); + generator.getTextUtil().adjustGlyphTJ(adjust); } } @@ -1213,6 +1189,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf if (tf instanceof SingleByteFont) { singleByteFont = (SingleByteFont)tf; } + PDFTextUtil textutil = generator.getTextUtil(); int l = s.length(); @@ -1258,48 +1235,9 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf } } - /** - * 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) { - pdfUtil.setColor(col, fill, pdf); - } else { - pdfUtil.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. - */ - private void updateColor(Color col, boolean fill, StringBuffer pdf) { - if (col == null) { - return; - } - boolean update = false; - if (fill) { - update = currentState.setBackColor(col); - } else { - update = currentState.setColor(col); - } - - if (update) { - setColor(col, fill, pdf); - } - } - /** {@inheritDoc} */ protected void updateColor(Color col, boolean fill) { - updateColor(col, fill, null); + generator.updateColor(col, fill, null); } /** {@inheritDoc} */ @@ -1398,7 +1336,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf // output new data try { - this.pdfDoc.output(ostream); + this.generator.flushPDFDoc(); } catch (IOException ioe) { // ioexception will be caught later } @@ -1414,7 +1352,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf */ public void placeImage(float x, float y, float w, float h, PDFXObject xobj) { saveGraphicsState(); - currentStream.add(format(w) + " 0 0 " + generator.add(format(w) + " 0 0 " + format(-h) + " " + format(currentIPPosition / 1000f + x) + " " + format(currentBPPosition / 1000f + h + y) @@ -1429,12 +1367,12 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf x, y, width, height, foreignAttributes); context.setProperty(PDFRendererContextConstants.PDF_DOCUMENT, pdfDoc); context.setProperty(PDFRendererContextConstants.OUTPUT_STREAM, ostream); - context.setProperty(PDFRendererContextConstants.PDF_STATE, currentState); + context.setProperty(PDFRendererContextConstants.PDF_STATE, getState()); context.setProperty(PDFRendererContextConstants.PDF_PAGE, currentPage); context.setProperty(PDFRendererContextConstants.PDF_CONTEXT, currentContext == null ? currentPage : currentContext); context.setProperty(PDFRendererContextConstants.PDF_CONTEXT, currentContext); - context.setProperty(PDFRendererContextConstants.PDF_STREAM, currentStream); + context.setProperty(PDFRendererContextConstants.PDF_STREAM, generator.getStream()); context.setProperty(PDFRendererContextConstants.PDF_FONT_INFO, fontInfo); context.setProperty(PDFRendererContextConstants.PDF_FONT_NAME, ""); context.setProperty(PDFRendererContextConstants.PDF_FONT_SIZE, new Integer(0)); @@ -1449,7 +1387,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf public void renderLeader(Leader area) { renderInlineAreaBackAndBorders(area); - currentState.push(); + getState().push(); saveGraphicsState(); int style = area.getRuleStyle(); float startx = (currentIPPosition + area.getBorderAndPaddingWidthStart()) / 1000f; @@ -1470,7 +1408,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf clipRect(startx, starty, endx - startx, ruleThickness); //This displaces the dots to the right by half a dot's width //TODO There's room for improvement here - currentStream.add("1 0 0 1 " + format(ruleThickness / 2) + " 0 cm\n"); + generator.add("1 0 0 1 " + format(ruleThickness / 2) + " 0 cm\n"); drawBorderLine(startx, starty, endx, starty + ruleThickness, true, true, style, col); break; @@ -1478,36 +1416,36 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf case EN_RIDGE: float half = area.getRuleThickness() / 2000f; - setColor(lightenColor(col, 0.6f), true, null); - currentStream.add(format(startx) + " " + format(starty) + " m\n"); - currentStream.add(format(endx) + " " + format(starty) + " l\n"); - currentStream.add(format(endx) + " " + format(starty + 2 * half) + " l\n"); - currentStream.add(format(startx) + " " + format(starty + 2 * half) + " l\n"); - currentStream.add("h\n"); - currentStream.add("f\n"); - setColor(col, true, null); + generator.setColor(lightenColor(col, 0.6f), true); + generator.add(format(startx) + " " + format(starty) + " m\n"); + generator.add(format(endx) + " " + format(starty) + " l\n"); + generator.add(format(endx) + " " + format(starty + 2 * half) + " l\n"); + generator.add(format(startx) + " " + format(starty + 2 * half) + " l\n"); + generator.add("h\n"); + generator.add("f\n"); + generator.setColor(col, true); if (style == EN_GROOVE) { - currentStream.add(format(startx) + " " + format(starty) + " m\n"); - currentStream.add(format(endx) + " " + format(starty) + " l\n"); - currentStream.add(format(endx) + " " + format(starty + half) + " l\n"); - currentStream.add(format(startx + half) + " " + format(starty + half) + " l\n"); - currentStream.add(format(startx) + " " + format(starty + 2 * half) + " l\n"); + generator.add(format(startx) + " " + format(starty) + " m\n"); + generator.add(format(endx) + " " + format(starty) + " l\n"); + generator.add(format(endx) + " " + format(starty + half) + " l\n"); + generator.add(format(startx + half) + " " + format(starty + half) + " l\n"); + generator.add(format(startx) + " " + format(starty + 2 * half) + " l\n"); } else { - currentStream.add(format(endx) + " " + format(starty) + " m\n"); - currentStream.add(format(endx) + " " + format(starty + 2 * half) + " l\n"); - currentStream.add(format(startx) + " " + format(starty + 2 * half) + " l\n"); - currentStream.add(format(startx) + " " + format(starty + half) + " l\n"); - currentStream.add(format(endx - half) + " " + format(starty + half) + " l\n"); + generator.add(format(endx) + " " + format(starty) + " m\n"); + generator.add(format(endx) + " " + format(starty + 2 * half) + " l\n"); + generator.add(format(startx) + " " + format(starty + 2 * half) + " l\n"); + generator.add(format(startx) + " " + format(starty + half) + " l\n"); + generator.add(format(endx - half) + " " + format(starty + half) + " l\n"); } - currentStream.add("h\n"); - currentStream.add("f\n"); + generator.add("h\n"); + generator.add("f\n"); break; default: throw new UnsupportedOperationException("rule style not supported"); } restoreGraphicsState(); - currentState.pop(); + getState().pop(); beginTextObject(); super.renderLeader(area); } diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderingContext.java b/src/java/org/apache/fop/render/pdf/PDFRenderingContext.java new file mode 100644 index 000000000..98b0c8203 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/PDFRenderingContext.java @@ -0,0 +1,82 @@ +/* + * 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 org.apache.xmlgraphics.util.MimeConstants; + +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.fonts.FontInfo; +import org.apache.fop.pdf.PDFPage; +import org.apache.fop.render.AbstractRenderingContext; + +/** + * Rendering context for PDF production. + */ +public class PDFRenderingContext extends AbstractRenderingContext { + + private PDFContentGenerator generator; + private FontInfo fontInfo; + private PDFPage page; + + /** + * Main constructor. + * @param userAgent the user agent + * @param generator the PDF content generator + * @param page the current PDF page + * @param fontInfo the font list + */ + public PDFRenderingContext(FOUserAgent userAgent, + PDFContentGenerator generator, PDFPage page, FontInfo fontInfo) { + super(userAgent); + this.generator = generator; + this.page = page; + this.fontInfo = fontInfo; + } + + /** {@inheritDoc} */ + public String getMimeType() { + return MimeConstants.MIME_PDF; + } + + /** + * Returns the PDF content generator. + * @return the PDF content generator + */ + public PDFContentGenerator getGenerator() { + return this.generator; + } + + /** + * Returns the current PDF page. + * @return the PDF page + */ + public PDFPage getPage() { + return this.page; + } + + /** + * Returns the font list. + * @return the font list + */ + public FontInfo getFontInfo() { + return this.fontInfo; + } + +} diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java index adc3ff771..e44edf8af 100644 --- a/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java +++ b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java @@ -19,7 +19,6 @@ package org.apache.fop.render.pdf; -import java.awt.Color; import java.awt.color.ICC_Profile; import java.io.IOException; import java.io.InputStream; @@ -41,7 +40,6 @@ import org.apache.xmlgraphics.xmp.schemas.XMPBasicSchema; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.fo.extensions.xmp.XMPMetadata; import org.apache.fop.pdf.PDFAMode; -import org.apache.fop.pdf.PDFColor; import org.apache.fop.pdf.PDFConformanceException; import org.apache.fop.pdf.PDFDictionary; import org.apache.fop.pdf.PDFDocument; @@ -54,7 +52,6 @@ import org.apache.fop.pdf.PDFMetadata; import org.apache.fop.pdf.PDFNumsArray; import org.apache.fop.pdf.PDFOutputIntent; import org.apache.fop.pdf.PDFPageLabels; -import org.apache.fop.pdf.PDFStream; import org.apache.fop.pdf.PDFXMode; import org.apache.fop.util.ColorProfileUtil; @@ -410,30 +407,4 @@ class PDFRenderingUtil implements PDFConfigurationConstants { nums.put(pageIndex, dict); } - /** - * 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; - PDFColor color = new PDFColor(this.pdfDoc, col); - pdf.append(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 - * @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.pdfDoc, col); - stream.add(color.getColorSpaceOut(fill)); - } - - } diff --git a/src/java/org/apache/fop/render/pdf/PDFSVGHandler.java b/src/java/org/apache/fop/render/pdf/PDFSVGHandler.java index 864a82517..11d9b1c3f 100644 --- a/src/java/org/apache/fop/render/pdf/PDFSVGHandler.java +++ b/src/java/org/apache/fop/render/pdf/PDFSVGHandler.java @@ -44,8 +44,6 @@ import org.apache.fop.fonts.FontInfo; import org.apache.fop.pdf.PDFDocument; import org.apache.fop.pdf.PDFPage; import org.apache.fop.pdf.PDFResourceContext; -import org.apache.fop.pdf.PDFState; -import org.apache.fop.pdf.PDFStream; import org.apache.fop.render.AbstractGenericSVGHandler; import org.apache.fop.render.Renderer; import org.apache.fop.render.RendererContext; @@ -78,10 +76,10 @@ public class PDFSVGHandler extends AbstractGenericSVGHandler PDFInfo pdfi = new PDFInfo(); pdfi.pdfDoc = (PDFDocument)context.getProperty(PDF_DOCUMENT); pdfi.outputStream = (OutputStream)context.getProperty(OUTPUT_STREAM); - pdfi.pdfState = (PDFState)context.getProperty(PDF_STATE); + //pdfi.pdfState = (PDFState)context.getProperty(PDF_STATE); pdfi.pdfPage = (PDFPage)context.getProperty(PDF_PAGE); pdfi.pdfContext = (PDFResourceContext)context.getProperty(PDF_CONTEXT); - pdfi.currentStream = (PDFStream)context.getProperty(PDF_STREAM); + //pdfi.currentStream = (PDFStream)context.getProperty(PDF_STREAM); pdfi.width = ((Integer)context.getProperty(WIDTH)).intValue(); pdfi.height = ((Integer)context.getProperty(HEIGHT)).intValue(); pdfi.fi = (FontInfo)context.getProperty(PDF_FONT_INFO); @@ -108,13 +106,13 @@ public class PDFSVGHandler extends AbstractGenericSVGHandler /** see OUTPUT_STREAM */ public OutputStream outputStream; /** see PDF_STATE */ - public PDFState pdfState; + //public PDFState pdfState; /** see PDF_PAGE */ public PDFPage pdfPage; /** see PDF_CONTEXT */ public PDFResourceContext pdfContext; /** see PDF_STREAM */ - public PDFStream currentStream; + //public PDFStream currentStream; /** see PDF_WIDTH */ public int width; /** see PDF_HEIGHT */ @@ -216,14 +214,15 @@ public class PDFSVGHandler extends AbstractGenericSVGHandler * Note: To have the svg overlay (under) a text area then use * an fo:block-container */ - pdfInfo.currentStream.add("%SVG setup\n"); - renderer.saveGraphicsState(); - renderer.setColor(Color.black, false, null); - renderer.setColor(Color.black, true, null); + PDFContentGenerator generator = renderer.getGenerator(); + generator.comment("SVG setup"); + generator.saveGraphicsState(); + generator.setColor(Color.black, false); + generator.setColor(Color.black, true); if (!scaling.isIdentity()) { - pdfInfo.currentStream.add("%viewbox\n"); - pdfInfo.currentStream.add(CTMHelper.toPDFString(scaling, false) + " cm\n"); + generator.comment("viewbox"); + generator.add(CTMHelper.toPDFString(scaling, false) + " cm\n"); } //SVGSVGElement svg = ((SVGDocument)doc).getRootElement(); @@ -238,38 +237,38 @@ public class PDFSVGHandler extends AbstractGenericSVGHandler graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext()); if (!resolutionScaling.isIdentity()) { - pdfInfo.currentStream.add("%resolution scaling for " + uaResolution + generator.comment("resolution scaling for " + uaResolution + " -> " + deviceResolution + "\n"); - pdfInfo.currentStream.add( + generator.add( CTMHelper.toPDFString(resolutionScaling, false) + " cm\n"); 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 - pdfInfo.pdfState.push(); - pdfInfo.pdfState.concatenate(imageTransform); + generator.getState().push(); + 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.pdfState.getTransform()); + aBridge.getCurrentTransform().setTransform(generator.getState().getTransform()); - graphics.setPDFState(pdfInfo.pdfState); + graphics.setPDFState(generator.getState()); graphics.setOutputStream(pdfInfo.outputStream); try { root.paint(graphics); - pdfInfo.currentStream.add(graphics.getString()); + generator.add(graphics.getString()); } catch (Exception e) { SVGEventProducer eventProducer = SVGEventProducer.Provider.get( context.getUserAgent().getEventBroadcaster()); eventProducer.svgRenderingError(this, e, getDocumentURI(doc)); } - pdfInfo.pdfState.pop(); - renderer.restoreGraphicsState(); - pdfInfo.currentStream.add("%SVG end\n"); + generator.getState().pop(); + generator.restoreGraphicsState(); + generator.comment("SVG end"); } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/svg/PDFGraphics2D.java b/src/java/org/apache/fop/svg/PDFGraphics2D.java index ca2245a12..0b2f0a45f 100644 --- a/src/java/org/apache/fop/svg/PDFGraphics2D.java +++ b/src/java/org/apache/fop/svg/PDFGraphics2D.java @@ -440,6 +440,14 @@ public class PDFGraphics2D extends AbstractGraphics2D { } PDFXObject xObject = this.pdfDoc.addImage(resourceContext, pdfImage); + flushPDFDocument(); + + AffineTransform at = new AffineTransform(); + at.translate(x, y); + useXObject(xObject, at, width, height); + } + + private void flushPDFDocument() { if (outputStream != null) { try { this.pdfDoc.output(outputStream); @@ -447,10 +455,6 @@ public class PDFGraphics2D extends AbstractGraphics2D { // ignore exception, will be thrown again later } } - - AffineTransform at = new AffineTransform(); - at.translate(x, y); - useXObject(xObject, at, width, height); } /** @@ -1044,13 +1048,7 @@ public class PDFGraphics2D extends AbstractGraphics2D { this.pdfDoc.addObject(annots); } - if (outputStream != null) { - try { - this.pdfDoc.output(outputStream); - } catch (IOException ioe) { - // ignore exception, will be thrown again later - } - } + flushPDFDocument(); return true; } @@ -1147,26 +1145,14 @@ public class PDFGraphics2D extends AbstractGraphics2D { PDFImageXObject xobj = pdfDoc.addImage(resourceContext, fopimg); maskRef = xobj.referencePDF(); - if (outputStream != null) { - try { - this.pdfDoc.output(outputStream); - } catch (IOException ioe) { - // ignore exception, will be thrown again later - } - } + flushPDFDocument(); } BitmapImage fopimg; fopimg = new BitmapImage("TempImage:" + pctx.toString(), devW, devH, rgb, maskRef); fopimg.setTransparent(new PDFColor(255, 255, 255)); imageInfo = pdfDoc.addImage(resourceContext, fopimg); - if (outputStream != null) { - try { - this.pdfDoc.output(outputStream); - } catch (IOException ioe) { - // ignore exception, will be thrown again later - } - } + flushPDFDocument(); } currentStream.write("q\n"); @@ -1275,13 +1261,7 @@ public class PDFGraphics2D extends AbstractGraphics2D { ImageRendered imgRend = new ImageRendered(info, img, null); ImageRenderedAdapter adapter = new ImageRenderedAdapter(imgRend, key); PDFXObject xObject = pdfDoc.addImage(resourceContext, adapter); - if (outputStream != null) { - try { - this.pdfDoc.output(outputStream); - } catch (IOException ioe) { - // ignore exception, will be thrown again later - } - } + flushPDFDocument(); return xObject; } |