diff options
Diffstat (limited to 'src/java')
20 files changed, 1295 insertions, 292 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 index 58e2a52be..730032927 100644 --- a/src/java/META-INF/services/org.apache.fop.render.ImageHandler +++ b/src/java/META-INF/services/org.apache.fop.render.ImageHandler @@ -7,3 +7,9 @@ org.apache.fop.render.java2d.Java2DImageHandlerRenderedImage org.apache.fop.render.java2d.Java2DImageHandlerGraphics2D
org.apache.fop.render.pcl.PCLImageHandlerRenderedImage
org.apache.fop.render.pcl.PCLImageHandlerGraphics2D
+org.apache.fop.render.ps.PSImageHandlerRenderedImage
+org.apache.fop.render.ps.PSImageHandlerEPS
+org.apache.fop.render.ps.PSImageHandlerRawCCITTFax
+org.apache.fop.render.ps.PSImageHandlerRawJPEG
+org.apache.fop.render.ps.PSImageHandlerGraphics2D
+org.apache.fop.render.ps.PSImageHandlerSVG
diff --git a/src/java/org/apache/fop/apps/FOUserAgent.java b/src/java/org/apache/fop/apps/FOUserAgent.java index f051dc281..ebd2e0594 100644 --- a/src/java/org/apache/fop/apps/FOUserAgent.java +++ b/src/java/org/apache/fop/apps/FOUserAgent.java @@ -46,6 +46,7 @@ import org.apache.fop.fo.FOEventHandler; import org.apache.fop.render.Renderer; import org.apache.fop.render.RendererFactory; import org.apache.fop.render.XMLHandlerRegistry; +import org.apache.fop.render.intermediate.IFDocumentHandler; /** * This is the user agent for FOP. @@ -92,6 +93,7 @@ public class FOUserAgent { private float targetResolution = FopFactoryConfigurator.DEFAULT_TARGET_RESOLUTION; private Map rendererOptions = new java.util.HashMap(); private File outputFile = null; + private IFDocumentHandler documentHandlerOverride = null; private Renderer rendererOverride = null; private FOEventHandler foEventHandlerOverride = null; private boolean locatorEnabled = true; // true by default (for error messages). @@ -116,6 +118,8 @@ public class FOUserAgent { protected String author = null; /** Title of the document. */ protected String title = null; + /** Subject of the document. */ + protected String subject = null; /** Set of keywords applicable to this document. */ protected String keywords = null; @@ -159,6 +163,24 @@ public class FOUserAgent { // ---------------------------------------------- rendering-run dependent stuff /** + * Sets an explicit document handler to use which overrides the one that would be + * selected by default. + * @param documentHandler the document handler instance to use + */ + public void setDocumentHandlerOverride(IFDocumentHandler documentHandler) { + this.documentHandlerOverride = documentHandler; + + } + + /** + * Returns the overriding {@link IFDocumentHandler} instance, if any. + * @return the overriding document handler or null + */ + public IFDocumentHandler getDocumentHandlerOverride() { + return this.documentHandlerOverride; + } + + /** * Sets an explicit renderer to use which overrides the one defined by the * render type setting. * @param renderer the Renderer instance to use @@ -274,6 +296,22 @@ public class FOUserAgent { } /** + * Sets the subject of the document. + * @param subject of document + */ + public void setSubject(String subject) { + this.subject = subject; + } + + /** + * Returns the subject of the document + * @return the subject + */ + public String getSubject() { + return subject; + } + + /** * Sets the keywords for the document. * @param keywords for the document */ diff --git a/src/java/org/apache/fop/render/RendererFactory.java b/src/java/org/apache/fop/render/RendererFactory.java index 721ffa131..19cf76931 100644 --- a/src/java/org/apache/fop/render/RendererFactory.java +++ b/src/java/org/apache/fop/render/RendererFactory.java @@ -231,7 +231,9 @@ public class RendererFactory { */ public Renderer createRenderer(FOUserAgent userAgent, String outputFormat) throws FOPException { - if (userAgent.getRendererOverride() != null) { + if (userAgent.getDocumentHandlerOverride() != null) { + return createRendererForDocumentHandler(userAgent.getDocumentHandlerOverride()); + } else if (userAgent.getRendererOverride() != null) { return userAgent.getRendererOverride(); } else { AbstractRendererMaker maker = getRendererMaker(outputFormat); @@ -247,12 +249,9 @@ public class RendererFactory { AbstractIFDocumentHandlerMaker documentHandlerMaker = getDocumentHandlerMaker(outputFormat); if (documentHandlerMaker != null) { - IFRenderer rend = new IFRenderer(); - rend.setUserAgent(userAgent); IFDocumentHandler documentHandler = createDocumentHandler( userAgent, outputFormat); - rend.setDocumentHandler(documentHandler); - return rend; + return createRendererForDocumentHandler(documentHandler); } else { throw new UnsupportedOperationException( "No renderer for the requested format available: " + outputFormat); @@ -261,6 +260,13 @@ public class RendererFactory { } } + private Renderer createRendererForDocumentHandler(IFDocumentHandler documentHandler) { + IFRenderer rend = new IFRenderer(); + rend.setUserAgent(documentHandler.getUserAgent()); + rend.setDocumentHandler(documentHandler); + return rend; + } + /** * Creates FOEventHandler instances based on the desired output. * @param userAgent the user agent for access to configuration @@ -319,23 +325,17 @@ public class RendererFactory { */ public IFDocumentHandler createDocumentHandler(FOUserAgent userAgent, String outputFormat) throws FOPException { - /* - if (userAgent.getIFDocumentHandlerOverride() != null) { - return userAgent.getIFDocumentHandlerOverride(); - } else { - */ - AbstractIFDocumentHandlerMaker maker = getDocumentHandlerMaker(outputFormat); - if (maker == null) { - throw new UnsupportedOperationException( - "No IF document handler for the requested format available: " + outputFormat); - } - IFDocumentHandler documentHandler = maker.makeIFDocumentHandler(userAgent); - IFDocumentHandlerConfigurator configurator = documentHandler.getConfigurator(); - if (configurator != null) { - configurator.configure(documentHandler); - } - return documentHandler; - //} + AbstractIFDocumentHandlerMaker maker = getDocumentHandlerMaker(outputFormat); + if (maker == null) { + throw new UnsupportedOperationException( + "No IF document handler for the requested format available: " + outputFormat); + } + IFDocumentHandler documentHandler = maker.makeIFDocumentHandler(userAgent); + IFDocumentHandlerConfigurator configurator = documentHandler.getConfigurator(); + if (configurator != null) { + configurator.configure(documentHandler); + } + return documentHandler; } /** diff --git a/src/java/org/apache/fop/render/intermediate/AbstractIFDocumentHandler.java b/src/java/org/apache/fop/render/intermediate/AbstractIFDocumentHandler.java index 4894604a2..a7b2757a8 100644 --- a/src/java/org/apache/fop/render/intermediate/AbstractIFDocumentHandler.java +++ b/src/java/org/apache/fop/render/intermediate/AbstractIFDocumentHandler.java @@ -48,10 +48,7 @@ public abstract class AbstractIFDocumentHandler implements IFDocumentHandler { this.userAgent = ua; } - /** - * Returns the user agent. - * @return the user agent - */ + /** {@inheritDoc} */ public FOUserAgent getUserAgent() { return this.userAgent; } diff --git a/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java b/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java index 07d8334d5..ea74a37c8 100644 --- a/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java +++ b/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java @@ -209,6 +209,34 @@ public abstract class AbstractIFPainter implements IFPainter { } /** + * Returns an ImageInfo instance for the given URI. If there's an error, null is returned. + * The caller can assume that any exceptions have already been handled properly. The caller + * simply skips painting anything in this case. + * @param uri the URI identifying the image + * @return the ImageInfo instance or null if there has been an error. + */ + protected ImageInfo getImageInfo(String uri) { + ImageManager manager = getFopFactory().getImageManager(); + try { + ImageSessionContext sessionContext = getUserAgent().getImageSessionContext(); + return manager.getImageInfo(uri, sessionContext); + } catch (ImageException ie) { + ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get( + getUserAgent().getEventBroadcaster()); + eventProducer.imageError(this, uri, ie, null); + } catch (FileNotFoundException fe) { + ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get( + getUserAgent().getEventBroadcaster()); + eventProducer.imageNotFound(this, uri, fe, null); + } catch (IOException ioe) { + ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get( + getUserAgent().getEventBroadcaster()); + eventProducer.imageIOError(this, uri, ioe, null); + } + return null; + } + + /** * Default drawing method for handling an image referenced by a URI. * @param uri the image's URI * @param rect the rectangle in which to paint the image diff --git a/src/java/org/apache/fop/render/intermediate/IFDocumentHandler.java b/src/java/org/apache/fop/render/intermediate/IFDocumentHandler.java index f1d6e2057..ea07deee6 100644 --- a/src/java/org/apache/fop/render/intermediate/IFDocumentHandler.java +++ b/src/java/org/apache/fop/render/intermediate/IFDocumentHandler.java @@ -82,6 +82,12 @@ public interface IFDocumentHandler { void setUserAgent(FOUserAgent userAgent); /** + * Returns the associated user agent. + * @return the user agent + */ + FOUserAgent getUserAgent(); + + /** * Sets the JAXP Result object to receive the generated content. * @param result the JAXP Result object to receive the generated content * @throws IFException if an error occurs setting up the output @@ -199,7 +205,8 @@ public interface IFDocumentHandler { * @param size the size of the page (equivalent to the MediaBox in PDF) * @throws IFException if an error occurs while handling this event */ - void startPage(int index, String name, String pageMasterName, Dimension size) throws IFException; + void startPage(int index, String name, String pageMasterName, Dimension size) + throws IFException; /** * Indicates the end of a page diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java index e44edf8af..2e3c83126 100644 --- a/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java +++ b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java @@ -234,6 +234,7 @@ class PDFRenderingUtil implements PDFConfigurationConstants { info.setCreationDate(userAgent.getCreationDate()); info.setAuthor(userAgent.getAuthor()); info.setTitle(userAgent.getTitle()); + info.setSubject(userAgent.getSubject()); info.setKeywords(userAgent.getKeywords()); } diff --git a/src/java/org/apache/fop/render/ps/PSDocumentHandler.java b/src/java/org/apache/fop/render/ps/PSDocumentHandler.java index 8223ec29b..365df2fe3 100644 --- a/src/java/org/apache/fop/render/ps/PSDocumentHandler.java +++ b/src/java/org/apache/fop/render/ps/PSDocumentHandler.java @@ -268,8 +268,9 @@ public class PSDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { in = new java.io.BufferedInputStream(in); try { try { - ResourceHandler.process(getUserAgent(), in, this.outputStream, - this.fontInfo, resTracker, this.formResources, + ResourceHandler handler = new ResourceHandler(getUserAgent(), this.fontInfo, + resTracker, this.formResources); + handler.process(in, this.outputStream, this.currentPageNumber, this.documentBoundingBox); this.outputStream.flush(); } catch (DSCException e) { @@ -519,4 +520,24 @@ public class PSDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { return res; } + /** + * Returns a PSResource instance representing a image as a PostScript form. + * @param uri the image URI + * @return a PSResource instance + */ + protected PSResource getFormForImage(String uri) { + if (uri == null || "".equals(uri)) { + throw new IllegalArgumentException("uri must not be empty or null"); + } + if (this.formResources == null) { + this.formResources = new java.util.HashMap(); + } + PSResource form = (PSResource)this.formResources.get(uri); + if (form == null) { + form = new PSImageFormResource(this.formResources.size() + 1, uri); + this.formResources.put(uri, form); + } + return form; + } + } diff --git a/src/java/org/apache/fop/render/ps/PSImageHandler.java b/src/java/org/apache/fop/render/ps/PSImageHandler.java new file mode 100644 index 000000000..ff94fdd2c --- /dev/null +++ b/src/java/org/apache/fop/render/ps/PSImageHandler.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.ps; + +import java.io.IOException; + +import org.apache.xmlgraphics.image.loader.Image; + +import org.apache.fop.render.ImageHandler; +import org.apache.fop.render.RenderingContext; + +/** + * Specialized image handler interface for PostScript output. Implementations can optionally + * support creating PostScript forms. The implementation shall check the rendering context + * to see if forms functionality is enabled in the + * {@link #isCompatible(org.apache.fop.render.RenderingContext, org.apache.xmlgraphics.image.loader.Image)} + * method. + */ +public interface PSImageHandler extends ImageHandler { + + /** + * Generates a PostScript form for the given {@link Image} instance. + * @param context the rendering context + * @param image the image to be handled + * @param form the associated form resource + * @throws IOException if an I/O error occurs + */ + void generateForm(RenderingContext context, Image image, PSImageFormResource form) + throws IOException; + +} diff --git a/src/java/org/apache/fop/render/ps/PSImageHandlerEPS.java b/src/java/org/apache/fop/render/ps/PSImageHandlerEPS.java new file mode 100644 index 000000000..b77d48162 --- /dev/null +++ b/src/java/org/apache/fop/render/ps/PSImageHandlerEPS.java @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.ps; + +import java.awt.Rectangle; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.io.IOException; +import java.io.InputStream; + +import org.apache.commons.io.IOUtils; + +import org.apache.xmlgraphics.image.loader.Image; +import org.apache.xmlgraphics.image.loader.ImageFlavor; +import org.apache.xmlgraphics.image.loader.ImageInfo; +import org.apache.xmlgraphics.image.loader.impl.ImageRawEPS; +import org.apache.xmlgraphics.ps.PSGenerator; +import org.apache.xmlgraphics.ps.PSImageUtils; + +import org.apache.fop.render.ImageHandler; +import org.apache.fop.render.RenderingContext; + +/** + * Image handler implementation which handles EPS images for PostScript output. + */ +public class PSImageHandlerEPS implements ImageHandler { + + private static final ImageFlavor[] FLAVORS = new ImageFlavor[] { + ImageFlavor.RAW_EPS + }; + + /** {@inheritDoc} */ + public void handleImage(RenderingContext context, Image image, Rectangle pos) + throws IOException { + PSRenderingContext psContext = (PSRenderingContext)context; + PSGenerator gen = psContext.getGenerator(); + ImageRawEPS eps = (ImageRawEPS)image; + + float x = (float)pos.getX() / 1000f; + float y = (float)pos.getY() / 1000f; + float w = (float)pos.getWidth() / 1000f; + float h = (float)pos.getHeight() / 1000f; + + ImageInfo info = image.getInfo(); + Rectangle2D bbox = eps.getBoundingBox(); + if (bbox == null) { + bbox = new Rectangle2D.Double(); + bbox.setFrame(new Point2D.Double(), info.getSize().getDimensionPt()); + } + InputStream in = eps.createInputStream(); + try { + String resourceName = info.getOriginalURI(); + if (resourceName == null) { + resourceName = "inline image"; + } + PSImageUtils.renderEPS(in, resourceName, + new Rectangle2D.Float(x, y, w, h), + bbox, + gen); + } finally { + IOUtils.closeQuietly(in); + } + } + + /** {@inheritDoc} */ + public int getPriority() { + return 200; + } + + /** {@inheritDoc} */ + public Class getSupportedImageClass() { + return ImageRawEPS.class; + } + + /** {@inheritDoc} */ + public ImageFlavor[] getSupportedImageFlavors() { + return FLAVORS; + } + + /** {@inheritDoc} */ + public boolean isCompatible(RenderingContext targetContext, Image image) { + if (targetContext instanceof PSRenderingContext) { + PSRenderingContext psContext = (PSRenderingContext)targetContext; + return !psContext.isCreateForms() + && (image == null || image instanceof ImageRawEPS); + } + return false; + } + +} diff --git a/src/java/org/apache/fop/render/ps/PSImageHandlerGraphics2D.java b/src/java/org/apache/fop/render/ps/PSImageHandlerGraphics2D.java new file mode 100644 index 000000000..b2934d4dd --- /dev/null +++ b/src/java/org/apache/fop/render/ps/PSImageHandlerGraphics2D.java @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.ps; + +import java.awt.Dimension; +import java.awt.Rectangle; +import java.awt.geom.AffineTransform; +import java.awt.geom.Dimension2D; +import java.awt.geom.Rectangle2D; +import java.io.IOException; + +import org.apache.xmlgraphics.image.loader.Image; +import org.apache.xmlgraphics.image.loader.ImageFlavor; +import org.apache.xmlgraphics.image.loader.ImageInfo; +import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D; +import org.apache.xmlgraphics.java2d.Graphics2DImagePainter; +import org.apache.xmlgraphics.java2d.ps.PSGraphics2D; +import org.apache.xmlgraphics.ps.FormGenerator; +import org.apache.xmlgraphics.ps.PSGenerator; +import org.apache.xmlgraphics.ps.PSProcSets; + +import org.apache.fop.render.RenderingContext; + +/** + * Image handler implementation which handles vector graphics (Java2D) for PostScript output. + */ +public class PSImageHandlerGraphics2D implements PSImageHandler { + + private static final ImageFlavor[] FLAVORS = new ImageFlavor[] { + ImageFlavor.GRAPHICS2D + }; + + /** {@inheritDoc} */ + public void handleImage(RenderingContext context, Image image, Rectangle pos) + throws IOException { + PSRenderingContext psContext = (PSRenderingContext)context; + PSGenerator gen = psContext.getGenerator(); + ImageGraphics2D imageG2D = (ImageGraphics2D)image; + Graphics2DImagePainter painter = imageG2D.getGraphics2DImagePainter(); + + float fx = (float)pos.getX() / 1000f; + float fy = (float)pos.getY() / 1000f; + float fwidth = (float)pos.getWidth() / 1000f; + float fheight = (float)pos.getHeight() / 1000f; + + // get the 'width' and 'height' attributes of the SVG document + Dimension dim = painter.getImageSize(); + float imw = (float)dim.getWidth() / 1000f; + float imh = (float)dim.getHeight() / 1000f; + + float sx = fwidth / (float)imw; + float sy = fheight / (float)imh; + + gen.commentln("%FOPBeginGraphics2D"); + gen.saveGraphicsState(); + final boolean clip = false; + if (clip) { + // Clip to the image area. + gen.writeln("newpath"); + gen.defineRect(fx, fy, fwidth, fheight); + gen.writeln("clip"); + } + + // transform so that the coordinates (0,0) is from the top left + // and positive is down and to the right. (0,0) is where the + // viewBox puts it. + gen.concatMatrix(sx, 0, 0, sy, fx, fy); + + final boolean textAsShapes = false; + PSGraphics2D graphics = new PSGraphics2D(textAsShapes, gen); + graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext()); + AffineTransform transform = new AffineTransform(); + // scale to viewbox + transform.translate(fx, fy); + gen.getCurrentState().concatMatrix(transform); + Rectangle2D area = new Rectangle2D.Double(0.0, 0.0, imw, imh); + painter.paint(graphics, area); + gen.restoreGraphicsState(); + gen.commentln("%FOPEndGraphics2D"); + } + + /** {@inheritDoc} */ + public void generateForm(RenderingContext context, Image image, PSImageFormResource form) + throws IOException { + PSRenderingContext psContext = (PSRenderingContext)context; + PSGenerator gen = psContext.getGenerator(); + final ImageGraphics2D imageG2D = (ImageGraphics2D)image; + ImageInfo info = image.getInfo(); + String imageDescription = info.getMimeType() + " " + info.getOriginalURI(); + final Dimension2D dimensionsPt = info.getSize().getDimensionPt(); + final Dimension2D dimensionsMpt = info.getSize().getDimensionMpt(); + + FormGenerator formGen = new FormGenerator( + form.getName(), imageDescription, dimensionsPt) { + + protected void generatePaintProc(PSGenerator gen) + throws IOException { + gen.getResourceTracker().notifyResourceUsageOnPage( + PSProcSets.EPS_PROCSET); + gen.writeln("BeginEPSF"); + PSGraphics2DAdapter adapter = new PSGraphics2DAdapter(gen, false); + adapter.paintImage(imageG2D.getGraphics2DImagePainter(), + null, + 0, 0, + (int)Math.round(dimensionsMpt.getWidth()), + (int)Math.round(dimensionsMpt.getHeight())); + gen.writeln("EndEPSF"); + } + + }; + formGen.generate(gen); + } + /** {@inheritDoc} */ + public int getPriority() { + return 200; + } + + /** {@inheritDoc} */ + public Class getSupportedImageClass() { + return ImageGraphics2D.class; + } + + /** {@inheritDoc} */ + public ImageFlavor[] getSupportedImageFlavors() { + return FLAVORS; + } + + /** {@inheritDoc} */ + public boolean isCompatible(RenderingContext targetContext, Image image) { + if (targetContext instanceof PSRenderingContext) { + return (image == null || image instanceof ImageGraphics2D); + } + return false; + } + +} diff --git a/src/java/org/apache/fop/render/ps/PSImageHandlerRawCCITTFax.java b/src/java/org/apache/fop/render/ps/PSImageHandlerRawCCITTFax.java new file mode 100644 index 000000000..1bbbf310c --- /dev/null +++ b/src/java/org/apache/fop/render/ps/PSImageHandlerRawCCITTFax.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.ps; + +import java.awt.Rectangle; +import java.awt.geom.Rectangle2D; +import java.io.IOException; + +import org.apache.xmlgraphics.image.loader.Image; +import org.apache.xmlgraphics.image.loader.ImageFlavor; +import org.apache.xmlgraphics.image.loader.ImageInfo; +import org.apache.xmlgraphics.image.loader.impl.ImageRawCCITTFax; +import org.apache.xmlgraphics.ps.FormGenerator; +import org.apache.xmlgraphics.ps.ImageEncoder; +import org.apache.xmlgraphics.ps.ImageFormGenerator; +import org.apache.xmlgraphics.ps.PSGenerator; +import org.apache.xmlgraphics.ps.PSImageUtils; + +import org.apache.fop.render.RenderingContext; + +/** + * Image handler implementation which handles CCITT fax images for PostScript output. + */ +public class PSImageHandlerRawCCITTFax implements PSImageHandler { + + private static final ImageFlavor[] FLAVORS = new ImageFlavor[] { + ImageFlavor.RAW_CCITTFAX + }; + + /** {@inheritDoc} */ + public void handleImage(RenderingContext context, Image image, Rectangle pos) + throws IOException { + PSRenderingContext psContext = (PSRenderingContext)context; + PSGenerator gen = psContext.getGenerator(); + ImageRawCCITTFax ccitt = (ImageRawCCITTFax)image; + + float x = (float)pos.getX() / 1000f; + float y = (float)pos.getY() / 1000f; + float w = (float)pos.getWidth() / 1000f; + float h = (float)pos.getHeight() / 1000f; + Rectangle2D targetRect = new Rectangle2D.Float( + x, y, w, h); + + ImageInfo info = image.getInfo(); + + ImageEncoder encoder = new ImageEncoderCCITTFax(ccitt); + PSImageUtils.writeImage(encoder, info.getSize().getDimensionPx(), + info.getOriginalURI(), targetRect, + ccitt.getColorSpace(), 1, false, gen); + } + + /** {@inheritDoc} */ + public void generateForm(RenderingContext context, Image image, PSImageFormResource form) + throws IOException { + PSRenderingContext psContext = (PSRenderingContext)context; + PSGenerator gen = psContext.getGenerator(); + ImageRawCCITTFax ccitt = (ImageRawCCITTFax)image; + ImageInfo info = image.getInfo(); + String imageDescription = info.getMimeType() + " " + info.getOriginalURI(); + + ImageEncoder encoder = new ImageEncoderCCITTFax(ccitt); + FormGenerator formGen = new ImageFormGenerator( + form.getName(), imageDescription, + info.getSize().getDimensionPt(), + info.getSize().getDimensionPx(), + encoder, + ccitt.getColorSpace(), 1, false); + formGen.generate(gen); + } + + /** {@inheritDoc} */ + public int getPriority() { + return 200; + } + + /** {@inheritDoc} */ + public Class getSupportedImageClass() { + return ImageRawCCITTFax.class; + } + + /** {@inheritDoc} */ + public ImageFlavor[] getSupportedImageFlavors() { + return FLAVORS; + } + + /** {@inheritDoc} */ + public boolean isCompatible(RenderingContext targetContext, Image image) { + if (targetContext instanceof PSRenderingContext) { + PSRenderingContext psContext = (PSRenderingContext)targetContext; + //The filters required for this implementation need PS level 2 or higher + if (psContext.getGenerator().getPSLevel() >= 2) { + return (image == null || image instanceof ImageRawCCITTFax); + } + } + return false; + } + +} diff --git a/src/java/org/apache/fop/render/ps/PSImageHandlerRawJPEG.java b/src/java/org/apache/fop/render/ps/PSImageHandlerRawJPEG.java new file mode 100644 index 000000000..deedf3bc4 --- /dev/null +++ b/src/java/org/apache/fop/render/ps/PSImageHandlerRawJPEG.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.ps; + +import java.awt.Rectangle; +import java.awt.geom.Rectangle2D; +import java.io.IOException; + +import org.apache.xmlgraphics.image.loader.Image; +import org.apache.xmlgraphics.image.loader.ImageFlavor; +import org.apache.xmlgraphics.image.loader.ImageInfo; +import org.apache.xmlgraphics.image.loader.impl.ImageRawJPEG; +import org.apache.xmlgraphics.ps.FormGenerator; +import org.apache.xmlgraphics.ps.ImageEncoder; +import org.apache.xmlgraphics.ps.ImageFormGenerator; +import org.apache.xmlgraphics.ps.PSGenerator; +import org.apache.xmlgraphics.ps.PSImageUtils; + +import org.apache.fop.render.RenderingContext; + +/** + * Image handler implementation which handles undecoded JPEG images for PostScript output. + */ +public class PSImageHandlerRawJPEG implements PSImageHandler { + + private static final ImageFlavor[] FLAVORS = new ImageFlavor[] { + ImageFlavor.RAW_JPEG + }; + + /** {@inheritDoc} */ + public void handleImage(RenderingContext context, Image image, Rectangle pos) + throws IOException { + PSRenderingContext psContext = (PSRenderingContext)context; + PSGenerator gen = psContext.getGenerator(); + ImageRawJPEG jpeg = (ImageRawJPEG)image; + + float x = (float)pos.getX() / 1000f; + float y = (float)pos.getY() / 1000f; + float w = (float)pos.getWidth() / 1000f; + float h = (float)pos.getHeight() / 1000f; + Rectangle2D targetRect = new Rectangle2D.Float( + x, y, w, h); + + ImageInfo info = image.getInfo(); + + ImageEncoder encoder = new ImageEncoderJPEG(jpeg); + PSImageUtils.writeImage(encoder, info.getSize().getDimensionPx(), + info.getOriginalURI(), targetRect, + jpeg.getColorSpace(), 8, jpeg.isInverted(), gen); + } + + /** {@inheritDoc} */ + public void generateForm(RenderingContext context, Image image, PSImageFormResource form) + throws IOException { + PSRenderingContext psContext = (PSRenderingContext)context; + PSGenerator gen = psContext.getGenerator(); + ImageRawJPEG jpeg = (ImageRawJPEG)image; + ImageInfo info = image.getInfo(); + String imageDescription = info.getMimeType() + " " + info.getOriginalURI(); + + ImageEncoder encoder = new ImageEncoderJPEG(jpeg); + FormGenerator formGen = new ImageFormGenerator( + form.getName(), imageDescription, + info.getSize().getDimensionPt(), + info.getSize().getDimensionPx(), + encoder, + jpeg.getColorSpace(), jpeg.isInverted()); + formGen.generate(gen); + } + + /** {@inheritDoc} */ + public int getPriority() { + return 200; + } + + /** {@inheritDoc} */ + public Class getSupportedImageClass() { + return ImageRawJPEG.class; + } + + /** {@inheritDoc} */ + public ImageFlavor[] getSupportedImageFlavors() { + return FLAVORS; + } + + /** {@inheritDoc} */ + public boolean isCompatible(RenderingContext targetContext, Image image) { + if (targetContext instanceof PSRenderingContext) { + PSRenderingContext psContext = (PSRenderingContext)targetContext; + //The filters required for this implementation need PS level 2 or higher + if (psContext.getGenerator().getPSLevel() >= 2) { + return (image == null || image instanceof ImageRawJPEG); + } + } + return false; + } + +} diff --git a/src/java/org/apache/fop/render/ps/PSImageHandlerRenderedImage.java b/src/java/org/apache/fop/render/ps/PSImageHandlerRenderedImage.java new file mode 100644 index 000000000..5a13c1c8e --- /dev/null +++ b/src/java/org/apache/fop/render/ps/PSImageHandlerRenderedImage.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.ps; + +import java.awt.Rectangle; +import java.awt.image.RenderedImage; +import java.io.IOException; + +import org.apache.xmlgraphics.image.loader.Image; +import org.apache.xmlgraphics.image.loader.ImageFlavor; +import org.apache.xmlgraphics.image.loader.ImageInfo; +import org.apache.xmlgraphics.image.loader.impl.ImageRendered; +import org.apache.xmlgraphics.ps.FormGenerator; +import org.apache.xmlgraphics.ps.ImageFormGenerator; +import org.apache.xmlgraphics.ps.PSGenerator; +import org.apache.xmlgraphics.ps.PSImageUtils; + +import org.apache.fop.render.RenderingContext; + +/** + * Image handler implementation which handles RenderedImage instances for PostScript output. + */ +public class PSImageHandlerRenderedImage implements PSImageHandler { + + private static final ImageFlavor[] FLAVORS = new ImageFlavor[] { + ImageFlavor.BUFFERED_IMAGE, + ImageFlavor.RENDERED_IMAGE + }; + + /** {@inheritDoc} */ + public void handleImage(RenderingContext context, Image image, Rectangle pos) + throws IOException { + PSRenderingContext psContext = (PSRenderingContext)context; + PSGenerator gen = psContext.getGenerator(); + ImageRendered imageRend = (ImageRendered)image; + + float x = (float)pos.getX() / 1000f; + float y = (float)pos.getY() / 1000f; + float w = (float)pos.getWidth() / 1000f; + float h = (float)pos.getHeight() / 1000f; + + RenderedImage ri = imageRend.getRenderedImage(); + PSImageUtils.renderBitmapImage(ri, x, y, w, h, gen); + } + + /** {@inheritDoc} */ + public void generateForm(RenderingContext context, Image image, PSImageFormResource form) + throws IOException { + PSRenderingContext psContext = (PSRenderingContext)context; + PSGenerator gen = psContext.getGenerator(); + ImageRendered imageRend = (ImageRendered)image; + ImageInfo info = image.getInfo(); + String imageDescription = info.getMimeType() + " " + info.getOriginalURI(); + + RenderedImage ri = imageRend.getRenderedImage(); + FormGenerator formGen = new ImageFormGenerator( + form.getName(), imageDescription, + info.getSize().getDimensionPt(), + ri, false); + formGen.generate(gen); + } + + /** {@inheritDoc} */ + public int getPriority() { + return 300; + } + + /** {@inheritDoc} */ + public Class getSupportedImageClass() { + return ImageRendered.class; + } + + /** {@inheritDoc} */ + public ImageFlavor[] getSupportedImageFlavors() { + return FLAVORS; + } + + /** {@inheritDoc} */ + public boolean isCompatible(RenderingContext targetContext, Image image) { + return (image == null || image instanceof ImageRendered) + && targetContext instanceof PSRenderingContext; + } + +} diff --git a/src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java b/src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java new file mode 100644 index 000000000..2138e3cf3 --- /dev/null +++ b/src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java @@ -0,0 +1,161 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.ps; + +import java.awt.Rectangle; +import java.awt.geom.AffineTransform; +import java.io.IOException; + +import org.apache.batik.bridge.BridgeContext; +import org.apache.batik.bridge.GVTBuilder; +import org.apache.batik.gvt.GraphicsNode; + +import org.apache.xmlgraphics.image.loader.Image; +import org.apache.xmlgraphics.image.loader.ImageFlavor; +import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM; +import org.apache.xmlgraphics.java2d.ps.PSGraphics2D; +import org.apache.xmlgraphics.ps.PSGenerator; + +import org.apache.fop.image.loader.batik.BatikImageFlavors; +import org.apache.fop.render.ImageHandler; +import org.apache.fop.render.RenderingContext; +import org.apache.fop.svg.SVGEventProducer; +import org.apache.fop.svg.SVGUserAgent; + +/** + * Image handler implementation which handles SVG images for PostScript output. + */ +public class PSImageHandlerSVG implements ImageHandler { + + private static final ImageFlavor[] FLAVORS = new ImageFlavor[] { + BatikImageFlavors.SVG_DOM + }; + + /** {@inheritDoc} */ + public void handleImage(RenderingContext context, Image image, Rectangle pos) + throws IOException { + PSRenderingContext psContext = (PSRenderingContext)context; + PSGenerator gen = psContext.getGenerator(); + ImageXMLDOM imageSVG = (ImageXMLDOM)image; + + //Controls whether text painted by Batik is generated using text or path operations + boolean strokeText = false; + //TODO Configure text stroking + + SVGUserAgent ua + = new SVGUserAgent(context.getUserAgent(), new AffineTransform()); + + PSGraphics2D graphics = new PSGraphics2D(strokeText, gen); + graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext()); + + GVTBuilder builder = new GVTBuilder(); + NativeTextHandler nativeTextHandler = null; + BridgeContext ctx = new BridgeContext(ua); + if (!strokeText) { + nativeTextHandler = new NativeTextHandler(graphics, psContext.getFontInfo()); + graphics.setCustomTextHandler(nativeTextHandler); + PSTextPainter textPainter = new PSTextPainter(nativeTextHandler); + ctx.setTextPainter(textPainter); + PSTextElementBridge tBridge = new PSTextElementBridge(textPainter); + ctx.putBridge(tBridge); + } + + GraphicsNode root; + try { + root = builder.build(ctx, imageSVG.getDocument()); + } catch (Exception e) { + SVGEventProducer eventProducer = SVGEventProducer.Provider.get( + context.getUserAgent().getEventBroadcaster()); + eventProducer.svgNotBuilt(this, e, image.getInfo().getOriginalURI()); + return; + } + // get the 'width' and 'height' attributes of the SVG document + float w = (float)ctx.getDocumentSize().getWidth() * 1000f; + float h = (float)ctx.getDocumentSize().getHeight() * 1000f; + + float sx = pos.width / w; + float sy = pos.height / h; + + ctx = null; + builder = null; + + gen.commentln("%FOPBeginSVG"); + gen.saveGraphicsState(); + final boolean clip = false; + if (clip) { + /* + * Clip to the svg area. + * Note: To have the svg overlay (under) a text area then use + * an fo:block-container + */ + gen.writeln("newpath"); + gen.defineRect(pos.getMinX() / 1000f, pos.getMinY() / 1000f, + pos.width / 1000f, pos.height / 1000f); + gen.writeln("clip"); + } + + // transform so that the coordinates (0,0) is from the top left + // and positive is down and to the right. (0,0) is where the + // viewBox puts it. + gen.concatMatrix(sx, 0, 0, sy, pos.getMinX() / 1000f, pos.getMinY() / 1000f); + + AffineTransform transform = new AffineTransform(); + // scale to viewbox + transform.translate(pos.getMinX(), pos.getMinY()); + gen.getCurrentState().concatMatrix(transform); + try { + root.paint(graphics); + } catch (Exception e) { + SVGEventProducer eventProducer = SVGEventProducer.Provider.get( + context.getUserAgent().getEventBroadcaster()); + eventProducer.svgRenderingError(this, e, image.getInfo().getOriginalURI()); + } + + gen.restoreGraphicsState(); + gen.commentln("%FOPEndSVG"); + } + + /** {@inheritDoc} */ + public int getPriority() { + return 400; + } + + /** {@inheritDoc} */ + public Class getSupportedImageClass() { + return ImageXMLDOM.class; + } + + /** {@inheritDoc} */ + public ImageFlavor[] getSupportedImageFlavors() { + return FLAVORS; + } + + /** {@inheritDoc} */ + public boolean isCompatible(RenderingContext targetContext, Image image) { + if (targetContext instanceof PSRenderingContext) { + PSRenderingContext psContext = (PSRenderingContext)targetContext; + return !psContext.isCreateForms() + && (image == null || (image instanceof ImageXMLDOM + && image.getFlavor().isCompatible(BatikImageFlavors.SVG_DOM))); + } + return false; + } + +} diff --git a/src/java/org/apache/fop/render/ps/PSImageUtils.java b/src/java/org/apache/fop/render/ps/PSImageUtils.java index 7a011fbec..431071701 100644 --- a/src/java/org/apache/fop/render/ps/PSImageUtils.java +++ b/src/java/org/apache/fop/render/ps/PSImageUtils.java @@ -19,11 +19,97 @@ package org.apache.fop.render.ps; +import java.awt.Rectangle; +import java.awt.geom.Rectangle2D; +import java.io.IOException; + +import org.apache.xmlgraphics.image.loader.ImageFlavor; +import org.apache.xmlgraphics.image.loader.ImageInfo; +import org.apache.xmlgraphics.image.loader.ImageManager; +import org.apache.xmlgraphics.image.loader.pipeline.ImageProviderPipeline; +import org.apache.xmlgraphics.ps.DSCConstants; +import org.apache.xmlgraphics.ps.PSGenerator; +import org.apache.xmlgraphics.ps.PSResource; + +import org.apache.fop.render.ImageHandlerRegistry; +import org.apache.fop.render.RenderingContext; + /** * Utility code for rendering images in PostScript. - * @deprecated Kept for compatibility with older FOP extensions (like Barcode4J). Use the - * super-class instead. */ public class PSImageUtils extends org.apache.xmlgraphics.ps.PSImageUtils { + /** + * Indicates whether the given image (identified by an {@link ImageInfo} object) shall be + * inlined rather than generated as a PostScript form. + * @param info the info object for the image + * @param renderingContext the rendering context + * @return true if the image shall be inlined, false if forms shall be used. + */ + public static boolean isImageInlined(ImageInfo info, PSRenderingContext renderingContext) { + String uri = info.getOriginalURI(); + if (uri == null || "".equals(uri)) { + return true; + } + //Investigate choice for inline mode + ImageFlavor[] inlineFlavors = determineSupportedImageFlavors(renderingContext); + ImageManager manager = renderingContext.getUserAgent().getFactory().getImageManager(); + ImageProviderPipeline[] inlineCandidates + = manager.getPipelineFactory().determineCandidatePipelines( + info, inlineFlavors); + ImageProviderPipeline inlineChoice = manager.choosePipeline(inlineCandidates); + ImageFlavor inlineFlavor = (inlineChoice != null + ? inlineChoice.getTargetFlavor() : null); + + //Create a rendering context for form creation + PSRenderingContext formContext = renderingContext.toFormContext(); + + //Investigate choice for form mode + ImageFlavor[] formFlavors = determineSupportedImageFlavors(formContext); + ImageProviderPipeline[] formCandidates + = manager.getPipelineFactory().determineCandidatePipelines( + info, formFlavors); + ImageProviderPipeline formChoice = manager.choosePipeline(formCandidates); + ImageFlavor formFlavor = (formChoice != null ? formChoice.getTargetFlavor() : null); + + //Inline if form is not supported or if a better choice is available with inline mode + return formFlavor == null || !formFlavor.equals(inlineFlavor); + } + + private static ImageFlavor[] determineSupportedImageFlavors(RenderingContext renderingContext) { + ImageFlavor[] inlineFlavors; + ImageHandlerRegistry imageHandlerRegistry + = renderingContext.getUserAgent().getFactory().getImageHandlerRegistry(); + inlineFlavors = imageHandlerRegistry.getSupportedFlavors(renderingContext); + return inlineFlavors; + } + + /** + * Draws a form at a given location. + * @param form the form resource + * @param info the image info object representing the image in the form + * @param rect the target rectangle (coordinates in millipoints) + * @param generator the PostScript generator + * @throws IOException if an I/O error occurs + */ + public static void drawForm(PSResource form, ImageInfo info, Rectangle rect, + PSGenerator generator) throws IOException { + Rectangle2D targetRect = new Rectangle2D.Double( + rect.getMinX() / 1000.0, + rect.getMinY() / 1000.0, + rect.getWidth() / 1000.0, + rect.getHeight() / 1000.0); + generator.saveGraphicsState(); + translateAndScale(generator, + info.getSize().getDimensionPt(), targetRect); + + //The following %%IncludeResource marker is needed later by ResourceHandler! + generator.writeDSCComment(DSCConstants.INCLUDE_RESOURCE, form); + generator.getResourceTracker().notifyResourceUsageOnPage(form); + + generator.writeln(form.getName() + " execform"); + generator.restoreGraphicsState(); + } + + } diff --git a/src/java/org/apache/fop/render/ps/PSPainter.java b/src/java/org/apache/fop/render/ps/PSPainter.java index e43b0f2c4..3c937addf 100644 --- a/src/java/org/apache/fop/render/ps/PSPainter.java +++ b/src/java/org/apache/fop/render/ps/PSPainter.java @@ -33,6 +33,10 @@ import org.w3c.dom.Document; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.xmlgraphics.image.loader.ImageException; +import org.apache.xmlgraphics.image.loader.ImageInfo; +import org.apache.xmlgraphics.image.loader.ImageProcessingHints; +import org.apache.xmlgraphics.image.loader.ImageSessionContext; import org.apache.xmlgraphics.ps.PSGenerator; import org.apache.xmlgraphics.ps.PSResource; @@ -135,19 +139,57 @@ public class PSPainter extends AbstractIFPainter { } /** {@inheritDoc} */ - public void drawImage(String uri, Rectangle rect, Map foreignAttributes) throws IFException { - //TODO Implement me + protected Map createDefaultImageProcessingHints(ImageSessionContext sessionContext) { + Map hints = super.createDefaultImageProcessingHints(sessionContext); + + //PostScript doesn't support alpha channels + hints.put(ImageProcessingHints.TRANSPARENCY_INTENT, + ImageProcessingHints.TRANSPARENCY_INTENT_IGNORE); + //TODO We might want to support image masks in the future. + return hints; } /** {@inheritDoc} */ protected RenderingContext createRenderingContext() { PSRenderingContext psContext = new PSRenderingContext( - getUserAgent(), getFontInfo()); + getUserAgent(), getGenerator(), getFontInfo()); return psContext; } /** {@inheritDoc} */ + protected void drawImageUsingImageHandler(ImageInfo info, Rectangle rect) + throws ImageException, IOException { + if (!getPSUtil().isOptimizeResources() + || PSImageUtils.isImageInlined(info, + (PSRenderingContext)createRenderingContext())) { + super.drawImageUsingImageHandler(info, rect); + } else { + if (log.isDebugEnabled()) { + log.debug("Image " + info + " is embedded as a form later"); + } + //Don't load image at this time, just put a form placeholder in the stream + PSResource form = documentHandler.getFormForImage(info.getOriginalURI()); + PSImageUtils.drawForm(form, info, rect, getGenerator()); + } + } + + /** {@inheritDoc} */ + public void drawImage(String uri, Rectangle rect, Map foreignAttributes) throws IFException { + try { + endTextObject(); + } catch (IOException ioe) { + throw new IFException("I/O error in drawImage()", ioe); + } + drawImageUsingURI(uri, rect); + } + + /** {@inheritDoc} */ public void drawImage(Document doc, Rectangle rect, Map foreignAttributes) throws IFException { + try { + endTextObject(); + } catch (IOException ioe) { + throw new IFException("I/O error in drawImage()", ioe); + } drawImageUsingDocument(doc, rect); } diff --git a/src/java/org/apache/fop/render/ps/PSRenderer.java b/src/java/org/apache/fop/render/ps/PSRenderer.java index 83dbd4b62..55f714f31 100644 --- a/src/java/org/apache/fop/render/ps/PSRenderer.java +++ b/src/java/org/apache/fop/render/ps/PSRenderer.java @@ -21,6 +21,7 @@ package org.apache.fop.render.ps; // Java import java.awt.Color; +import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.RenderedImage; @@ -36,6 +37,7 @@ import java.util.Map; import javax.xml.transform.Source; +import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -45,21 +47,11 @@ import org.apache.xmlgraphics.image.loader.ImageFlavor; import org.apache.xmlgraphics.image.loader.ImageInfo; import org.apache.xmlgraphics.image.loader.ImageManager; import org.apache.xmlgraphics.image.loader.ImageSessionContext; -import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D; -import org.apache.xmlgraphics.image.loader.impl.ImageRawCCITTFax; -import org.apache.xmlgraphics.image.loader.impl.ImageRawEPS; -import org.apache.xmlgraphics.image.loader.impl.ImageRawJPEG; -import org.apache.xmlgraphics.image.loader.impl.ImageRawStream; -import org.apache.xmlgraphics.image.loader.impl.ImageRendered; -import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM; -import org.apache.xmlgraphics.image.loader.pipeline.ImageProviderPipeline; import org.apache.xmlgraphics.image.loader.util.ImageUtil; import org.apache.xmlgraphics.ps.DSCConstants; -import org.apache.xmlgraphics.ps.ImageEncoder; import org.apache.xmlgraphics.ps.PSDictionary; import org.apache.xmlgraphics.ps.PSDictionaryFormatException; import org.apache.xmlgraphics.ps.PSGenerator; -import org.apache.xmlgraphics.ps.PSImageUtils; import org.apache.xmlgraphics.ps.PSPageDeviceDictionary; import org.apache.xmlgraphics.ps.PSProcSets; import org.apache.xmlgraphics.ps.PSResource; @@ -97,6 +89,8 @@ import org.apache.fop.fonts.Typeface; import org.apache.fop.render.AbstractPathOrientedRenderer; import org.apache.fop.render.Graphics2DAdapter; import org.apache.fop.render.ImageAdapter; +import org.apache.fop.render.ImageHandler; +import org.apache.fop.render.ImageHandlerRegistry; import org.apache.fop.render.RendererContext; import org.apache.fop.render.RendererEventProducer; import org.apache.fop.render.ps.extensions.PSCommentAfter; @@ -350,50 +344,6 @@ public class PSRenderer extends AbstractPathOrientedRenderer } } - /** - * Indicates whether an image should be inlined or added as a PostScript form. - * @param uri the URI of the image - * @return true if the image should be inlined rather than added as a form - */ - protected boolean isImageInlined(String uri) { - return !isOptimizeResources() || uri == null || "".equals(uri); - } - - /** - * Indicates whether an image should be inlined or added as a PostScript form. - * @param info the ImageInfo object of the image - * @return true if the image should be inlined rather than added as a form - */ - protected boolean isImageInlined(ImageInfo info) { - if (isImageInlined(info.getOriginalURI())) { - return true; - } - - if (!isOptimizeResources()) { - throw new IllegalStateException("Must not get here if form support is enabled"); - } - - //Investigate choice for inline mode - ImageFlavor[] inlineFlavors = getInlineFlavors(); - ImageManager manager = getUserAgent().getFactory().getImageManager(); - ImageProviderPipeline[] inlineCandidates - = manager.getPipelineFactory().determineCandidatePipelines( - info, inlineFlavors); - ImageProviderPipeline inlineChoice = manager.choosePipeline(inlineCandidates); - ImageFlavor inlineFlavor = (inlineChoice != null ? inlineChoice.getTargetFlavor() : null); - - //Investigate choice for form mode - ImageFlavor[] formFlavors = getFormFlavors(); - ImageProviderPipeline[] formCandidates - = manager.getPipelineFactory().determineCandidatePipelines( - info, formFlavors); - ImageProviderPipeline formChoice = manager.choosePipeline(formCandidates); - ImageFlavor formFlavor = (formChoice != null ? formChoice.getTargetFlavor() : null); - - //Inline if form is not supported or if a better choice is available with inline mode - return formFlavor == null || !formFlavor.equals(inlineFlavor); - } - /** {@inheritDoc} */ protected void drawImage(String uri, Rectangle2D pos, Map foreignAttributes) { endTextObject(); @@ -403,89 +353,48 @@ public class PSRenderer extends AbstractPathOrientedRenderer if (log.isDebugEnabled()) { log.debug("Handling image: " + uri); } + int width = (int)pos.getWidth(); + int height = (int)pos.getHeight(); + Rectangle targetRect = new Rectangle(x, y, width, height); ImageManager manager = getUserAgent().getFactory().getImageManager(); ImageInfo info = null; try { ImageSessionContext sessionContext = getUserAgent().getImageSessionContext(); info = manager.getImageInfo(uri, sessionContext); - int width = (int)pos.getWidth(); - int height = (int)pos.getHeight(); - //millipoints --> points for PostScript - float ptx = x / 1000f; - float pty = y / 1000f; - float ptw = width / 1000f; - float pth = height / 1000f; + PSRenderingContext renderingContext = new PSRenderingContext( + getUserAgent(), gen, getFontInfo()); - if (isImageInlined(info)) { + if (!isOptimizeResources() + || PSImageUtils.isImageInlined(info, renderingContext)) { if (log.isDebugEnabled()) { log.debug("Image " + info + " is inlined"); } + + //Determine supported flavors + ImageFlavor[] flavors; + ImageHandlerRegistry imageHandlerRegistry + = userAgent.getFactory().getImageHandlerRegistry(); + flavors = imageHandlerRegistry.getSupportedFlavors(renderingContext); + //Only now fully load/prepare the image Map hints = ImageUtil.getDefaultHints(sessionContext); org.apache.xmlgraphics.image.loader.Image img = manager.getImage( - info, getInlineFlavors(), hints, sessionContext); + info, flavors, hints, sessionContext); + + //Get handler for image + ImageHandler basicHandler = imageHandlerRegistry.getHandler(renderingContext, img); //...and embed as inline image - if (img instanceof ImageGraphics2D) { - ImageGraphics2D imageG2D = (ImageGraphics2D)img; - RendererContext context = createRendererContext( - x, y, width, height, foreignAttributes); - getGraphics2DAdapter().paintImage(imageG2D.getGraphics2DImagePainter(), - context, x, y, width, height); - } else if (img instanceof ImageRendered) { - ImageRendered imgRend = (ImageRendered)img; - RenderedImage ri = imgRend.getRenderedImage(); - PSImageUtils.renderBitmapImage(ri, ptx, pty, ptw, pth, gen); - } else if (img instanceof ImageXMLDOM) { - ImageXMLDOM imgXML = (ImageXMLDOM)img; - renderDocument(imgXML.getDocument(), imgXML.getRootNamespace(), - pos, foreignAttributes); - } else if (img instanceof ImageRawStream) { - final ImageRawStream raw = (ImageRawStream)img; - if (raw instanceof ImageRawEPS) { - ImageRawEPS eps = (ImageRawEPS)raw; - Rectangle2D bbox = eps.getBoundingBox(); - InputStream in = raw.createInputStream(); - try { - PSImageUtils.renderEPS(in, uri, - new Rectangle2D.Float(ptx, pty, ptw, pth), - bbox, - gen); - } finally { - IOUtils.closeQuietly(in); - } - } else if (raw instanceof ImageRawCCITTFax) { - final ImageRawCCITTFax ccitt = (ImageRawCCITTFax)raw; - ImageEncoder encoder = new ImageEncoderCCITTFax(ccitt); - Rectangle2D targetRect = new Rectangle2D.Float( - ptx, pty, ptw, pth); - PSImageUtils.writeImage(encoder, info.getSize().getDimensionPx(), - uri, targetRect, - ccitt.getColorSpace(), 1, false, gen); - } else if (raw instanceof ImageRawJPEG) { - ImageRawJPEG jpeg = (ImageRawJPEG)raw; - ImageEncoder encoder = new ImageEncoderJPEG(jpeg); - Rectangle2D targetRect = new Rectangle2D.Float( - ptx, pty, ptw, pth); - PSImageUtils.writeImage(encoder, info.getSize().getDimensionPx(), - uri, targetRect, - jpeg.getColorSpace(), 8, jpeg.isInverted(), gen); - } else { - throw new UnsupportedOperationException("Unsupported raw image: " + info); - } - } else { - throw new UnsupportedOperationException("Unsupported image type: " + img); - } + basicHandler.handleImage(renderingContext, img, targetRect); } else { if (log.isDebugEnabled()) { log.debug("Image " + info + " is embedded as a form later"); } //Don't load image at this time, just put a form placeholder in the stream - PSResource form = getFormForImage(uri); - Rectangle2D targetRect = new Rectangle2D.Double(ptx, pty, ptw, pth); - PSImageUtils.paintForm(form, info.getSize().getDimensionPt(), targetRect, gen); + PSResource form = getFormForImage(info.getOriginalURI()); + PSImageUtils.drawForm(form, info, targetRect, gen); } } catch (ImageException ie) { @@ -503,26 +412,6 @@ public class PSRenderer extends AbstractPathOrientedRenderer } } - private ImageFlavor[] getInlineFlavors() { - ImageFlavor[] flavors; - if (gen.getPSLevel() >= 3) { - flavors = LEVEL_3_FLAVORS_INLINE; - } else { - flavors = LEVEL_2_FLAVORS_INLINE; - } - return flavors; - } - - private ImageFlavor[] getFormFlavors() { - ImageFlavor[] flavors; - if (gen.getPSLevel() >= 3) { - flavors = LEVEL_3_FLAVORS_FORM; - } else { - flavors = LEVEL_2_FLAVORS_FORM; - } - return flavors; - } - /** * Returns a PSResource instance representing a image as a PostScript form. * @param uri the image URI @@ -978,8 +867,9 @@ public class PSRenderer extends AbstractPathOrientedRenderer in = new java.io.BufferedInputStream(in); try { try { - ResourceHandler.process(this.userAgent, in, this.outputStream, - this.fontInfo, resTracker, this.formResources, + ResourceHandler handler = new ResourceHandler(this.userAgent, this.fontInfo, + resTracker, this.formResources); + handler.process(in, this.outputStream, this.currentPageNumber, this.documentBoundingBox); this.outputStream.flush(); } catch (DSCException e) { diff --git a/src/java/org/apache/fop/render/ps/PSRenderingContext.java b/src/java/org/apache/fop/render/ps/PSRenderingContext.java index 63f00ea1a..b34874ffa 100644 --- a/src/java/org/apache/fop/render/ps/PSRenderingContext.java +++ b/src/java/org/apache/fop/render/ps/PSRenderingContext.java @@ -19,6 +19,7 @@ package org.apache.fop.render.ps; +import org.apache.xmlgraphics.ps.PSGenerator; import org.apache.xmlgraphics.util.MimeConstants; import org.apache.fop.apps.FOUserAgent; @@ -30,17 +31,34 @@ import org.apache.fop.render.AbstractRenderingContext; */ public class PSRenderingContext extends AbstractRenderingContext { + private PSGenerator gen; private FontInfo fontInfo; + private boolean createForms; /** * Main constructor. * @param userAgent the user agent + * @param gen the PostScript generator * @param fontInfo the font list */ public PSRenderingContext(FOUserAgent userAgent, - FontInfo fontInfo) { + PSGenerator gen, FontInfo fontInfo) { + this(userAgent, gen, fontInfo, false); + } + + /** + * Special constructor. + * @param userAgent the user agent + * @param gen the PostScript generator + * @param fontInfo the font list + * @param createForms true if form generation mode should be enabled + */ + public PSRenderingContext(FOUserAgent userAgent, + PSGenerator gen, FontInfo fontInfo, boolean createForms) { super(userAgent); + this.gen = gen; this.fontInfo = fontInfo; + this.createForms = createForms; } /** {@inheritDoc} */ @@ -49,6 +67,14 @@ public class PSRenderingContext extends AbstractRenderingContext { } /** + * Returns the PostScript generator. + * @return the PostScript generator + */ + public PSGenerator getGenerator() { + return this.gen; + } + + /** * Returns the font list. * @return the font list */ @@ -56,4 +82,21 @@ public class PSRenderingContext extends AbstractRenderingContext { return this.fontInfo; } + /** + * Indicates whether PS forms should be created for the images instead of inline images. + * Note that not all image handlers will support this! + * @return true if PS forms shall be created + */ + public boolean isCreateForms() { + return this.createForms; + } + + /** + * Create a copy of this rendering context and activate form mode. + * @return the form-enabled rendering context + */ + public PSRenderingContext toFormContext() { + return new PSRenderingContext(getUserAgent(), getGenerator(), getFontInfo(), true); + } + } diff --git a/src/java/org/apache/fop/render/ps/ResourceHandler.java b/src/java/org/apache/fop/render/ps/ResourceHandler.java index bdd305164..0b2f174f5 100644 --- a/src/java/org/apache/fop/render/ps/ResourceHandler.java +++ b/src/java/org/apache/fop/render/ps/ResourceHandler.java @@ -21,7 +21,6 @@ package org.apache.fop.render.ps; import java.awt.geom.Dimension2D; import java.awt.geom.Rectangle2D; -import java.awt.image.RenderedImage; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -29,27 +28,22 @@ import java.util.Iterator; import java.util.Map; import java.util.Set; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + import org.apache.xmlgraphics.image.loader.ImageException; import org.apache.xmlgraphics.image.loader.ImageFlavor; import org.apache.xmlgraphics.image.loader.ImageInfo; import org.apache.xmlgraphics.image.loader.ImageManager; import org.apache.xmlgraphics.image.loader.ImageSessionContext; -import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D; -import org.apache.xmlgraphics.image.loader.impl.ImageRawCCITTFax; -import org.apache.xmlgraphics.image.loader.impl.ImageRawEPS; -import org.apache.xmlgraphics.image.loader.impl.ImageRawJPEG; -import org.apache.xmlgraphics.image.loader.impl.ImageRawStream; -import org.apache.xmlgraphics.image.loader.impl.ImageRendered; -import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM; import org.apache.xmlgraphics.image.loader.util.ImageUtil; import org.apache.xmlgraphics.ps.DSCConstants; import org.apache.xmlgraphics.ps.FormGenerator; -import org.apache.xmlgraphics.ps.ImageEncoder; -import org.apache.xmlgraphics.ps.ImageFormGenerator; import org.apache.xmlgraphics.ps.PSGenerator; -import org.apache.xmlgraphics.ps.PSProcSets; +import org.apache.xmlgraphics.ps.PSResource; import org.apache.xmlgraphics.ps.dsc.DSCException; import org.apache.xmlgraphics.ps.dsc.DSCFilter; +import org.apache.xmlgraphics.ps.dsc.DSCListener; import org.apache.xmlgraphics.ps.dsc.DSCParser; import org.apache.xmlgraphics.ps.dsc.DSCParserConstants; import org.apache.xmlgraphics.ps.dsc.DefaultNestedDocumentHandler; @@ -59,17 +53,21 @@ import org.apache.xmlgraphics.ps.dsc.events.DSCCommentBoundingBox; import org.apache.xmlgraphics.ps.dsc.events.DSCCommentDocumentNeededResources; import org.apache.xmlgraphics.ps.dsc.events.DSCCommentDocumentSuppliedResources; import org.apache.xmlgraphics.ps.dsc.events.DSCCommentHiResBoundingBox; +import org.apache.xmlgraphics.ps.dsc.events.DSCCommentIncludeResource; import org.apache.xmlgraphics.ps.dsc.events.DSCCommentLanguageLevel; import org.apache.xmlgraphics.ps.dsc.events.DSCCommentPage; import org.apache.xmlgraphics.ps.dsc.events.DSCCommentPages; import org.apache.xmlgraphics.ps.dsc.events.DSCEvent; import org.apache.xmlgraphics.ps.dsc.events.DSCHeaderComment; import org.apache.xmlgraphics.ps.dsc.events.PostScriptComment; +import org.apache.xmlgraphics.ps.dsc.events.PostScriptLine; import org.apache.xmlgraphics.ps.dsc.tools.DSCTools; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.events.ResourceEventProducer; import org.apache.fop.fonts.FontInfo; +import org.apache.fop.render.ImageHandler; +import org.apache.fop.render.ImageHandlerRegistry; /** * This class is used when two-pass production is used to generate the PostScript file (setting @@ -79,28 +77,79 @@ import org.apache.fop.fonts.FontInfo; */ public class ResourceHandler implements DSCParserConstants, PSSupportedFlavors { + /** logging instance */ + private static Log log = LogFactory.getLog(ResourceHandler.class); + + private FOUserAgent userAgent; + private FontInfo fontInfo; + + private ResourceTracker resTracker; + + //key: URI, values PSImageFormResource + private Map globalFormResources = new java.util.HashMap(); + //key: PSResource, values PSImageFormResource + private Map inlineFormResources = new java.util.HashMap(); + /** - * Rewrites the temporary PostScript file generated by PSRenderer adding all needed resources - * (fonts and images). + * Main constructor. * @param userAgent the FO user agent - * @param in the InputStream for the temporary PostScript file - * @param out the OutputStream to write the finished file to * @param fontInfo the font information * @param resTracker the resource tracker to use * @param formResources Contains all forms used by this document (maintained by PSRenderer) + */ + public ResourceHandler(FOUserAgent userAgent, FontInfo fontInfo, + ResourceTracker resTracker, Map formResources) { + this.userAgent = userAgent; + this.fontInfo = fontInfo; + this.resTracker = resTracker; + determineInlineForms(formResources); + } + + /** + * This method splits up the form resources map into two. One for global forms which + * have been referenced more than once, and one for inline forms which have only been + * used once. The latter is to conserve memory in the PostScript interpreter. + * @param formResources the original form resources map + */ + private void determineInlineForms(Map formResources) { + if (formResources == null) { + return; + } + Iterator iter = formResources.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = (Map.Entry)iter.next(); + PSResource res = (PSResource)entry.getValue(); + long count = resTracker.getUsageCount(res); + if (count > 1) { + //Make global form + this.globalFormResources.put(entry.getKey(), res); + } else { + //Inline resource + this.inlineFormResources.put(res, res); + resTracker.declareInlined(res); + } + } + } + + /** + * Rewrites the temporary PostScript file generated by PSRenderer adding all needed resources + * (fonts and images). + * @param in the InputStream for the temporary PostScript file + * @param out the OutputStream to write the finished file to * @param pageCount the number of pages (given here because PSRenderer writes an "(atend)") * @param documentBoundingBox the document's bounding box * (given here because PSRenderer writes an "(atend)") * @throws DSCException If there's an error in the DSC structure of the PS file * @throws IOException In case of an I/O error */ - public static void process(FOUserAgent userAgent, InputStream in, OutputStream out, - FontInfo fontInfo, ResourceTracker resTracker, Map formResources, + public void process(InputStream in, OutputStream out, int pageCount, Rectangle2D documentBoundingBox) throws DSCException, IOException { DSCParser parser = new DSCParser(in); + PSGenerator gen = new PSGenerator(out); - parser.setNestedDocumentHandler(new DefaultNestedDocumentHandler(gen)); + parser.addListener(new DefaultNestedDocumentHandler(gen)); + parser.addListener(new IncludeResourceListener(gen)); //Skip DSC header DSCHeaderComment header = DSCTools.checkAndSkipDSC30Header(parser); @@ -140,7 +189,7 @@ public class ResourceHandler implements DSCParserConstants, PSSupportedFlavors { new DSCCommentHiResBoundingBox(documentBoundingBox).generate(gen); PSFontUtils.determineSuppliedFonts(resTracker, fontInfo, fontInfo.getUsedFonts()); - registerSuppliedForms(resTracker, formResources); + registerSuppliedForms(resTracker, globalFormResources); //Supplied Resources DSCCommentDocumentSuppliedResources supplied @@ -174,7 +223,7 @@ public class ResourceHandler implements DSCParserConstants, PSSupportedFlavors { throw new DSCException("Didn't find %FOPFontSetup comment in stream"); } PSFontUtils.writeFontDict(gen, fontInfo, fontInfo.getUsedFonts()); - generateForms(resTracker, userAgent, formResources, gen); + generateForms(globalFormResources, gen); //Skip the prolog and to the first page DSCComment pageOrTrailer = parser.nextDSCComment(DSCConstants.PAGE, gen); @@ -218,115 +267,64 @@ public class ResourceHandler implements DSCParserConstants, PSSupportedFlavors { } } - private static void generateForms(ResourceTracker resTracker, FOUserAgent userAgent, - Map formResources, PSGenerator gen) throws IOException { + private void generateForms(Map formResources, PSGenerator gen) throws IOException { if (formResources == null) { return; } Iterator iter = formResources.values().iterator(); while (iter.hasNext()) { PSImageFormResource form = (PSImageFormResource)iter.next(); - final String uri = form.getImageURI(); + generateFormForImage(gen, form); + } + } - ImageManager manager = userAgent.getFactory().getImageManager(); - ImageInfo info = null; - try { - ImageSessionContext sessionContext = userAgent.getImageSessionContext(); - info = manager.getImageInfo(uri, sessionContext); + private void generateFormForImage(PSGenerator gen, PSImageFormResource form) + throws IOException { + final String uri = form.getImageURI(); - ImageFlavor[] flavors; - if (gen.getPSLevel() >= 3) { - flavors = LEVEL_3_FLAVORS_FORM; - } else { - flavors = LEVEL_2_FLAVORS_FORM; - } - Map hints = ImageUtil.getDefaultHints(sessionContext); - org.apache.xmlgraphics.image.loader.Image img = manager.getImage( - info, flavors, hints, sessionContext); - - String imageDescription = info.getMimeType() + " " + info.getOriginalURI(); - final Dimension2D dimensionsPt = info.getSize().getDimensionPt(); - final Dimension2D dimensionsMpt = info.getSize().getDimensionMpt(); - - if (img instanceof ImageGraphics2D) { - final ImageGraphics2D imageG2D = (ImageGraphics2D)img; - FormGenerator formGen = new FormGenerator( - form.getName(), imageDescription, dimensionsPt) { - - protected void generatePaintProc(PSGenerator gen) - throws IOException { - gen.getResourceTracker().notifyResourceUsageOnPage( - PSProcSets.EPS_PROCSET); - gen.writeln("BeginEPSF"); - PSGraphics2DAdapter adapter = new PSGraphics2DAdapter(gen, false); - adapter.paintImage(imageG2D.getGraphics2DImagePainter(), - null, - 0, 0, - (int)Math.round(dimensionsMpt.getWidth()), - (int)Math.round(dimensionsMpt.getHeight())); - gen.writeln("EndEPSF"); - } + ImageManager manager = userAgent.getFactory().getImageManager(); + ImageInfo info = null; + try { + ImageSessionContext sessionContext = userAgent.getImageSessionContext(); + info = manager.getImageInfo(uri, sessionContext); - }; - formGen.generate(gen); - } else if (img instanceof ImageRendered) { - ImageRendered imgRend = (ImageRendered)img; - RenderedImage ri = imgRend.getRenderedImage(); - FormGenerator formGen = new ImageFormGenerator( - form.getName(), imageDescription, - info.getSize().getDimensionPt(), - ri, false); - formGen.generate(gen); - } else if (img instanceof ImageXMLDOM) { - throw new UnsupportedOperationException( - "Embedding an ImageXMLDOM as a form isn't supported, yet"); - } else if (img instanceof ImageRawStream) { - final ImageRawStream raw = (ImageRawStream)img; - if (raw instanceof ImageRawEPS) { - final ImageRawEPS eps = (ImageRawEPS)raw; - throw new UnsupportedOperationException( - "Embedding EPS as forms isn't supported, yet"); - /* - InputStream in = eps.createInputStream(); - try { - FormGenerator formGen = new EPSFormGenerator(form.getName(), - imageDescription, dimensions, in); - formGen.generate(gen); - } finally { - IOUtils.closeQuietly(in); - }*/ - } else if (raw instanceof ImageRawCCITTFax) { - ImageRawCCITTFax jpeg = (ImageRawCCITTFax)raw; - ImageEncoder encoder = new ImageEncoderCCITTFax(jpeg); - FormGenerator formGen = new ImageFormGenerator( - form.getName(), imageDescription, - info.getSize().getDimensionPt(), - info.getSize().getDimensionPx(), - encoder, - jpeg.getColorSpace(), 1, false); - formGen.generate(gen); - } else if (raw instanceof ImageRawJPEG) { - ImageRawJPEG jpeg = (ImageRawJPEG)raw; - ImageEncoder encoder = new ImageEncoderJPEG(jpeg); - FormGenerator formGen = new ImageFormGenerator( - form.getName(), imageDescription, - info.getSize().getDimensionPt(), - info.getSize().getDimensionPx(), - encoder, - jpeg.getColorSpace(), jpeg.isInverted()); - formGen.generate(gen); - } else { - throw new UnsupportedOperationException("Unsupported raw image: " + info); - } - } else { - throw new UnsupportedOperationException("Unsupported image type: " + img); - } - } catch (ImageException ie) { - ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get( - userAgent.getEventBroadcaster()); - eventProducer.imageError(resTracker, (info != null ? info.toString() : uri), - ie, null); + //Create a rendering context for form creation + PSRenderingContext formContext = new PSRenderingContext( + userAgent, gen, fontInfo, true); + + ImageFlavor[] flavors; + ImageHandlerRegistry imageHandlerRegistry + = userAgent.getFactory().getImageHandlerRegistry(); + flavors = imageHandlerRegistry.getSupportedFlavors(formContext); + + Map hints = ImageUtil.getDefaultHints(sessionContext); + org.apache.xmlgraphics.image.loader.Image img = manager.getImage( + info, flavors, hints, sessionContext); + + ImageHandler basicHandler = imageHandlerRegistry.getHandler(formContext, img); + if (basicHandler == null) { + throw new UnsupportedOperationException( + "No ImageHandler available for image: " + + img.getInfo() + " (" + img.getClass().getName() + ")"); } + + if (!(basicHandler instanceof PSImageHandler)) { + throw new IllegalStateException( + "ImageHandler implementation doesn't behave properly." + + " It should have returned false in isCompatible(). Class: " + + basicHandler.getClass().getName()); + } + PSImageHandler handler = (PSImageHandler)basicHandler; + if (log.isTraceEnabled()) { + log.trace("Using ImageHandler: " + handler.getClass().getName()); + } + handler.generateForm(formContext, img, form); + + } catch (ImageException ie) { + ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get( + userAgent.getEventBroadcaster()); + eventProducer.imageError(resTracker, (info != null ? info.toString() : uri), + ie, null); } } @@ -354,4 +352,50 @@ public class ResourceHandler implements DSCParserConstants, PSSupportedFlavors { return formGen; } + private class IncludeResourceListener implements DSCListener { + + private PSGenerator gen; + + public IncludeResourceListener(PSGenerator gen) { + this.gen = gen; + } + + /** {@inheritDoc} */ + public void processEvent(DSCEvent event, DSCParser parser) + throws IOException, DSCException { + if (event.isDSCComment() && event instanceof DSCCommentIncludeResource) { + DSCCommentIncludeResource include = (DSCCommentIncludeResource)event; + PSResource res = include.getResource(); + if (res.getType().equals(PSResource.TYPE_FORM)) { + if (inlineFormResources.containsValue(res)) { + PSImageFormResource form = (PSImageFormResource) + inlineFormResources.get(res); + //Create an inline form + //Wrap in save/restore pair to release memory + gen.writeln("save"); + generateFormForImage(gen, form); + boolean execformFound = false; + DSCEvent next = parser.nextEvent(); + if (next.isLine()) { + PostScriptLine line = next.asLine(); + if (line.getLine().endsWith(" execform")) { + line.generate(gen); + execformFound = true; + } + } + if (!execformFound) { + throw new IOException( + "Expected a PostScript line in the form: <form> execform"); + } + gen.writeln("restore"); + } else { + //Do nothing + } + parser.next(); + } + } + } + + } + } |