org.apache.fop.render.java2d.Java2DImageHandlerGraphics2D\r
org.apache.fop.render.pcl.PCLImageHandlerRenderedImage\r
org.apache.fop.render.pcl.PCLImageHandlerGraphics2D\r
+org.apache.fop.render.ps.PSImageHandlerRenderedImage\r
+org.apache.fop.render.ps.PSImageHandlerEPS\r
+org.apache.fop.render.ps.PSImageHandlerRawCCITTFax\r
+org.apache.fop.render.ps.PSImageHandlerRawJPEG\r
+org.apache.fop.render.ps.PSImageHandlerGraphics2D\r
+org.apache.fop.render.ps.PSImageHandlerSVG\r
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.
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).
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;
// ---------------------------------------------- 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.
return title;
}
+ /**
+ * 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
*/
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);
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);
}
}
+ 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
*/
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;
}
/**
this.userAgent = ua;
}
- /**
- * Returns the user agent.
- * @return the user agent
- */
+ /** {@inheritDoc} */
public FOUserAgent getUserAgent() {
return this.userAgent;
}
handler.handleImage(context, effImage, rect);
}
+ /**
+ * 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
*/
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
* @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
info.setCreationDate(userAgent.getCreationDate());
info.setAuthor(userAgent.getAuthor());
info.setTitle(userAgent.getTitle());
+ info.setSubject(userAgent.getSubject());
info.setKeywords(userAgent.getKeywords());
}
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) {
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;
+ }
+
}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.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;
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.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;
+ }
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.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;
+ }
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.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;
+ }
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.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;
+ }
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.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;
+ }
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.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;
+ }
+
+}
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();
+ }
+
+
}
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;
}
/** {@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);
}
// Java
import java.awt.Color;
+import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.RenderedImage;
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;
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;
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;
}
}
- /**
- * 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();
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) {
}
}
- 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
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) {
package org.apache.fop.render.ps;
+import org.apache.xmlgraphics.ps.PSGenerator;
import org.apache.xmlgraphics.util.MimeConstants;
import org.apache.fop.apps.FOUserAgent;
*/
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} */
return MimeConstants.MIME_POSTSCRIPT;
}
+ /**
+ * Returns the PostScript generator.
+ * @return the PostScript generator
+ */
+ public PSGenerator getGenerator() {
+ return this.gen;
+ }
+
/**
* Returns the font list.
* @return the font list
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);
+ }
+
}
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;
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;
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
*/
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);
new DSCCommentHiResBoundingBox(documentBoundingBox).generate(gen);
PSFontUtils.determineSuppliedFonts(resTracker, fontInfo, fontInfo.getUsedFonts());
- registerSuppliedForms(resTracker, formResources);
+ registerSuppliedForms(resTracker, globalFormResources);
//Supplied Resources
DSCCommentDocumentSuppliedResources supplied
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);
}
}
- 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);
}
}
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();
+ }
+ }
+ }
+
+ }
+
}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.MissingResourceException;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.stream.StreamSource;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+
+import org.apache.xmlgraphics.ps.PSResource;
+import org.apache.xmlgraphics.ps.dsc.DSCException;
+import org.apache.xmlgraphics.ps.dsc.DSCParser;
+import org.apache.xmlgraphics.ps.dsc.events.AbstractResourceDSCComment;
+import org.apache.xmlgraphics.ps.dsc.events.DSCComment;
+import org.apache.xmlgraphics.ps.dsc.events.DSCEvent;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.apps.Fop;
+import org.apache.fop.apps.FopFactory;
+import org.apache.fop.apps.MimeConstants;
+
+/**
+ * Abstract base class for PostScript verification tests.
+ */
+public abstract class AbstractPostScriptTestCase extends TestCase {
+
+ /** the JAXP TransformerFactory */
+ protected TransformerFactory tFactory = TransformerFactory.newInstance();
+ /** the FopFactory */
+ protected FopFactory fopFactory = FopFactory.newInstance();
+
+ /**
+ * Renders a test file.
+ * @param ua the user agent (with override set!)
+ * @param resourceName the resource name for the FO file
+ * @param suffix a suffix for the output filename
+ * @return the output file
+ * @throws Exception if an error occurs
+ */
+ protected File renderFile(FOUserAgent ua, String resourceName, String suffix)
+ throws Exception {
+ File outputFile = new File("build/test-results/" + resourceName + suffix + ".ps");
+ File outputDir = outputFile.getParentFile();
+ FileUtils.forceMkdir(outputDir);
+
+ // Prepare input file
+ InputStream in = getClass().getResourceAsStream(resourceName);
+ if (in == null) {
+ throw new MissingResourceException(resourceName + " not found in resources",
+ getClass().getName(), null);
+ }
+ try {
+ Source src = new StreamSource(in);
+
+ // Create PostScript
+ OutputStream out = new java.io.FileOutputStream(outputFile);
+ out = new java.io.BufferedOutputStream(out);
+ try {
+ Fop fop = fopFactory.newFop(MimeConstants.MIME_POSTSCRIPT, ua, out);
+ SAXResult res = new SAXResult(fop.getDefaultHandler());
+
+ Transformer transformer = tFactory.newTransformer();
+ transformer.transform(src, res);
+ } finally {
+ IOUtils.closeQuietly(out);
+ }
+ } finally {
+ IOUtils.closeQuietly(in);
+ }
+ return outputFile;
+ }
+
+ /**
+ * Scans for a certain resource DSC comment and checks against a given resource.
+ * @param parser the DSC parser
+ * @param comment the comment to scan for
+ * @param resource the resource to check against
+ * @throws IOException if an I/O error occurs
+ * @throws DSCException if a DSC error occurs
+ */
+ protected void checkResourceComment(DSCParser parser, String comment, PSResource resource)
+ throws IOException, DSCException {
+ AbstractResourceDSCComment resComment;
+ resComment = (AbstractResourceDSCComment)gotoDSCComment(parser, comment);
+ assertEquals(resource, resComment.getResource());
+ }
+
+ /**
+ * Advances the DSC parser to a DSC comment with the given name.
+ * @param parser the DSC parser
+ * @param name the name of the DSC comment
+ * @return the DSC comment
+ * @throws IOException if an I/O error occurs
+ * @throws DSCException if a DSC error occurs
+ */
+ protected static DSCComment gotoDSCComment(DSCParser parser, String name)
+ throws IOException, DSCException {
+ while (parser.hasNext()) {
+ DSCEvent event = parser.nextEvent();
+ if (event.isDSCComment()) {
+ DSCComment comment = event.asDSCComment();
+ if (comment.getName().equals(name)) {
+ return comment;
+ }
+ }
+ }
+ return null;
+ }
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.commons.io.IOUtils;
+
+import org.apache.xmlgraphics.ps.DSCConstants;
+import org.apache.xmlgraphics.ps.PSResource;
+import org.apache.xmlgraphics.ps.dsc.DSCException;
+import org.apache.xmlgraphics.ps.dsc.DSCParser;
+import org.apache.xmlgraphics.ps.dsc.events.DSCCommentPage;
+import org.apache.xmlgraphics.ps.dsc.events.DSCCommentPages;
+import org.apache.xmlgraphics.ps.dsc.events.DSCCommentTitle;
+import org.apache.xmlgraphics.ps.dsc.events.DSCEvent;
+
+import org.apache.fop.apps.FOUserAgent;
+
+/**
+ * Tests the image handling in PostScript output.
+ */
+public class ImageHandlingTestCase extends AbstractPostScriptTestCase {
+
+ /**
+ * Tests JPEG handling with the {@link PSRenderer}.
+ * @throws Exception if an error occurs
+ */
+ public void testJPEGImageWithRendererLevel3() throws Exception {
+ innerTestJPEGImageWithRenderer(3);
+ }
+
+ /**
+ * Tests JPEG handling with the {@link PSRenderer}.
+ * @throws Exception if an error occurs
+ */
+ public void testJPEGImageWithRendererLevel2() throws Exception {
+ innerTestJPEGImageWithRenderer(2);
+ }
+
+ /**
+ * Tests JPEG handling with the {@link PSDocumentHandler}.
+ * @throws Exception if an error occurs
+ */
+ public void testJPEGImageWithIFLevel3() throws Exception {
+ innerTestJPEGImageWithIF(3);
+ }
+
+ /**
+ * Tests JPEG handling with the {@link PSDocumentHandler}.
+ * @throws Exception if an error occurs
+ */
+ public void testJPEGImageWithIFLevel2() throws Exception {
+ innerTestJPEGImageWithIF(2);
+ }
+
+ private void innerTestJPEGImageWithRenderer(int level) throws Exception {
+ FOUserAgent ua = fopFactory.newFOUserAgent();
+ PSRenderer renderer = new PSRenderer();
+ renderer.setUserAgent(ua);
+ PSRenderingUtil psUtil = renderer.getPSUtil();
+ psUtil.setLanguageLevel(level);
+ psUtil.setOptimizeResources(true);
+ ua.setRendererOverride(renderer);
+
+ // Prepare output file
+ File outputFile = renderFile(ua, "ps-jpeg-image.fo", "-rend-l" + psUtil.getLanguageLevel());
+ verifyPostScriptFile(outputFile, psUtil.getLanguageLevel());
+ }
+
+ private void innerTestJPEGImageWithIF(int level) throws Exception {
+ FOUserAgent ua = fopFactory.newFOUserAgent();
+ PSDocumentHandler handler = new PSDocumentHandler();
+ handler.setUserAgent(ua);
+ PSRenderingUtil psUtil = handler.getPSUtil();
+ psUtil.setLanguageLevel(level);
+ psUtil.setOptimizeResources(true);
+ ua.setDocumentHandlerOverride(handler);
+
+ // Prepare output file
+ File outputFile = renderFile(ua, "ps-jpeg-image.fo", "-if-l" + psUtil.getLanguageLevel());
+ verifyPostScriptFile(outputFile, psUtil.getLanguageLevel());
+ }
+
+ private void verifyPostScriptFile(File psFile, int level)
+ throws IOException, DSCException {
+ InputStream in = new java.io.FileInputStream(psFile);
+ in = new java.io.BufferedInputStream(in);
+ try {
+ DSCParser parser = new DSCParser(in);
+
+ DSCCommentPages pages = (DSCCommentPages)gotoDSCComment(parser, DSCConstants.PAGES);
+ assertEquals(1, pages.getPageCount());
+
+ //Skip procsets and encoding
+ gotoDSCComment(parser, DSCConstants.BEGIN_RESOURCE);
+ gotoDSCComment(parser, DSCConstants.BEGIN_RESOURCE);
+ gotoDSCComment(parser, DSCConstants.BEGIN_RESOURCE);
+
+ PSResource form2 = new PSResource(PSResource.TYPE_FORM, "FOPForm:2");
+ checkResourceComment(parser, DSCConstants.BEGIN_RESOURCE, form2);
+ DSCCommentTitle title = (DSCCommentTitle)parser.nextEvent().asDSCComment();
+ assertEquals("image/jpeg test/resources/images/bgimg300dpi.jpg", title.getTitle());
+
+ String resourceContent = getResourceContent(parser);
+
+ if (level == 3) {
+ assertContains(resourceContent, "/FOPForm:2");
+ assertContains(resourceContent, "/DCTDecode filter");
+ assertContains(resourceContent, "/ReusableStreamDecode filter");
+ } else {
+ assertContains(resourceContent, "/FOPForm:2");
+ assertContains(resourceContent, "/DCTDecode filter");
+ assertAbsent(resourceContent, "/ReusableStreamDecode filter");
+ }
+
+ //---=== Page 1 ===---
+ DSCCommentPage page = (DSCCommentPage)gotoDSCComment(parser, DSCConstants.PAGE);
+ assertEquals(1, page.getPagePosition());
+
+ PSResource form1 = new PSResource(PSResource.TYPE_FORM, "FOPForm:1");
+ checkResourceComment(parser, DSCConstants.BEGIN_RESOURCE, form1);
+ title = (DSCCommentTitle)parser.nextEvent().asDSCComment();
+ assertEquals("image/jpeg test/resources/images/bgimg72dpi.jpg", title.getTitle());
+ resourceContent = getResourceContent(parser);
+
+ if (level == 3) {
+ assertContains(resourceContent, "/FOPForm:1");
+ assertContains(resourceContent, "/DCTDecode filter");
+ assertContains(resourceContent, "/ReusableStreamDecode filter");
+ } else {
+ assertContains(resourceContent, "/FOPForm:1");
+ assertContains(resourceContent, "/DCTDecode filter");
+ assertAbsent(resourceContent, "/ReusableStreamDecode filter");
+ }
+
+ } finally {
+ IOUtils.closeQuietly(in);
+ }
+ }
+
+ private void assertMatches(String text, String regex) {
+ assertTrue("Text didn't match '" + regex + "'", text.matches(regex));
+ }
+
+ private void assertContains(String text, String searchString) {
+ assertTrue("Text doesn't contain '" + searchString + "'", text.indexOf(searchString) >= 0);
+ }
+
+ private void assertAbsent(String text, String searchString) {
+ assertTrue("Text contains '" + searchString + "'", text.indexOf(searchString) < 0);
+ }
+
+ private String getResourceContent(DSCParser parser) throws IOException, DSCException {
+ StringBuffer sb = new StringBuffer();
+ while (parser.hasNext()) {
+ DSCEvent event = parser.nextEvent();
+ if (event.isLine()) {
+ sb.append(event.asLine().getLine()).append('\n');
+ } else if (event.isDSCComment()) {
+ if (DSCConstants.END_RESOURCE.equals(event.asDSCComment().getName())) {
+ break;
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Set;
+
+import org.apache.commons.io.IOUtils;
+
+import org.apache.xmlgraphics.ps.DSCConstants;
+import org.apache.xmlgraphics.ps.PSResource;
+import org.apache.xmlgraphics.ps.dsc.DSCException;
+import org.apache.xmlgraphics.ps.dsc.DSCListener;
+import org.apache.xmlgraphics.ps.dsc.DSCParser;
+import org.apache.xmlgraphics.ps.dsc.DefaultNestedDocumentHandler;
+import org.apache.xmlgraphics.ps.dsc.events.AbstractResourcesDSCComment;
+import org.apache.xmlgraphics.ps.dsc.events.DSCAtend;
+import org.apache.xmlgraphics.ps.dsc.events.DSCCommentBeginDocument;
+import org.apache.xmlgraphics.ps.dsc.events.DSCCommentDocumentNeededResources;
+import org.apache.xmlgraphics.ps.dsc.events.DSCCommentDocumentSuppliedResources;
+import org.apache.xmlgraphics.ps.dsc.events.DSCCommentEndOfFile;
+import org.apache.xmlgraphics.ps.dsc.events.DSCCommentIncludeResource;
+import org.apache.xmlgraphics.ps.dsc.events.DSCCommentPage;
+import org.apache.xmlgraphics.ps.dsc.events.DSCCommentPages;
+
+import org.apache.fop.apps.FOUserAgent;
+
+/**
+ * Tests the PostScript resource optimization (selective de-duplication of
+ * images that are used multiple times).
+ */
+public class ResourceOptimizationTestCase extends AbstractPostScriptTestCase {
+
+ /**
+ * Tests resource optimization with the {@link PSRenderer}.
+ * @throws Exception if an error occurs
+ */
+ public void testResourceOptimizationWithRenderer() throws Exception {
+ FOUserAgent ua = fopFactory.newFOUserAgent();
+ PSRenderer renderer = new PSRenderer();
+ renderer.setUserAgent(ua);
+ // This is the important part: we're enabling resource optimization
+ renderer.getPSUtil().setOptimizeResources(true);
+ ua.setRendererOverride(renderer);
+
+ // Prepare output file
+ File outputFile = renderFile(ua, "ps-resources.fo",
+ "-rend-l" + renderer.getPSUtil().getLanguageLevel());
+ verifyPostScriptFile(outputFile);
+ }
+
+ /**
+ * Tests resource optimization with the {@link PSDocumentHandler}.
+ * @throws Exception if an error occurs
+ */
+ public void testResourceOptimizationWithIF() throws Exception {
+ FOUserAgent ua = fopFactory.newFOUserAgent();
+ PSDocumentHandler handler = new PSDocumentHandler();
+ handler.setUserAgent(ua);
+ // This is the important part: we're enabling resource optimization
+ handler.getPSUtil().setOptimizeResources(true);
+ ua.setDocumentHandlerOverride(handler);
+
+ // Prepare output file
+ File outputFile = renderFile(ua, "ps-resources.fo",
+ "-if-l" + handler.getPSUtil().getLanguageLevel());
+ verifyPostScriptFile(outputFile);
+ }
+
+ private void verifyPostScriptFile(File psFile) throws IOException, DSCException {
+ InputStream in = new java.io.FileInputStream(psFile);
+ in = new java.io.BufferedInputStream(in);
+ try {
+ DSCParser parser = new DSCParser(in);
+
+ //The first form is for arrow_down_small.png (to be reused)
+ PSResource form1 = new PSResource(PSResource.TYPE_FORM, "FOPForm:1");
+ PSResource helvetica = new PSResource(PSResource.TYPE_FONT, "Helvetica");
+ PSResource helveticaBold = new PSResource(PSResource.TYPE_FONT, "Helvetica-Bold");
+
+ PSResource res;
+ DSCCommentPages pages = (DSCCommentPages)gotoDSCComment(parser, DSCConstants.PAGES);
+ assertEquals(2, pages.getPageCount());
+
+ DSCCommentDocumentSuppliedResources supplied
+ = (DSCCommentDocumentSuppliedResources)gotoDSCComment(parser,
+ DSCConstants.DOCUMENT_SUPPLIED_RESOURCES);
+ Set resources = supplied.getResources();
+ assertEquals(4, resources.size());
+ assertTrue(resources.contains(form1));
+ assertTrue("Expected barcode.eps as supplied resource",
+ resources.contains(new PSResource(PSResource.TYPE_FILE,
+ "test/resources/images/barcode.eps")));
+
+ DSCCommentDocumentNeededResources needed
+ = (DSCCommentDocumentNeededResources)gotoDSCComment(parser,
+ DSCConstants.DOCUMENT_NEEDED_RESOURCES);
+ resources = needed.getResources();
+ assertEquals(2, resources.size());
+ assertTrue("Expected Helvetica as needed resource",
+ resources.contains(new PSResource(PSResource.TYPE_FONT, "Helvetica")));
+ assertTrue("Expected Helvetica-Bold as needed resource",
+ resources.contains(new PSResource(PSResource.TYPE_FONT, "Helvetica-Bold")));
+
+ //Some document structure checking
+ assertNotNull(gotoDSCComment(parser, DSCConstants.BEGIN_DEFAULTS));
+ assertNotNull(gotoDSCComment(parser, DSCConstants.END_DEFAULTS));
+ assertNotNull(gotoDSCComment(parser, DSCConstants.BEGIN_PROLOG));
+ assertNotNull(gotoDSCComment(parser, DSCConstants.END_PROLOG));
+ assertNotNull(gotoDSCComment(parser, DSCConstants.BEGIN_SETUP));
+
+ //Check includes for the two referenced base 14 fonts
+ DSCCommentIncludeResource include;
+ Collection strings = new java.util.HashSet(
+ Arrays.asList(new String[] {"Helvetica", "Helvetica-Bold"}));
+ for (int i = 0; i < 2; i++) {
+ include = (DSCCommentIncludeResource)gotoDSCComment(
+ parser, DSCConstants.INCLUDE_RESOURCE);
+ res = include.getResource();
+ assertEquals(PSResource.TYPE_FONT, res.getType());
+ strings.remove(res.getName());
+ }
+ assertEquals(0, strings.size());
+
+ checkResourceComment(parser, DSCConstants.BEGIN_RESOURCE,
+ new PSResource(PSResource.TYPE_ENCODING, "WinAnsiEncoding"));
+
+ //Here, we encounter form 1 again
+ checkResourceComment(parser, DSCConstants.BEGIN_RESOURCE, form1);
+
+ assertNotNull(gotoDSCComment(parser, DSCConstants.END_SETUP));
+ //Now the actual pages begin
+
+ //---=== Page 1 ===---
+ DSCCommentPage page = (DSCCommentPage)gotoDSCComment(parser, DSCConstants.PAGE);
+ assertEquals(1, page.getPagePosition());
+
+ assertEquals(DSCAtend.class,
+ gotoDSCComment(parser, DSCConstants.PAGE_RESOURCES).getClass());
+ assertNotNull(gotoDSCComment(parser, DSCConstants.BEGIN_PAGE_SETUP));
+ assertNotNull(gotoDSCComment(parser, DSCConstants.END_PAGE_SETUP));
+
+ PSResource form2 = new PSResource(PSResource.TYPE_FORM, "FOPForm:2");
+ checkResourceComment(parser, DSCConstants.BEGIN_RESOURCE, form2);
+ assertNotNull(gotoDSCComment(parser, DSCConstants.PAGE_TRAILER));
+
+ AbstractResourcesDSCComment pageResources;
+ pageResources = (AbstractResourcesDSCComment)gotoDSCComment(
+ parser, DSCConstants.PAGE_RESOURCES);
+ resources = pageResources.getResources();
+ assertEquals(5, resources.size());
+ assertTrue(resources.contains(form1));
+ assertTrue(resources.contains(form2));
+ assertTrue(resources.contains(helvetica));
+ assertTrue(resources.contains(helveticaBold));
+
+ //---=== Page 2 ===---
+ page = (DSCCommentPage)gotoDSCComment(parser, DSCConstants.PAGE);
+ assertEquals(2, page.getPagePosition());
+
+ assertEquals(DSCAtend.class,
+ gotoDSCComment(parser, DSCConstants.PAGE_RESOURCES).getClass());
+ assertNotNull(gotoDSCComment(parser, DSCConstants.BEGIN_PAGE_SETUP));
+ assertNotNull(gotoDSCComment(parser, DSCConstants.END_PAGE_SETUP));
+
+ DSCCommentBeginDocument beginDocument;
+ beginDocument = (DSCCommentBeginDocument)gotoDSCComment(
+ parser, DSCConstants.BEGIN_DOCUMENT);
+ assertEquals("test/resources/images/barcode.eps",
+ beginDocument.getResource().getName());
+ DSCListener listener = new DefaultNestedDocumentHandler(null);
+ listener.processEvent(beginDocument, parser);
+
+ //And again (the barcode is generated twice)
+ beginDocument = (DSCCommentBeginDocument)gotoDSCComment(
+ parser, DSCConstants.BEGIN_DOCUMENT);
+ assertEquals("test/resources/images/barcode.eps",
+ beginDocument.getResource().getName());
+ listener.processEvent(beginDocument, parser);
+
+ assertNotNull(gotoDSCComment(parser, DSCConstants.PAGE_TRAILER));
+ pageResources = (AbstractResourcesDSCComment)gotoDSCComment(
+ parser, DSCConstants.PAGE_RESOURCES);
+ resources = pageResources.getResources();
+ assertEquals(6, resources.size());
+ assertTrue(resources.contains(form1));
+ assertFalse(resources.contains(form2));
+ assertTrue(resources.contains(helvetica));
+ assertTrue(resources.contains(helveticaBold));
+ assertTrue(resources.contains(beginDocument.getResource()));
+
+ assertNotNull(gotoDSCComment(parser, DSCConstants.TRAILER));
+ //No headers in between, as they should have been put at the beginning of the file
+ assertEquals(DSCCommentEndOfFile.class, parser.nextEvent().asDSCComment().getClass());
+
+ } finally {
+ IOUtils.closeQuietly(in);
+ }
+ }
+
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="A4" page-height="29.7cm" page-width="21cm" margin="2cm">
+ <fo:region-body/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="A4">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block>JPEG image:</fo:block>
+ <fo:block><fo:external-graphic src="test/resources/images/bgimg72dpi.jpg"/></fo:block>
+ <fo:block>
+ <fo:external-graphic src="test/resources/images/bgimg300dpi.jpg"/>
+ <fo:external-graphic src="test/resources/images/bgimg300dpi.jpg"/>
+ </fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+</fo:root>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="A4" page-height="29.7cm" page-width="21cm" margin="2cm">
+ <fo:region-body/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="A4">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block font-weight="bold">PostScript Resource Optimization Test</fo:block>
+ <fo:block>Used again later:</fo:block>
+ <fo:block><fo:external-graphic src="test/resources/images/arrow_down_small.png"/></fo:block>
+ <fo:block>Used only once in the whole document:</fo:block>
+ <fo:block><fo:external-graphic src="test/resources/images/arrow_up_small.png"/></fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ <fo:page-sequence master-reference="A4">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block font-weight="bold">PostScript Resource Optimization Test</fo:block>
+ <fo:block>Image already used in previous page-sequence:</fo:block>
+ <fo:block>
+ <fo:external-graphic src="test/resources/images/arrow_down_small.png" content-width="80%"/>
+ <fo:external-graphic src="test/resources/images/arrow_down_small.png"/>
+ <fo:external-graphic src="test/resources/images/arrow_down_small.png" content-width="120%"/>
+ </fo:block>
+ <fo:block>Can't currently reuse EPS images:</fo:block>
+ <fo:block>
+ <fo:external-graphic src="test/resources/images/barcode.eps"/>
+ <fo:external-graphic src="test/resources/images/barcode.eps"/>
+ </fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+</fo:root>