From 01e51818270a7a8351dcd2cba4093abf586bd6ed Mon Sep 17 00:00:00 2001 From: Jeremias Maerki Date: Fri, 7 Nov 2003 22:19:19 +0000 Subject: [PATCH] Substantial refactoring of the PS transcoder (especially the text painting, less painting using the StrokingTextPainter, therefore more speed and smaller output files) Addition of an EPS transcoder git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@196990 13f79535-47bb-0310-9956-ffa450edef68 --- .../ps/AbstractPSDocumentGraphics2D.java | 293 ++++++++ .../fop/render/ps/AbstractPSTranscoder.java | 195 +++++ .../apache/fop/render/ps/DSCConstants.java | 3 + .../fop/render/ps/EPSDocumentGraphics2D.java | 124 ++++ .../apache/fop/render/ps/EPSTranscoder.java | 94 +++ .../fop/render/ps/PSDocumentGraphics2D.java | 199 ++--- .../org/apache/fop/render/ps/PSGenerator.java | 83 ++- .../apache/fop/render/ps/PSGraphics2D.java | 684 +++++++++--------- .../org/apache/fop/render/ps/PSProcSets.java | 8 + .../org/apache/fop/render/ps/PSRenderer.java | 79 +- .../fop/render/ps/PSTextElementBridge.java | 39 +- .../apache/fop/render/ps/PSTextPainter.java | 413 +++++++---- .../apache/fop/render/ps/PSTranscoder.java | 102 +-- .../apache/fop/render/ps/PSXMLHandler.java | 16 +- 14 files changed, 1518 insertions(+), 814 deletions(-) create mode 100644 src/java/org/apache/fop/render/ps/AbstractPSDocumentGraphics2D.java create mode 100644 src/java/org/apache/fop/render/ps/AbstractPSTranscoder.java create mode 100644 src/java/org/apache/fop/render/ps/EPSDocumentGraphics2D.java create mode 100644 src/java/org/apache/fop/render/ps/EPSTranscoder.java diff --git a/src/java/org/apache/fop/render/ps/AbstractPSDocumentGraphics2D.java b/src/java/org/apache/fop/render/ps/AbstractPSDocumentGraphics2D.java new file mode 100644 index 000000000..f28061249 --- /dev/null +++ b/src/java/org/apache/fop/render/ps/AbstractPSDocumentGraphics2D.java @@ -0,0 +1,293 @@ +/* + * $Id$ + * ============================================================================ + * The Apache Software License, Version 1.1 + * ============================================================================ + * + * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modifica- + * tion, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: "This product includes software + * developed by the Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "FOP" and "Apache Software Foundation" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", nor may + * "Apache" appear in their name, without prior written permission of the + * Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- + * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ============================================================================ + * + * This software consists of voluntary contributions made by many individuals + * on behalf of the Apache Software Foundation and was originally created by + * James Tauber . For more information on the Apache + * Software Foundation, please see . + */ +package org.apache.fop.render.ps; + +//Java +import java.awt.Graphics; +import java.awt.Color; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.io.OutputStream; +import java.io.IOException; + +//FOP +import org.apache.fop.apps.Document; +import org.apache.fop.fonts.Font; +import org.apache.fop.render.pdf.FontSetup; + +/** + * This class is a wrapper for the PSGraphics2D that + * is used to create a full document around the PostScript rendering from + * PSGraphics2D. + * + * @author Keiron Liddle + * @author Jeremias Maerki + * @version $Id$ + * @see org.apache.fop.render.ps.PSGraphics2D + */ +public abstract class AbstractPSDocumentGraphics2D extends PSGraphics2D { + + protected static final Integer ZERO = new Integer(0); + + protected int width; + protected int height; + + protected int pagecount; + protected boolean pagePending; + + protected Shape initialClip; + protected AffineTransform initialTransform; + + + /** + * Create a new AbstractPSDocumentGraphics2D. + * This is used to create a new PostScript document, the height, + * width and output stream can be setup later. + * For use by the transcoder which needs font information + * for the bridge before the document size is known. + * The resulting document is written to the stream after rendering. + * + * @param textAsShapes set this to true so that text will be rendered + * using curves and not the font. + */ + AbstractPSDocumentGraphics2D(boolean textAsShapes) { + super(textAsShapes); + + if (!textAsShapes) { + this.document = new Document(null); + FontSetup.setup(this.document, null); + } + } + + /** + * Setup the document. + * @param stream the output stream to write the document + * @param width the width of the page + * @param height the height of the page + * @throws IOException an io exception if there is a problem + * writing to the output stream + */ + public void setupDocument(OutputStream stream, int width, int height) throws IOException { + this.width = width; + this.height = height; + this.pagecount = 0; + this.pagePending = false; + + //Setup for PostScript generation + setPSGenerator(new PSGenerator(stream)); + + writeFileHeader(); + } + + protected abstract void writeFileHeader() throws IOException; + + /** + * Create a new AbstractPSDocumentGraphics2D. + * This is used to create a new PostScript document of the given height + * and width. + * The resulting document is written to the stream after rendering. + * + * @param textAsShapes set this to true so that text will be rendered + * using curves and not the font. + * @param stream the stream that the final document should be written to. + * @param width the width of the document + * @param height the height of the document + * @throws IOException an io exception if there is a problem + * writing to the output stream + */ + public AbstractPSDocumentGraphics2D(boolean textAsShapes, OutputStream stream, + int width, int height) throws IOException { + this(textAsShapes); + setupDocument(stream, width, height); + } + + /** + * Get the context document. + * @return the context document + */ + public Document getDocument() { + return this.document; + } + + /** + * Set the dimensions of the SVG document that will be drawn. + * This is useful if the dimensions of the SVG document are different + * from the PostScript document that is to be created. + * The result is scaled so that the SVG fits correctly inside the + * PostScript document. + * @param w the width of the page + * @param h the height of the page + * @throws IOException in case of an I/O problem + */ + public void setSVGDimension(float w, float h) throws IOException { + if (w != this.width || h != this.height) { + gen.concatMatrix(width / w, 0, 0, height / h, 0, 0); + } + } + + /** + * Set the background of the PostScript document. + * This is used to set the background for the PostScript document + * Rather than leaving it as the default white. + * @param col the background colour to fill + */ + public void setBackgroundColor(Color col) { + /**(todo) Implement this */ + /* + Color c = col; + PDFColor currentColour = new PDFColor(c.getRed(), c.getGreen(), c.getBlue()); + currentStream.write("q\n"); + currentStream.write(currentColour.getColorSpaceOut(true)); + + currentStream.write("0 0 " + width + " " + height + " re\n"); + + currentStream.write("f\n"); + currentStream.write("Q\n"); + */ + } + + public int getPageCount() { + return this.pagecount; + } + + public void nextPage() throws IOException { + closePage(); + } + + protected void closePage() throws IOException { + if (!this.pagePending) { + return; //ignore + } + //Finish page + writePageTrailer(); + this.pagePending = false; + } + + /** + * Writes the page header for a page. + * @throws IOException In case an I/O error occurs + */ + protected abstract void writePageHeader() throws IOException; + + /** + * Writes the page trailer for a page. + * @throws IOException In case an I/O error occurs + */ + protected abstract void writePageTrailer() throws IOException; + + + /** {@inheritDoc} */ + protected void preparePainting() { + if (this.pagePending) { + return; + } + try { + startPage(); + } catch (IOException ioe) { + handleIOException(ioe); + } + } + + protected void startPage() throws IOException { + if (this.pagePending) { + throw new IllegalStateException("Close page first before starting another"); + } + //Start page + this.pagecount++; + + if (this.initialTransform == null) { + //Save initial transformation matrix + this.initialTransform = getTransform(); + this.initialClip = getClip(); + } else { + //Reset transformation matrix + setTransform(this.initialTransform); + setClip(this.initialClip); + } + + writePageHeader(); + gen.writeln("0.001 0.001 scale"); + gen.concatMatrix(1, 0, 0, -1, 0, this.height * 1000); + gen.writeDSCComment(DSCConstants.END_PAGE_SETUP); + this.pagePending = true; + } + + /** + * The rendering process has finished. + * This should be called after the rendering has completed as there is + * no other indication it is complete. + * This will then write the results to the output stream. + * @throws IOException an io exception if there is a problem + * writing to the output stream + */ + public void finish() throws IOException { + if (this.pagePending) { + closePage(); + } + + //Finish document + gen.writeDSCComment(DSCConstants.TRAILER); + gen.writeDSCComment(DSCConstants.PAGES, new Integer(this.pagecount)); + gen.writeDSCComment(DSCConstants.EOF); + gen.flush(); + } + + /** + * This constructor supports the create method + * @param g the PostScript document graphics to make a copy of + */ + public AbstractPSDocumentGraphics2D(AbstractPSDocumentGraphics2D g) { + super(g); + } + + +} + diff --git a/src/java/org/apache/fop/render/ps/AbstractPSTranscoder.java b/src/java/org/apache/fop/render/ps/AbstractPSTranscoder.java new file mode 100644 index 000000000..a02b51c24 --- /dev/null +++ b/src/java/org/apache/fop/render/ps/AbstractPSTranscoder.java @@ -0,0 +1,195 @@ +/* + * $Id$ + * ============================================================================ + * The Apache Software License, Version 1.1 + * ============================================================================ + * + * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modifica- + * tion, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: "This product includes software + * developed by the Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "FOP" and "Apache Software Foundation" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", nor may + * "Apache" appear in their name, without prior written permission of the + * Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- + * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ============================================================================ + * + * This software consists of voluntary contributions made by many individuals + * on behalf of the Apache Software Foundation and was originally created by + * James Tauber . For more information on the Apache + * Software Foundation, please see . + */ +package org.apache.fop.render.ps; + + +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; + +import java.awt.Color; + +import java.net.MalformedURLException; +import java.net.URL; + +import java.io.IOException; + +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.container.ContainerUtil; +import org.apache.batik.bridge.BridgeContext; +import org.apache.batik.bridge.BridgeException; +import org.apache.batik.bridge.GVTBuilder; +import org.apache.batik.bridge.ViewBox; + +import org.apache.batik.dom.svg.SVGOMDocument; + +import org.apache.batik.gvt.GraphicsNode; + +import org.apache.batik.transcoder.TranscoderException; +import org.apache.batik.transcoder.TranscoderOutput; +import org.apache.batik.transcoder.image.resources.Messages; + +import org.apache.batik.transcoder.image.ImageTranscoder; + +import org.apache.fop.svg.AbstractFOPTranscoder; + +import org.w3c.dom.Document; +import org.w3c.dom.svg.SVGDocument; +import org.w3c.dom.svg.SVGSVGElement; + +/** + * This class enables to transcode an input to a PostScript document. + * + *

Two transcoding hints (KEY_WIDTH and + * KEY_HEIGHT) can be used to respectively specify the image + * width and the image height. If only one of these keys is specified, + * the transcoder preserves the aspect ratio of the original image. + * + *

The KEY_BACKGROUND_COLOR defines the background color + * to use for opaque image formats, or the background color that may + * be used for image formats that support alpha channel. + * + *

The KEY_AOI represents the area of interest to paint + * in device space. + * + *

Three additional transcoding hints that act on the SVG + * processor can be specified: + * + *

KEY_LANGUAGE to set the default language to use (may be + * used by a <switch> SVG element for example), + * KEY_USER_STYLESHEET_URI to fix the URI of a user + * stylesheet, and KEY_PIXEL_TO_MM to specify the pixel to + * millimeter conversion factor. + * + * @author Keiron Liddle + * @author Jeremias Maerki + * @version $Id$ + */ +public abstract class AbstractPSTranscoder extends AbstractFOPTranscoder { + + private Configuration cfg = null; + protected AbstractPSDocumentGraphics2D graphics = null; + + /** + * Constructs a new AbstractPSTranscoder. + */ + public AbstractPSTranscoder() { + super(); + } + + protected abstract AbstractPSDocumentGraphics2D createDocumentGraphics2D(); + + protected BridgeContext createBridgeContext() { + /*boolean stroke = true; + if (hints.containsKey(KEY_STROKE_TEXT)) { + stroke = ((Boolean)hints.get(KEY_STROKE_TEXT)).booleanValue(); + }*/ + + BridgeContext ctx = new BridgeContext(userAgent); + PSTextPainter textPainter = new PSTextPainter(graphics.getDocument()); + ctx.setTextPainter(textPainter); + ctx.putBridge(new PSTextElementBridge(textPainter)); + + //ctx.putBridge(new PSImageElementBridge()); + return ctx; + } + + /** + * Transcodes the specified Document as an image in the specified output. + * + * @param document the document to transcode + * @param uri the uri of the document or null if any + * @param output the ouput where to transcode + * @exception TranscoderException if an error occured while transcoding + */ + protected void transcode(Document document, String uri, + TranscoderOutput output) + throws TranscoderException { + + graphics = createDocumentGraphics2D(); + ContainerUtil.enableLogging(graphics, getLogger()); + try { + if (this.cfg != null) { + ContainerUtil.configure(graphics, this.cfg); + } + ContainerUtil.initialize(graphics); + } catch (Exception e) { + throw new TranscoderException( + "Error while setting up PDFDocumentGraphics2D", e); + } + + super.transcode(document, uri, output); + + // prepare the image to be painted + int w = (int)(width + 0.5); + int h = (int)(height + 0.5); + + try { + graphics.setupDocument(output.getOutputStream(), w, h); + graphics.setSVGDimension(width, height); + + if (hints.containsKey(ImageTranscoder.KEY_BACKGROUND_COLOR)) { + graphics.setBackgroundColor + ((Color)hints.get(ImageTranscoder.KEY_BACKGROUND_COLOR)); + } + graphics.setGraphicContext + (new org.apache.batik.ext.awt.g2d.GraphicContext()); + graphics.setTransform(curTxf); + + this.root.paint(graphics); + + graphics.finish(); + } catch (IOException ex) { + throw new TranscoderException(ex); + } + } + + +} diff --git a/src/java/org/apache/fop/render/ps/DSCConstants.java b/src/java/org/apache/fop/render/ps/DSCConstants.java index 40da71313..fe8570975 100644 --- a/src/java/org/apache/fop/render/ps/DSCConstants.java +++ b/src/java/org/apache/fop/render/ps/DSCConstants.java @@ -64,6 +64,9 @@ public class DSCConstants { /** Lead-in for a DSC-conformant PostScript file */ public static final String PS_ADOBE_30 = "%!PS-Adobe-3.0"; + /** Lead-in for an EPS file */ + public static final String EPSF_30 = "EPSF-3.0"; + /** Bounding box for the document */ public static final String BBOX = "BoundingBox"; /** High-resolution bounding box for the document */ diff --git a/src/java/org/apache/fop/render/ps/EPSDocumentGraphics2D.java b/src/java/org/apache/fop/render/ps/EPSDocumentGraphics2D.java new file mode 100644 index 000000000..b38a4adae --- /dev/null +++ b/src/java/org/apache/fop/render/ps/EPSDocumentGraphics2D.java @@ -0,0 +1,124 @@ +/* + * $Id$ + * ============================================================================ + * The Apache Software License, Version 1.1 + * ============================================================================ + * + * Copyright (C) 2003 The Apache Software Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modifica- + * tion, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: "This product includes software + * developed by the Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "FOP" and "Apache Software Foundation" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", nor may + * "Apache" appear in their name, without prior written permission of the + * Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- + * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ============================================================================ + * + * This software consists of voluntary contributions made by many individuals + * on behalf of the Apache Software Foundation and was originally created by + * James Tauber . For more information on the Apache + * Software Foundation, please see . + */ +package org.apache.fop.render.ps; + +import java.io.IOException; + +/** + * This class is a wrapper for the AbstractPSDocumentGraphics2D that + * is used to create EPS (Encapsulated PostScript) files instead of PS file. + * + * @author Jeremias Maerki + * @version $Id$ + * @see org.apache.fop.render.ps.PSGraphics2D + * @see org.apache.fop.render.ps.AbstractPSDocumentGraphics2D + */ +public class EPSDocumentGraphics2D extends AbstractPSDocumentGraphics2D { + + /** + * Create a new EPSDocumentGraphics2D. + * This is used to create a new EPS document, the height, + * width and output stream can be setup later. + * For use by the transcoder which needs font information + * for the bridge before the document size is known. + * The resulting document is written to the stream after rendering. + * + * @param textAsShapes set this to true so that text will be rendered + * using curves and not the font. + */ + public EPSDocumentGraphics2D(boolean textAsShapes) { + super(textAsShapes); + } + + protected void writeFileHeader() throws IOException { + final Long pagewidth = new Long(this.width); + final Long pageheight = new Long(this.height); + + //PostScript Header + gen.writeln(DSCConstants.PS_ADOBE_30 + " " + DSCConstants.EPSF_30); + gen.writeDSCComment(DSCConstants.CREATOR, + new String[] {"FOP EPS Transcoder for SVG"}); + gen.writeDSCComment(DSCConstants.CREATION_DATE, + new Object[] {new java.util.Date()}); + gen.writeDSCComment(DSCConstants.PAGES, new Integer(0)); + gen.writeDSCComment(DSCConstants.BBOX, new Object[] + {ZERO, ZERO, pagewidth, pageheight}); + gen.writeDSCComment(DSCConstants.LANGUAGE_LEVEL, new Integer(2)); + gen.writeDSCComment(DSCConstants.END_COMMENTS); + + //Prolog + gen.writeDSCComment(DSCConstants.BEGIN_PROLOG); + PSProcSets.writeFOPStdProcSet(gen); + PSProcSets.writeFOPEPSProcSet(gen); + if (document != null) { + PSProcSets.writeFontDict(gen, document); + } + gen.writeDSCComment(DSCConstants.END_PROLOG); + } + + protected void writePageHeader() throws IOException { + Integer pageNumber = new Integer(this.pagecount); + gen.writeDSCComment(DSCConstants.PAGE, new Object[] + {pageNumber.toString(), pageNumber}); + gen.writeDSCComment(DSCConstants.PAGE_BBOX, new Object[] + {ZERO, ZERO, new Integer(width), new Integer(height)}); + gen.writeDSCComment(DSCConstants.BEGIN_PAGE_SETUP); + if (this.document != null) { + gen.writeln("FOPFonts begin"); + } + } + + protected void writePageTrailer() throws IOException { + gen.writeDSCComment(DSCConstants.PAGE_TRAILER); + gen.writeDSCComment(DSCConstants.END_PAGE); + } + +} diff --git a/src/java/org/apache/fop/render/ps/EPSTranscoder.java b/src/java/org/apache/fop/render/ps/EPSTranscoder.java new file mode 100644 index 000000000..602268b53 --- /dev/null +++ b/src/java/org/apache/fop/render/ps/EPSTranscoder.java @@ -0,0 +1,94 @@ +/* + * $Id$ + * ============================================================================ + * The Apache Software License, Version 1.1 + * ============================================================================ + * + * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modifica- + * tion, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: "This product includes software + * developed by the Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "FOP" and "Apache Software Foundation" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", nor may + * "Apache" appear in their name, without prior written permission of the + * Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- + * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ============================================================================ + * + * This software consists of voluntary contributions made by many individuals + * on behalf of the Apache Software Foundation and was originally created by + * James Tauber . For more information on the Apache + * Software Foundation, please see . + */ +package org.apache.fop.render.ps; + +/** + * This class enables to transcode an input to a EPS document. + * + *

Two transcoding hints (KEY_WIDTH and + * KEY_HEIGHT) can be used to respectively specify the image + * width and the image height. If only one of these keys is specified, + * the transcoder preserves the aspect ratio of the original image. + * + *

The KEY_BACKGROUND_COLOR defines the background color + * to use for opaque image formats, or the background color that may + * be used for image formats that support alpha channel. + * + *

The KEY_AOI represents the area of interest to paint + * in device space. + * + *

Three additional transcoding hints that act on the SVG + * processor can be specified: + * + *

KEY_LANGUAGE to set the default language to use (may be + * used by a <switch> SVG element for example), + * KEY_USER_STYLESHEET_URI to fix the URI of a user + * stylesheet, and KEY_PIXEL_TO_MM to specify the pixel to + * millimeter conversion factor. + * + * @author Keiron Liddle + * @author Jeremias Maerki + * @version $Id$ + */ +public class EPSTranscoder extends AbstractPSTranscoder { + + /** + * Constructs a new EPSPSTranscoder. + */ + public EPSTranscoder() { + super(); + } + + protected AbstractPSDocumentGraphics2D createDocumentGraphics2D() { + return new EPSDocumentGraphics2D(false); + } + +} diff --git a/src/java/org/apache/fop/render/ps/PSDocumentGraphics2D.java b/src/java/org/apache/fop/render/ps/PSDocumentGraphics2D.java index 35c3fc7fd..3c811d476 100644 --- a/src/java/org/apache/fop/render/ps/PSDocumentGraphics2D.java +++ b/src/java/org/apache/fop/render/ps/PSDocumentGraphics2D.java @@ -1,5 +1,5 @@ /* - * $Id: PDFDocumentGraphics2D.java,v 1.27 2003/03/07 09:51:26 jeremias Exp $ + * $Id$ * ============================================================================ * The Apache Software License, Version 1.1 * ============================================================================ @@ -52,17 +52,12 @@ package org.apache.fop.render.ps; //Java import java.awt.Graphics; -import java.awt.Font; -import java.awt.Color; -import java.awt.Shape; -import java.awt.font.FontRenderContext; -import java.awt.font.GlyphVector; import java.io.OutputStream; import java.io.IOException; //FOP -import org.apache.fop.render.pdf.FontSetup; import org.apache.fop.apps.Document; +import org.apache.fop.render.pdf.FontSetup; /** * This class is a wrapper for the PSGraphics2D that @@ -71,16 +66,14 @@ import org.apache.fop.apps.Document; * * @author Keiron Liddle * @author Jeremias Maerki - * @version $Id: PDFDocumentGraphics2D.java,v 1.27 2003/03/07 09:51:26 jeremias Exp $ + * @version $Id$ * @see org.apache.fop.render.ps.PSGraphics2D */ -public class PSDocumentGraphics2D extends PSGraphics2D { - - private int width; - private int height; +public class PSDocumentGraphics2D extends AbstractPSDocumentGraphics2D { + /** - * Create a new PSDocumentGraphics2D. + * Create a new AbstractPSDocumentGraphics2D. * This is used to create a new PostScript document, the height, * width and output stream can be setup later. * For use by the transcoder which needs font information @@ -94,158 +87,90 @@ public class PSDocumentGraphics2D extends PSGraphics2D { super(textAsShapes); if (!textAsShapes) { - fontInfo = new Document(null); - FontSetup.setup(fontInfo, null); - //FontState fontState = new FontState("Helvetica", "normal", - // FontInfo.NORMAL, 12, 0); + this.document = new Document(null); + FontSetup.setup(this.document, null); } - - currentFontName = ""; - currentFontSize = 0; } /** - * Setup the document. - * @param stream the output stream to write the document - * @param width the width of the page - * @param height the height of the page + * Create a new AbstractPSDocumentGraphics2D. + * This is used to create a new PostScript document of the given height + * and width. + * The resulting document is written to the stream after rendering. + * + * @param textAsShapes set this to true so that text will be rendered + * using curves and not the font. + * @param stream the stream that the final document should be written to. + * @param width the width of the document + * @param height the height of the document * @throws IOException an io exception if there is a problem * writing to the output stream */ - public void setupDocument(OutputStream stream, int width, int height) throws IOException { - this.width = width; - this.height = height; + public PSDocumentGraphics2D(boolean textAsShapes, OutputStream stream, + int width, int height) throws IOException { + this(textAsShapes); + setupDocument(stream, width, height); + } - final Integer zero = new Integer(0); + public void nextPage() throws IOException { + closePage(); + } + + protected void writeFileHeader() throws IOException { final Long pagewidth = new Long(this.width); final Long pageheight = new Long(this.height); - //Setup for PostScript generation - setPSGenerator(new PSGenerator(stream)); - //PostScript Header gen.writeln(DSCConstants.PS_ADOBE_30); gen.writeDSCComment(DSCConstants.CREATOR, new String[] {"FOP PostScript Transcoder for SVG"}); gen.writeDSCComment(DSCConstants.CREATION_DATE, new Object[] {new java.util.Date()}); - gen.writeDSCComment(DSCConstants.PAGES, new Object[] {new Integer(1)}); + gen.writeDSCComment(DSCConstants.PAGES, PSGenerator.ATEND); gen.writeDSCComment(DSCConstants.BBOX, new Object[] - {zero, zero, pagewidth, pageheight}); + {ZERO, ZERO, pagewidth, pageheight}); gen.writeDSCComment(DSCConstants.END_COMMENTS); - + //Defaults gen.writeDSCComment(DSCConstants.BEGIN_DEFAULTS); gen.writeDSCComment(DSCConstants.END_DEFAULTS); - + //Prolog gen.writeDSCComment(DSCConstants.BEGIN_PROLOG); gen.writeDSCComment(DSCConstants.END_PROLOG); - + //Setup gen.writeDSCComment(DSCConstants.BEGIN_SETUP); PSProcSets.writeFOPStdProcSet(gen); PSProcSets.writeFOPEPSProcSet(gen); - PSProcSets.writeFontDict(gen, fontInfo); + if (document != null) { + PSProcSets.writeFontDict(gen, document); + } gen.writeDSCComment(DSCConstants.END_SETUP); + } - //Start page - Integer pageNumber = new Integer(1); - gen.writeDSCComment(DSCConstants.PAGE, new Object[] + protected void writePageHeader() throws IOException { + Integer pageNumber = new Integer(this.pagecount); + gen.writeDSCComment(DSCConstants.PAGE, new Object[] {pageNumber.toString(), pageNumber}); gen.writeDSCComment(DSCConstants.PAGE_BBOX, new Object[] - {zero, zero, pagewidth, pageheight}); + {ZERO, ZERO, new Integer(width), new Integer(height)}); gen.writeDSCComment(DSCConstants.BEGIN_PAGE_SETUP); - gen.writeln("FOPFonts begin"); - gen.writeln("0.001 0.001 scale"); - gen.concatMatrix(1, 0, 0, -1, 0, pageheight.doubleValue() * 1000); - gen.writeDSCComment(DSCConstants.END_PAGE_SETUP); - - } - - /** - * Create a new PSDocumentGraphics2D. - * This is used to create a new PostScript document of the given height - * and width. - * The resulting document is written to the stream after rendering. - * - * @param textAsShapes set this to true so that text will be rendered - * using curves and not the font. - * @param stream the stream that the final document should be written to. - * @param width the width of the document - * @param height the height of the document - * @throws IOException an io exception if there is a problem - * writing to the output stream - */ - public PSDocumentGraphics2D(boolean textAsShapes, OutputStream stream, - int width, int height) throws IOException { - this(textAsShapes); - setupDocument(stream, width, height); - } - - /** - * Get the font info for this PostScript document. - * @return the font information - */ - public Document getFontInfo() { - return fontInfo; - } - - /** - * Set the dimensions of the SVG document that will be drawn. - * This is useful if the dimensions of the SVG document are different - * from the PostScript document that is to be created. - * The result is scaled so that the SVG fits correctly inside the - * PostScript document. - * @param w the width of the page - * @param h the height of the page - * @throws IOException in case of an I/O problem - */ - public void setSVGDimension(float w, float h) throws IOException { - gen.concatMatrix(width / w, 0, 0, height / h, 0, 0); - } - - /** - * Set the background of the PostScript document. - * This is used to set the background for the PostScript document - * Rather than leaving it as the default white. - * @param col the background colour to fill - */ - public void setBackgroundColor(Color col) { - /**(todo) Implement this */ - /* - Color c = col; - PDFColor currentColour = new PDFColor(c.getRed(), c.getGreen(), c.getBlue()); - currentStream.write("q\n"); - currentStream.write(currentColour.getColorSpaceOut(true)); - - currentStream.write("0 0 " + width + " " + height + " re\n"); - - currentStream.write("f\n"); - currentStream.write("Q\n"); - */ + gen.writeln("<<"); + gen.writeln("/PageSize [" + width + " " + height + "]"); + gen.writeln("/ImagingBBox null"); + gen.writeln(">> setpagedevice"); + if (this.document != null) { + gen.writeln("FOPFonts begin"); + } } - - /** - * The rendering process has finished. - * This should be called after the rendering has completed as there is - * no other indication it is complete. - * This will then write the results to the output stream. - * @throws IOException an io exception if there is a problem - * writing to the output stream - */ - public void finish() throws IOException { - //Finish page - gen.writeln("showpage"); + + protected void writePageTrailer() throws IOException { + gen.writeln("showpage"); gen.writeDSCComment(DSCConstants.PAGE_TRAILER); gen.writeDSCComment(DSCConstants.END_PAGE); - - //Finish document - gen.writeDSCComment(DSCConstants.TRAILER); - gen.writeDSCComment(DSCConstants.EOF); - gen.flush(); } - + /** * This constructor supports the create method * @param g the PostScript document graphics to make a copy of @@ -264,25 +189,5 @@ public class PSDocumentGraphics2D extends PSGraphics2D { return new PSDocumentGraphics2D(this); } - /** - * Draw a string to the PostScript document. - * This either draws the string directly or if drawing text as - * shapes it converts the string into shapes and draws that. - * @param s the string to draw - * @param x the x position - * @param y the y position - */ - public void drawString(String s, float x, float y) { - if (super.textAsShapes) { - Font font = super.getFont(); - FontRenderContext frc = super.getFontRenderContext(); - GlyphVector gv = font.createGlyphVector(frc, s); - Shape glyphOutline = gv.getOutline(x, y); - super.fill(glyphOutline); - } else { - super.drawString(s, x, y); - } - } - } diff --git a/src/java/org/apache/fop/render/ps/PSGenerator.java b/src/java/org/apache/fop/render/ps/PSGenerator.java index 988d53bd6..156c77149 100644 --- a/src/java/org/apache/fop/render/ps/PSGenerator.java +++ b/src/java/org/apache/fop/render/ps/PSGenerator.java @@ -50,11 +50,11 @@ */ package org.apache.fop.render.ps; +import java.awt.geom.AffineTransform; import java.io.OutputStream; import java.io.IOException; import java.text.DateFormat; import java.text.DecimalFormat; -import java.text.NumberFormat; import java.util.Date; import java.util.Stack; @@ -71,13 +71,17 @@ public class PSGenerator { * Indicator for the PostScript interpreter that the value is provided * later in the document (mostly in the %%Trailer section). */ - public static final AtendIndicator ATEND = new AtendIndicator() {}; + public static final AtendIndicator ATEND = new AtendIndicator() { + }; private OutputStream out; + private boolean commentsEnabled = true; private Stack graphicsStateStack = new Stack(); private PSState currentState; - private DecimalFormat df = new DecimalFormat("0.000"); + private DecimalFormat df3 = new DecimalFormat("0.000"); + private DecimalFormat df1 = new DecimalFormat("0.#"); + private DecimalFormat df5 = new DecimalFormat("0.#####"); private StringBuffer tempBuffer = new StringBuffer(256); @@ -87,6 +91,14 @@ public class PSGenerator { this.currentState = new PSState(); this.graphicsStateStack.push(this.currentState); } + + /** + * Returns the OutputStream the PSGenerator writes to. + * @return the OutputStream + */ + public OutputStream getOutputStream() { + return this.out; + } /** * Writes a newline character to the OutputStream. @@ -104,8 +116,17 @@ public class PSGenerator { * @return the formatted value */ public String formatDouble(double value) { - NumberFormat nf = new java.text.DecimalFormat("0.#"); - return nf.format(value); + return df1.format(value); + } + + /** + * Formats a double value for PostScript output (higher resolution). + * + * @param value value to format + * @return the formatted value + */ + public String formatDouble5(double value) { + return df5.format(value); } /** @@ -132,6 +153,12 @@ public class PSGenerator { newLine(); } + public void commentln(String comment) throws IOException { + if (this.commentsEnabled) { + writeln(comment); + } + } + /** * Writes encoded data to the PostScript stream. * @@ -290,12 +317,12 @@ public class PSGenerator { } else if (params[i] instanceof AtendIndicator) { tempBuffer.append("(atend)"); } else if (params[i] instanceof Double) { - tempBuffer.append(df.format(params[i])); + tempBuffer.append(df3.format(params[i])); } else if (params[i] instanceof Number) { tempBuffer.append(params[i].toString()); } else if (params[i] instanceof Date) { - DateFormat df = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); - tempBuffer.append(convertStringToDSC(df.format((Date)params[i]))); + DateFormat datef = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + tempBuffer.append(convertStringToDSC(datef.format((Date)params[i]))); } else { throw new IllegalArgumentException("Unsupported parameter type: " + params[i].getClass().getName()); @@ -340,12 +367,12 @@ public class PSGenerator { public void concatMatrix(double a, double b, double c, double d, double e, double f) throws IOException { - writeln("[" + formatDouble(a) + " " - + formatDouble(b) + " " - + formatDouble(c) + " " - + formatDouble(d) + " " - + formatDouble(e) + " " - + formatDouble(f) + "] concat"); + writeln("[" + formatDouble5(a) + " " + + formatDouble5(b) + " " + + formatDouble5(c) + " " + + formatDouble5(d) + " " + + formatDouble5(e) + " " + + formatDouble5(f) + "] concat"); } /** @@ -359,6 +386,34 @@ public class PSGenerator { matrix[4], matrix[5]); } + /** + * Concats the transformations matric. + * @param at the AffineTransform whose matrix to use + * @exception IOException In case of an I/O problem + */ + public void concatMatrix(AffineTransform at) throws IOException { + double[] matrix = new double[6]; + at.getMatrix(matrix); + concatMatrix(matrix); + } + + /** + * Adds a rectangle to the current path. + * @param x upper left corner + * @param y upper left corner + * @param w width + * @param h height + * @exception IOException In case of an I/O problem + */ + public void defineRect(double x, double y, double w, double h) + throws IOException { + writeln(formatDouble(x) + + " " + formatDouble(y) + + " " + formatDouble(w) + + " " + formatDouble(h) + + " re"); + } + /** * Returns the current graphics state. * @return the current graphics state diff --git a/src/java/org/apache/fop/render/ps/PSGraphics2D.java b/src/java/org/apache/fop/render/ps/PSGraphics2D.java index 98aa6fdbf..b017c0ad7 100644 --- a/src/java/org/apache/fop/render/ps/PSGraphics2D.java +++ b/src/java/org/apache/fop/render/ps/PSGraphics2D.java @@ -51,15 +51,13 @@ package org.apache.fop.render.ps; //Java -import java.util.List; import java.text.AttributedCharacterIterator; -import java.text.CharacterIterator; import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Dimension; -/* java.awt.Font is not imported to avoid confusion with - org.apache.fop.fonts.Font */ +/* java.awt.Font is not imported to avoid confusion with + org.apache.fop.fonts.Font */ import java.awt.GradientPaint; import java.awt.Graphics; import java.awt.Graphics2D; @@ -71,9 +69,11 @@ import java.awt.Rectangle; import java.awt.Shape; import java.awt.Stroke; import java.awt.TexturePaint; +import java.awt.color.ColorSpace; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; import java.awt.geom.AffineTransform; import java.awt.geom.PathIterator; -import java.awt.geom.Point2D; import java.awt.image.BufferedImage; import java.awt.image.DataBuffer; import java.awt.image.DataBufferInt; @@ -83,14 +83,13 @@ import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderableImage; import java.io.IOException; -// FOP -import org.apache.fop.apps.Document; -import org.apache.fop.fonts.Font; - -// Batik +//Batik import org.apache.batik.ext.awt.g2d.AbstractGraphics2D; import org.apache.batik.ext.awt.g2d.GraphicContext; +//FOP +import org.apache.fop.fonts.Font; +import org.apache.fop.apps.Document; /** * This concrete implementation of AbstractGraphics2D is a @@ -106,46 +105,30 @@ import org.apache.batik.ext.awt.g2d.GraphicContext; */ public class PSGraphics2D extends AbstractGraphics2D { - private boolean standalone = false; - - /** - * the PostScript genertaor being created - */ + /** the PostScript generator being created */ protected PSGenerator gen; - /** Currently valid FontState */ - protected Font fontState; + private boolean clippingDisabled = true; + /** Currently valid FontState */ + protected Font font; + /** Overriding FontState */ - protected Font overrideFontState = null; - - /** - * the current (internal) font name - */ + protected Font overrideFont = null; + + /** the current (internal) font name */ protected String currentFontName; - /** - * the current font size in millipoints - */ + /** the current font size in millipoints */ protected int currentFontSize; - /** - * the current vertical position in millipoints from bottom - */ - protected int currentYPosition = 0; - - /** - * the current horizontal position in millipoints from left - */ - protected int currentXPosition = 0; - /** * the current colour for use in svg */ protected Color currentColour = new Color(0, 0, 0); /** FontInfo containing all available fonts */ - protected Document fontInfo; + protected Document document; /** * Create a new Graphics2D that generates PostScript code. @@ -209,6 +192,14 @@ public class PSGraphics2D extends AbstractGraphics2D { ioe.printStackTrace(); } + /** + * This method is used by AbstractPSDocumentGraphics2D to prepare a new page if + * necessary. + */ + protected void preparePainting() { + //nop, used by AbstractPSDocumentGraphics2D + } + /** * Draws as much of the specified image as is currently available. * The image is drawn with its top-left corner at @@ -236,7 +227,8 @@ public class PSGraphics2D extends AbstractGraphics2D { */ public boolean drawImage(Image img, int x, int y, ImageObserver observer) { - // System.err.println("drawImage:x, y"); + preparePainting(); + System.out.println("drawImage: x, y " + img.getClass().getName()); final int width = img.getWidth(observer); final int height = img.getHeight(observer); @@ -289,23 +281,24 @@ public class PSGraphics2D extends AbstractGraphics2D { // error break; } - - /*try { +/* + try { FopImage fopimg = new TempImage(width, height, result, mask); AffineTransform at = getTransform(); double[] matrix = new double[6]; at.getMatrix(matrix); - psRenderer.write("gsave"); + gen.saveGraphicsState(); Shape imclip = getClip(); writeClip(imclip); // psRenderer.write("" + matrix[0] + " " + matrix[1] + // " " + matrix[2] + " " + matrix[3] + " " + // matrix[4] + " " + matrix[5] + " cm\n"); //psRenderer.renderBitmap(fopimg, x, y, width, height); - psRenderer.write("grestore"); - } catch (Exception e) { - e.printStackTrace(); - }*/ + gen.restoreGraphicsState(); + } catch (IOException ioe) { + handleIOException(ioe); + } +*/ return true; } @@ -319,26 +312,25 @@ public class PSGraphics2D extends AbstractGraphics2D { BufferedImage.TYPE_INT_ARGB); } - /*class TempImage implements FopImage { - int m_height; - int m_width; - int m_bitsPerPixel; - PDFColorSpace m_colorSpace; - int m_bitmapSiye; - byte[] m_bitmaps; - byte[] m_mask; +/* + class TempImage implements FopImage { + int height; + int width; + int bitsPerPixel; + ColorSpace colorSpace; + int bitmapSiye; + byte[] bitmaps; + byte[] mask; PDFColor transparent = new PDFColor(255, 255, 255); TempImage(int width, int height, byte[] result, byte[] mask) { - this.m_height = height; - this.m_width = width; - this.m_bitsPerPixel = 8; - this.m_colorSpace = new PDFColorSpace(PDFColorSpace.DEVICE_RGB); - // this.m_isTransparent = false; - // this.m_bitmapsSize = this.m_width * this.m_height * 3; - this.m_bitmaps = result; - this.m_mask = mask; + this.height = height; + this.width = width; + this.bitsPerPixel = 8; + this.colorSpace = ColorSpace.new PDFColorSpace(PDFColorSpace.DEVICE_RGB); + this.bitmaps = result; + this.mask = mask; } public boolean load(int type, FOUserAgent ua) { @@ -350,55 +342,55 @@ public class PSGraphics2D extends AbstractGraphics2D { } public String getURL() { - return "" + m_bitmaps; + return "" + this.bitmaps; } // image size public int getWidth() { - return m_width; + return this.width; } public int getHeight() { - return m_height; + return this.height; } // DeviceGray, DeviceRGB, or DeviceCMYK - public PDFColorSpace getColorSpace() { - return m_colorSpace; + public ColorSpace getColorSpace() { + return this.colorSpace; } // bits per pixel public int getBitsPerPixel() { - return m_bitsPerPixel; + return this.bitsPerPixel; } // For transparent images public boolean isTransparent() { - return transparent != null; + return this.transparent != null; } public PDFColor getTransparentColor() { - return transparent; + return this.transparent; } public boolean hasSoftMask() { - return m_mask != null; + return this.mask != null; } public byte[] getSoftMask() { - return m_mask; + return this.mask; } // get the image bytes, and bytes properties // get uncompressed image bytes public byte[] getBitmaps() { - return m_bitmaps; + return this.bitmaps; } // width * (bitsPerPixel / 8) * height, no ? public int getBitmapsSize() { - return m_width * m_height * 3; + return getWidth() * getHeight() * 3; //Assumes RGB! } // get compressed image bytes @@ -418,10 +410,16 @@ public class PSGraphics2D extends AbstractGraphics2D { } // release memory - public void close() {} + public void close() { + //nop + } - }*/ + public ICC_Profile getICCProfile() { + return null; + } + } +*/ /** * Draws as much of the specified image as has already been scaled @@ -459,6 +457,7 @@ public class PSGraphics2D extends AbstractGraphics2D { */ public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) { + preparePainting(); System.out.println("drawImage"); return true; } @@ -493,10 +492,54 @@ public class PSGraphics2D extends AbstractGraphics2D { public void dispose() { // System.out.println("dispose"); this.gen = null; - fontState = null; - currentFontName = null; - currentColour = null; - fontInfo = null; + this.font = null; + this.currentColour = null; + this.document = null; + } + + /** + * Processes a path iterator generating the nexessary painting operations. + * @param iter PathIterator to process + * @throws IOException In case of an I/O problem. + */ + public void processPathIterator(PathIterator iter) throws IOException { + double[] vals = new double[6]; + while (!iter.isDone()) { + int type = iter.currentSegment(vals); + switch (type) { + case PathIterator.SEG_CUBICTO: + gen.writeln(gen.formatDouble(1000 * vals[0]) + " " + + gen.formatDouble(1000 * vals[1]) + " " + + gen.formatDouble(1000 * vals[2]) + " " + + gen.formatDouble(1000 * vals[3]) + " " + + gen.formatDouble(1000 * vals[4]) + " " + + gen.formatDouble(1000 * vals[5]) + + " curveto"); + break; + case PathIterator.SEG_LINETO: + gen.writeln(gen.formatDouble(1000 * vals[0]) + " " + + gen.formatDouble(1000 * vals[1]) + + " lineto"); + break; + case PathIterator.SEG_MOVETO: + gen.writeln(gen.formatDouble(1000 * vals[0]) + " " + + gen.formatDouble(1000 * vals[1]) + + " M"); + break; + case PathIterator.SEG_QUADTO: + gen.writeln(gen.formatDouble(1000 * vals[0]) + " " + + gen.formatDouble(1000 * vals[1]) + " " + + gen.formatDouble(1000 * vals[2]) + " " + + gen.formatDouble(1000 * vals[3]) + " QUADTO "); + break; + case PathIterator.SEG_CLOSE: + gen.writeln("closepath"); + break; + default: + break; + } + iter.next(); + } } /** @@ -516,58 +559,20 @@ public class PSGraphics2D extends AbstractGraphics2D { * @see #setComposite */ public void draw(Shape s) { + preparePainting(); try { // System.out.println("draw(Shape)"); gen.saveGraphicsState(); Shape imclip = getClip(); writeClip(imclip); - Color c = getColor(); - gen.writeln(gen.formatDouble(c.getRed() / 255.0) + " " - + gen.formatDouble(c.getGreen() / 255.0) + " " - + gen.formatDouble(c.getBlue() / 255.0) + " setrgbcolor"); + establishColor(getColor()); applyPaint(getPaint(), false); applyStroke(getStroke()); gen.writeln("newpath"); PathIterator iter = s.getPathIterator(getTransform()); - while (!iter.isDone()) { - double vals[] = new double[6]; - int type = iter.currentSegment(vals); - switch (type) { - case PathIterator.SEG_CUBICTO: - gen.writeln(gen.formatDouble(1000 * vals[0]) + " " - + gen.formatDouble(1000 * vals[1]) + " " - + gen.formatDouble(1000 * vals[2]) + " " - + gen.formatDouble(1000 * vals[3]) + " " - + gen.formatDouble(1000 * vals[4]) + " " - + gen.formatDouble(1000 * vals[5]) - + " curveto"); - break; - case PathIterator.SEG_LINETO: - gen.writeln(gen.formatDouble(1000 * vals[0]) + " " - + gen.formatDouble(1000 * vals[1]) - + " lineto"); - break; - case PathIterator.SEG_MOVETO: - gen.writeln(gen.formatDouble(1000 * vals[0]) + " " - + gen.formatDouble(1000 * vals[1]) - + " M"); - break; - case PathIterator.SEG_QUADTO: - gen.writeln(gen.formatDouble(1000 * vals[0]) + " " - + gen.formatDouble(1000 * vals[1]) + " " - + gen.formatDouble(1000 * vals[2]) + " " - + gen.formatDouble(1000 * vals[3]) + " QUADTO "); - break; - case PathIterator.SEG_CLOSE: - gen.writeln("closepath"); - break; - default: - break; - } - iter.next(); - } + processPathIterator(iter); doDrawing(false, true, false); gen.restoreGraphicsState(); } catch (IOException ioe) { @@ -580,50 +585,20 @@ public class PSGraphics2D extends AbstractGraphics2D { * @param s Shape defining the clipping region */ protected void writeClip(Shape s) { - try { - PathIterator iter = s.getPathIterator(getTransform()); - gen.writeln("newpath"); - while (!iter.isDone()) { - double vals[] = new double[6]; - int type = iter.currentSegment(vals); - switch (type) { - case PathIterator.SEG_CUBICTO: - gen.writeln(gen.formatDouble(1000 * vals[0]) + " " - + gen.formatDouble(1000 * vals[1]) + " " - + gen.formatDouble(1000 * vals[2]) + " " - + gen.formatDouble(1000 * vals[3]) + " " - + gen.formatDouble(1000 * vals[4]) + " " - + gen.formatDouble(1000 * vals[5]) - + " curveto"); - break; - case PathIterator.SEG_LINETO: - gen.writeln(gen.formatDouble(1000 * vals[0]) + " " - + gen.formatDouble(1000 * vals[1]) - + " lineto"); - break; - case PathIterator.SEG_MOVETO: - gen.writeln(gen.formatDouble(1000 * vals[0]) + " " - + gen.formatDouble(1000 * vals[1]) - + " M"); - break; - case PathIterator.SEG_QUADTO: - gen.writeln(gen.formatDouble(1000 * vals[0]) + " " - + gen.formatDouble(1000 * vals[1]) + " " - + gen.formatDouble(1000 * vals[2]) + " " - + gen.formatDouble(1000 * vals[3]) + " QUADTO "); - break; - case PathIterator.SEG_CLOSE: - gen.writeln("closepath"); - break; - default: - break; - } - iter.next(); + if (s == null) { + return; + } + if (!this.clippingDisabled) { + preparePainting(); + try { + gen.writeln("newpath"); + PathIterator iter = s.getPathIterator(getTransform()); + processPathIterator(iter); + // clip area + gen.writeln("clippath"); + } catch (IOException ioe) { + handleIOException(ioe); } - // clip area - gen.writeln("clippath"); - } catch (IOException ioe) { - handleIOException(ioe); } } @@ -633,52 +608,11 @@ public class PSGraphics2D extends AbstractGraphics2D { * @param fill True if to be applied for filling */ protected void applyPaint(Paint paint, boolean fill) { + preparePainting(); if (paint instanceof GradientPaint) { - GradientPaint gp = (GradientPaint)paint; - Color c1 = gp.getColor1(); - Color c2 = gp.getColor2(); - Point2D p1 = gp.getPoint1(); - Point2D p2 = gp.getPoint2(); - //boolean cyclic = gp.isCyclic(); - - List theCoords = new java.util.ArrayList(); - theCoords.add(new Double(p1.getX())); - theCoords.add(new Double(p1.getY())); - theCoords.add(new Double(p2.getX())); - theCoords.add(new Double(p2.getY())); - - List theExtend = new java.util.ArrayList(); - theExtend.add(new Boolean(true)); - theExtend.add(new Boolean(true)); - - List theDomain = new java.util.ArrayList(); - theDomain.add(new Double(0)); - theDomain.add(new Double(1)); - - List theEncode = new java.util.ArrayList(); - theEncode.add(new Double(0)); - theEncode.add(new Double(1)); - theEncode.add(new Double(0)); - theEncode.add(new Double(1)); - - List theBounds = new java.util.ArrayList(); - theBounds.add(new Double(0)); - theBounds.add(new Double(1)); - - //List theFunctions = new java.util.ArrayList(); - - List someColors = new java.util.ArrayList(); - - Color color1 = new Color(c1.getRed(), c1.getGreen(), - c1.getBlue()); - someColors.add(color1); - Color color2 = new Color(c2.getRed(), c2.getGreen(), - c2.getBlue()); - someColors.add(color2); - - //PDFColorSpace aColorSpace = new PDFColorSpace(PDFColorSpace.DEVICE_RGB); + //NYI } else if (paint instanceof TexturePaint) { - //nop + //NYI } } @@ -687,33 +621,34 @@ public class PSGraphics2D extends AbstractGraphics2D { * @param stroke Stroke object to use */ protected void applyStroke(Stroke stroke) { + preparePainting(); try { if (stroke instanceof BasicStroke) { BasicStroke bs = (BasicStroke)stroke; float[] da = bs.getDashArray(); if (da != null) { - gen.writeln("["); + gen.write("["); for (int count = 0; count < da.length; count++) { - gen.writeln("" + (1000 * (int)da[count])); + gen.write("" + (1000 * (int)da[count])); if (count < da.length - 1) { - gen.writeln(" "); + gen.write(" "); } } - gen.writeln("] "); + gen.write("] "); float offset = bs.getDashPhase(); gen.writeln((1000 * (int)offset) + " setdash"); } int ec = bs.getEndCap(); switch (ec) { case BasicStroke.CAP_BUTT: - gen.writeln(0 + " setlinecap"); + gen.writeln("0 setlinecap"); break; case BasicStroke.CAP_ROUND: - gen.writeln(1 + " setlinecap"); + gen.writeln("1 setlinecap"); break; case BasicStroke.CAP_SQUARE: - gen.writeln(2 + " setlinecap"); + gen.writeln("2 setlinecap"); break; } @@ -761,10 +696,10 @@ public class PSGraphics2D extends AbstractGraphics2D { * @see #setClip */ public void drawRenderedImage(RenderedImage img, AffineTransform xform) { + preparePainting(); System.out.println("drawRenderedImage"); } - /** * Renders a * {@link RenderableImage}, @@ -797,9 +732,58 @@ public class PSGraphics2D extends AbstractGraphics2D { */ public void drawRenderableImage(RenderableImage img, AffineTransform xform) { + preparePainting(); System.out.println("drawRenderableImage"); } + /** + * Establishes the given color in the PostScript interpreter. + * @param c the color to set + * @throws IOException In case of an I/O problem + */ + protected void establishColor(Color c) throws IOException { + StringBuffer p = new StringBuffer(); + float[] comps = c.getColorComponents(null); + + if (c.getColorSpace().getType() == ColorSpace.TYPE_RGB) { + // according to pdfspec 12.1 p.399 + // if the colors are the same then just use the g or G operator + boolean same = (comps[0] == comps[1] + && comps[0] == comps[2]); + // output RGB + if (same) { + p.append(gen.formatDouble(comps[0])); + } else { + for (int i = 0; i < c.getColorSpace().getNumComponents(); i++) { + if (i > 0) { + p.append(" "); + } + p.append(gen.formatDouble(comps[i])); + } + } + if (same) { + p.append(" setgray"); + } else { + p.append(" setrgbcolor"); + } + } else if (c.getColorSpace().getType() == ColorSpace.TYPE_CMYK) { + // colorspace is CMYK + for (int i = 0; i < c.getColorSpace().getNumComponents(); i++) { + if (i > 0) { + p.append(" "); + } + p.append(gen.formatDouble(comps[i])); + } + p.append(" setcmykcolor"); + } else { + // means we're in DeviceGray or Unknown. + // assume we're in DeviceGray, because otherwise we're screwed. + p.append(gen.formatDouble(comps[0])); + p.append(" setgray"); + } + gen.writeln(p.toString()); + } + /** * Renders the text specified by the specified String, * using the current Font and Paint attributes @@ -825,73 +809,125 @@ public class PSGraphics2D extends AbstractGraphics2D { * @see #setClip */ public void drawString(String s, float x, float y) { - try { - if (overrideFontState == null) { - java.awt.Font gFont = getFont(); - String n = gFont.getFamily(); - if (n.equals("sanserif")) { - n = "sans-serif"; - } - int siz = gFont.getSize(); - String style = gFont.isItalic() ? "italic" : "normal"; - String weight = gFont.isBold() ? "bold" : "normal"; - - //try { - //fontState = new FontState(n, fontState.getFontMetrics(),siz); - //} catch (org.apache.fop.apps.FOPException fope) { - //fope.printStackTrace(); - //} + if (this.textAsShapes) { + drawStringAsShapes(s, x, y); } else { - fontState = overrideFontState; - overrideFontState = null; + drawStringAsText(s, x, y); } - Shape imclip = getClip(); - writeClip(imclip); - Color c = getColor(); - gen.writeln(c.getRed() / 255.0 + " " - + c.getGreen() / 255.0 + " " - + c.getBlue() / 255.0 + " setrgbcolor"); - - AffineTransform trans = getTransform(); - trans.translate(x, y); - double[] vals = new double[6]; - trans.getMatrix(vals); - gen.writeln(gen.formatDouble(1000 * vals[4]) + " " - + gen.formatDouble(1000 * vals[5]) + " moveto "); - //String fontWeight = fontState.getFontWeight(); - StringBuffer sb = new StringBuffer(); - - int l = s.length(); - - if ((currentFontName != fontState.getFontName()) - || (currentFontSize != fontState.getFontSize())) { - gen.writeln(fontState.getFontName() + " " + fontState.getFontSize() + " F"); - currentFontName = fontState.getFontName(); - currentFontSize = fontState.getFontSize(); - } - for (int i = 0; i < l; i++) { - char ch = s.charAt(i); - char mch = fontState.mapChar(ch); - if (mch > 127) { - sb = sb.append("\\" + Integer.toOctalString(mch)); + } + + /** + * Draw a string to the PostScript document. The text is painted as shapes. + * @param s the string to draw + * @param x the x position + * @param y the y position + */ + public void drawStringAsShapes(String s, float x, float y) { + java.awt.Font awtFont = super.getFont(); + FontRenderContext frc = super.getFontRenderContext(); + GlyphVector gv = awtFont.createGlyphVector(frc, s); + Shape glyphOutline = gv.getOutline(x, y); + fill(glyphOutline); + } + + /** + * Draw a string to the PostScript document. The text is painted using + * text operations. + * @param s the string to draw + * @param x the x position + * @param y the y position + */ + public void drawStringAsText(String s, float x, float y) { + preparePainting(); + //System.out.println("drawString('" + s + "', " + x + ", " + y + ")"); + try { + if (this.overrideFont == null) { + java.awt.Font awtFont = getFont(); + this.font = createFont(awtFont); } else { - String escape = "\\()[]{}"; - if (escape.indexOf(mch) >= 0) { - sb.append("\\"); - } - sb = sb.append(mch); + this.font = this.overrideFont; + this.overrideFont = null; } + + //Color and Font state + establishColor(getColor()); + establishCurrentFont(); + + //Clip + Shape imclip = getClip(); + writeClip(imclip); + + gen.saveGraphicsState(); + + //Prepare correct transformation + AffineTransform trans = getTransform(); + gen.writeln("[" + toArray(trans) + "] concat"); + gen.writeln(gen.formatDouble(1000 * x) + " " + + gen.formatDouble(1000 * y) + " moveto "); + gen.writeln("1 -1 scale"); + + StringBuffer sb = new StringBuffer("("); + escapeText(s, sb); + sb.append(") t "); + + gen.writeln(sb.toString()); + + gen.restoreGraphicsState(); + } catch (IOException ioe) { + handleIOException(ioe); } + } - String psString = null; - psString = " (" + sb.toString() + ") " + " t "; + /** + * Converts an AffineTransform to a value array. + * @param at AffineTransform to convert + * @return a String (array of six space-separated values) + */ + protected String toArray(AffineTransform at) { + final double[] vals = new double[6]; + at.getMatrix(vals); + return gen.formatDouble5(vals[0]) + " " + + gen.formatDouble5(vals[1]) + " " + + gen.formatDouble5(vals[2]) + " " + + gen.formatDouble5(vals[3]) + " " + + gen.formatDouble(1000 * vals[4]) + " " + + gen.formatDouble(1000 * vals[5]); + } + + private void escapeText(final String text, StringBuffer target) { + final int l = text.length(); + for (int i = 0; i < l; i++) { + final char ch = text.charAt(i); + final char mch = this.font.mapChar(ch); + PSGenerator.escapeChar(mch, target); + } + } + + private Font createFont(java.awt.Font f) { + String fontFamily = f.getFamily(); + if (fontFamily.equals("sanserif")) { + fontFamily = "sans-serif"; + } + int fontSize = 1000 * f.getSize(); + String style = f.isItalic() ? "italic" : "normal"; + int weight = f.isBold() ? Font.BOLD : Font.NORMAL; + + String fontKey = this.document.findAdjustWeight(fontFamily, style, weight); + if (fontKey == null) { + fontKey = this.document.findAdjustWeight("sans-serif", style, weight); + } + return new Font(fontKey, + this.document.getMetricsFor(fontKey), + fontSize); + } - gen.writeln(" 1.0 -1.0 scale"); - gen.writeln(psString); - gen.writeln(" 1.0 -1.0 scale"); - } catch (IOException ioe) { - handleIOException(ioe); - } + private void establishCurrentFont() throws IOException { + if ((currentFontName != this.font.getFontName()) + || (currentFontSize != this.font.getFontSize())) { + gen.writeln(this.font.getFontName() + " " + this.font.getFontSize() + " F"); + currentFontName = this.font.getFontName(); + currentFontSize = this.font.getFontSize(); + } } /** @@ -921,18 +957,14 @@ public class PSGraphics2D extends AbstractGraphics2D { */ public void drawString(AttributedCharacterIterator iterator, float x, float y) { + preparePainting(); + System.err.println("drawString(AttributedCharacterIterator) NYI"); + /* try { - System.err.println("drawString(AttributedCharacterIterator)"); - gen.writeln("BT"); Shape imclip = getClip(); writeClip(imclip); - Color c = getColor(); - currentColour = new Color(c.getRed(), c.getGreen(), c.getBlue()); - //gen.writeln(currentColour.getColorSpaceOut(true)); - c = getBackground(); - Color col = new Color(c.getRed(), c.getGreen(), c.getBlue()); - //gen.writeln(col.getColorSpaceOut(false)); + establishColor(getColor()); AffineTransform trans = getTransform(); trans.translate(x, y); @@ -952,11 +984,10 @@ public class PSGraphics2D extends AbstractGraphics2D { + gen.formatDouble(vals[6]) + " Tm [" + ch + "]"); } - gen.writeln("ET"); } catch (IOException ioe) { handleIOException(ioe); - } + }*/ } /** @@ -974,60 +1005,22 @@ public class PSGraphics2D extends AbstractGraphics2D { * @see #setClip */ public void fill(Shape s) { + preparePainting(); + // System.err.println("fill"); try { - // System.err.println("fill"); - gen.writeln("gsave"); + gen.saveGraphicsState(); Shape imclip = getClip(); writeClip(imclip); - Color c = getColor(); - gen.writeln(gen.formatDouble(c.getRed() / 255.0) + " " - + gen.formatDouble(c.getGreen() / 255.0) + " " - + gen.formatDouble(c.getBlue() / 255.0) + " setrgbcolor"); + establishColor(getColor()); applyPaint(getPaint(), true); gen.writeln("newpath"); PathIterator iter = s.getPathIterator(getTransform()); - while (!iter.isDone()) { - double vals[] = new double[6]; - int type = iter.currentSegment(vals); - switch (type) { - case PathIterator.SEG_CUBICTO: - gen.writeln(gen.formatDouble(1000 * vals[0]) + " " - + gen.formatDouble(1000 * vals[1]) + " " - + gen.formatDouble(1000 * vals[2]) + " " - + gen.formatDouble(1000 * vals[3]) + " " - + gen.formatDouble(1000 * vals[4]) + " " - + gen.formatDouble(1000 * vals[5]) - + " curveto"); - break; - case PathIterator.SEG_LINETO: - gen.writeln(gen.formatDouble(1000 * vals[0]) + " " - + gen.formatDouble(1000 * vals[1]) - + " lineto"); - break; - case PathIterator.SEG_MOVETO: - gen.writeln(gen.formatDouble(1000 * vals[0]) + " " - + gen.formatDouble(1000 * vals[1]) - + " M"); - break; - case PathIterator.SEG_QUADTO: - gen.writeln(gen.formatDouble(1000 * vals[0]) + " " - + gen.formatDouble(1000 * vals[1]) + " " - + gen.formatDouble(1000 * vals[2]) + " " - + gen.formatDouble(1000 * vals[3]) + " QUADTO "); - break; - case PathIterator.SEG_CLOSE: - gen.writeln("closepath"); - break; - default: - break; - } - iter.next(); - } + processPathIterator(iter); doDrawing(true, false, iter.getWindingRule() == PathIterator.WIND_EVEN_ODD); - gen.writeln("grestore"); + gen.restoreGraphicsState(); } catch (IOException ioe) { handleIOException(ioe); } @@ -1040,8 +1033,9 @@ public class PSGraphics2D extends AbstractGraphics2D { * @param nonzero ??? * @exception IOException In case of an I/O problem */ - protected void doDrawing(boolean fill, boolean stroke, boolean nonzero) + protected void doDrawing(boolean fill, boolean stroke, boolean nonzero) throws IOException { + preparePainting(); if (fill) { if (stroke) { if (!nonzero) { @@ -1086,13 +1080,13 @@ public class PSGraphics2D extends AbstractGraphics2D { } /** - * Sets the overrideing font state. - * @param infont FontState to set + * Sets the overriding font. + * @param font Font to set */ - public void setOverrideFontState(Font infont) { - overrideFontState = infont; + public void setOverrideFont(Font font) { + this.overrideFont = font; } - + /** * Gets the font metrics for the specified font. * @return the font metrics for the specified font. @@ -1149,4 +1143,26 @@ public class PSGraphics2D extends AbstractGraphics2D { System.out.println("copyArea"); } + /* --- for debugging + public void transform(AffineTransform tx) { + System.out.println("transform(" + toArray(tx) + ")"); + super.transform(zx); + } + + public void scale(double sx, double sy) { + System.out.println("scale(" + sx + ", " + sy + ")"); + super.scale(sx, sy); + } + + public void translate(double tx, double ty) { + System.out.println("translate(double " + tx + ", " + ty + ")"); + super.translate(tx, ty); + } + + public void translate(int tx, int ty) { + System.out.println("translate(int " + tx + ", " + ty + ")"); + super.translate(tx, ty); + } + */ + } diff --git a/src/java/org/apache/fop/render/ps/PSProcSets.java b/src/java/org/apache/fop/render/ps/PSProcSets.java index f1b40156b..55c7947f5 100644 --- a/src/java/org/apache/fop/render/ps/PSProcSets.java +++ b/src/java/org/apache/fop/render/ps/PSProcSets.java @@ -85,6 +85,14 @@ public final class PSProcSets { gen.writeln("/M/moveto ld"); gen.writeln("/RM/rmoveto ld"); gen.writeln("/t/show ld"); + gen.writeln("/A/ashow ld"); + gen.writeln("/cp/closepath ld"); + + gen.writeln("/re {4 2 roll M"); //define rectangle + gen.writeln("1 index 0 rlineto"); + gen.writeln("0 exch rlineto"); + gen.writeln("neg 0 rlineto"); + gen.writeln("cp } bd"); gen.writeln("/_ctm matrix def"); //Holds the current matrix gen.writeln("/_tm matrix def"); diff --git a/src/java/org/apache/fop/render/ps/PSRenderer.java b/src/java/org/apache/fop/render/ps/PSRenderer.java index c8b55debb..3f216c921 100644 --- a/src/java/org/apache/fop/render/ps/PSRenderer.java +++ b/src/java/org/apache/fop/render/ps/PSRenderer.java @@ -259,13 +259,12 @@ public class PSRenderer extends AbstractRenderer { /** * Set up the font info * - * @param fontInfo the font info object to set up + * @param foTreeControl the font info object to set up */ public void setupFontInfo(FOTreeControl foTreeControl) { /* use PDF's font setup to get PDF metrics */ org.apache.fop.render.pdf.FontSetup.setup((Document)foTreeControl, null); - // TODO: what's this? - this.fontInfo = fontInfo; + this.fontInfo = (Document)foTreeControl; } /** @@ -276,10 +275,14 @@ public class PSRenderer extends AbstractRenderer { * @param h height * @param col color to fill with */ - protected void fillRect(int x, int y, int w, int h, + protected void fillRect(float x, float y, float w, float h, ColorType col) { useColor(col); - writeln(x + " " + y + " " + w + " " + h + " rectfill"); + writeln(gen.formatDouble(x) + + " " + gen.formatDouble(y) + + " " + gen.formatDouble(w) + + " " + gen.formatDouble(h) + + " rectfill"); } /** @@ -289,8 +292,12 @@ public class PSRenderer extends AbstractRenderer { * @param w width * @param h height */ - protected void drawRect(int x, int y, int w, int h) { - writeln(x + " " + y + " " + w + " " + h + " rectstroke"); + protected void drawRect(float x, float y, float w, float h) { + writeln(gen.formatDouble(x) + + " " + gen.formatDouble(y) + + " " + gen.formatDouble(w) + + " " + gen.formatDouble(h) + + " rectstroke"); } /** @@ -325,7 +332,10 @@ public class PSRenderer extends AbstractRenderer { private void useColor(float red, float green, float blue) { if ((red != currRed) || (green != currGreen) || (blue != currBlue)) { - writeln(red + " " + green + " " + blue + " setrgbcolor"); + writeln(gen.formatDouble(red) + + " " + gen.formatDouble(green) + + " " + gen.formatDouble(blue) + + " setrgbcolor"); currRed = red; currGreen = green; currBlue = blue; @@ -480,7 +490,10 @@ public class PSRenderer extends AbstractRenderer { int bl = currentBPPosition + area.getOffset(); useFont(fontname, fontsize); - + ColorType ct = (ColorType)area.getTrait(Trait.COLOR); + if (ct != null) { + useColor(ct); + } paintText(rx, bl, area.getTextArea(), f); /* @@ -713,9 +726,7 @@ public class PSRenderer extends AbstractRenderer { //saveGraphicsState(); if (back.getColor() != null) { - updateColor(back.getColor(), true, null); - writeln(startx + " " + starty + " " - + width + " " + height + " rectfill"); + fillRect(startx, starty, width, height, back.getColor()); } if (back.getURL() != null) { ImageFactory fact = ImageFactory.getInstance(); @@ -748,7 +759,7 @@ public class PSRenderer extends AbstractRenderer { } float bwidth = bps.width; - updateColor(bps.color, false, null); + useColor(bps.color); writeln(bwidth + " setlinewidth"); drawLine(startx, starty + bwidth / 2, endx, starty + bwidth / 2); @@ -765,7 +776,7 @@ public class PSRenderer extends AbstractRenderer { } float bwidth = bps.width; - updateColor(bps.color, false, null); + useColor(bps.color); writeln(bwidth + " setlinewidth"); drawLine(startx + bwidth / 2, starty, startx + bwidth / 2, endy); @@ -783,7 +794,7 @@ public class PSRenderer extends AbstractRenderer { } float bwidth = bps.width; - updateColor(bps.color, false, null); + useColor(bps.color); writeln(bwidth + " setlinewidth"); drawLine(startx, sy - bwidth / 2, endx, sy - bwidth / 2); @@ -801,7 +812,7 @@ public class PSRenderer extends AbstractRenderer { } float bwidth = bps.width; - updateColor(bps.color, false, null); + useColor(bps.color); writeln(bwidth + " setlinewidth"); drawLine(sx - bwidth / 2, starty, sx - bwidth / 2, endy); } @@ -825,13 +836,7 @@ public class PSRenderer extends AbstractRenderer { writeln(startx + " " + starty + " M "); writeln(endx + " " + endy + " lineto"); } - - private void updateColor(ColorType col, boolean fill, StringBuffer pdf) { - writeln(gen.formatDouble(col.getRed()) + " " - + gen.formatDouble(col.getGreen()) + " " - + gen.formatDouble(col.getBlue()) + " setrgbcolor"); - } - + /** * @see org.apache.fop.render.AbstractRenderer#renderForeignObject(ForeignObject, Rectangle2D) */ @@ -863,34 +868,8 @@ public class PSRenderer extends AbstractRenderer { context.setProperty(PSXMLHandler.PS_YPOS, new Integer(currentBPPosition + (int) pos.getY())); //context.setProperty("strokeSVGText", options.get("strokeSVGText")); - - /* - context.setProperty(PDFXMLHandler.PDF_DOCUMENT, pdfDoc); - context.setProperty(PDFXMLHandler.OUTPUT_STREAM, ostream); - context.setProperty(PDFXMLHandler.PDF_STATE, currentState); - context.setProperty(PDFXMLHandler.PDF_PAGE, currentPage); - context.setProperty(PDFXMLHandler.PDF_CONTEXT, - currentContext == null ? currentPage: currentContext); - context.setProperty(PDFXMLHandler.PDF_CONTEXT, currentContext); - context.setProperty(PDFXMLHandler.PDF_STREAM, currentStream); - context.setProperty(PDFXMLHandler.PDF_XPOS, - new Integer(currentBlockIPPosition + (int) pos.getX())); - context.setProperty(PDFXMLHandler.PDF_YPOS, - new Integer(currentBPPosition + (int) pos.getY())); - context.setProperty(PDFXMLHandler.PDF_FONT_INFO, fontInfo); - context.setProperty(PDFXMLHandler.PDF_FONT_NAME, currentFontName); - context.setProperty(PDFXMLHandler.PDF_FONT_SIZE, - new Integer(currentFontSize)); - context.setProperty(PDFXMLHandler.PDF_WIDTH, - new Integer((int) pos.getWidth())); - context.setProperty(PDFXMLHandler.PDF_HEIGHT, - new Integer((int) pos.getHeight())); - */ + renderXML(userAgent, context, doc, ns); - } - - - } diff --git a/src/java/org/apache/fop/render/ps/PSTextElementBridge.java b/src/java/org/apache/fop/render/ps/PSTextElementBridge.java index 3a4604839..515822413 100644 --- a/src/java/org/apache/fop/render/ps/PSTextElementBridge.java +++ b/src/java/org/apache/fop/render/ps/PSTextElementBridge.java @@ -50,13 +50,10 @@ */ package org.apache.fop.render.ps; -//import org.apache.batik.gvt.TextNode; import org.apache.batik.bridge.SVGTextElementBridge; import org.apache.batik.bridge.BridgeContext; -//import org.apache.batik.bridge.TextUtilities; import org.apache.batik.gvt.GraphicsNode; - -import org.apache.fop.apps.Document; +import org.apache.batik.gvt.TextNode; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -70,15 +67,15 @@ import org.w3c.dom.Node; * @version $Id: PSTextElementBridge.java,v 1.2 2003/03/07 09:46:30 jeremias Exp $ */ public class PSTextElementBridge extends SVGTextElementBridge { - - //private PSTextPainter textPainter; + + private PSTextPainter textPainter; /** * Constructs a new bridge for the <text> element. - * @param fi the font infomration + * @param textPainter the text painter to use */ - public PSTextElementBridge(Document fi) { - //textPainter = new PSTextPainter(fi); + public PSTextElementBridge(PSTextPainter textPainter) { + this.textPainter = textPainter; } /** @@ -90,18 +87,17 @@ public class PSTextElementBridge extends SVGTextElementBridge { */ public GraphicsNode createGraphicsNode(BridgeContext ctx, Element e) { GraphicsNode node = super.createGraphicsNode(ctx, e); - /* - if (node != null && isSimple(ctx, e, node)) { + /* this code is worthless I think. PSTextPainter does a much better job + * at determining whether to stroke or not. */ + if (true/*node != null && isSimple(ctx, e, node)*/) { ((TextNode)node).setTextPainter(getTextPainter()); - }*/ + } return node; } - /* private PSTextPainter getTextPainter() { - return textPainter; + return this.textPainter; } - */ /** * Check if text element contains simple text. @@ -118,16 +114,6 @@ public class PSTextElementBridge extends SVGTextElementBridge { * easily rendered using normal drawString on the PDFGraphics2D */ private boolean isSimple(BridgeContext ctx, Element element, GraphicsNode node) { - /* - // Font size, in user space units. - float fs = TextUtilities.convertFontSize(element).floatValue(); - // PDF cannot display fonts over 36pt - if (fs > 36) { - return false; - } - */ - - for (Node n = element.getFirstChild(); n != null; n = n.getNextSibling()) { @@ -136,7 +122,7 @@ public class PSTextElementBridge extends SVGTextElementBridge { case Node.ELEMENT_NODE: if (n.getLocalName().equals(SVG_TSPAN_TAG) - || n.getLocalName().equals(SVG_ALT_GLYPH_TAG)) { + || n.getLocalName().equals(SVG_ALT_GLYPH_TAG)) { return false; } else if (n.getLocalName().equals(SVG_TEXT_PATH_TAG)) { return false; @@ -146,6 +132,7 @@ public class PSTextElementBridge extends SVGTextElementBridge { break; case Node.TEXT_NODE: case Node.CDATA_SECTION_NODE: + default: } } diff --git a/src/java/org/apache/fop/render/ps/PSTextPainter.java b/src/java/org/apache/fop/render/ps/PSTextPainter.java index ca12f70ed..5cb30e9d9 100644 --- a/src/java/org/apache/fop/render/ps/PSTextPainter.java +++ b/src/java/org/apache/fop/render/ps/PSTextPainter.java @@ -57,6 +57,7 @@ import java.awt.geom.Rectangle2D; org.apache.fop.fonts.Font */ import java.text.AttributedCharacterIterator; +import java.text.CharacterIterator; import java.awt.font.TextAttribute; import java.awt.Shape; import java.awt.Paint; @@ -65,17 +66,18 @@ import java.awt.Color; import java.util.List; import java.util.Iterator; +import org.apache.batik.dom.svg.SVGOMTextElement; import org.apache.batik.gvt.text.Mark; import org.apache.batik.gvt.TextPainter; import org.apache.batik.gvt.TextNode; import org.apache.batik.gvt.text.GVTAttributedCharacterIterator; import org.apache.batik.gvt.text.TextPaintInfo; import org.apache.batik.gvt.font.GVTFontFamily; -import org.apache.batik.bridge.SVGFontFamily; import org.apache.batik.gvt.renderer.StrokingTextPainter; import org.apache.fop.fonts.FontMetrics; import org.apache.fop.fonts.Font; +import org.apache.fop.svg.ACIUtils; import org.apache.fop.apps.Document; /** @@ -90,24 +92,26 @@ import org.apache.fop.apps.Document; * (todo) use drawString(AttributedCharacterIterator iterator...) for some * * @author Keiron Liddle + * @author Jeremias Maerki * @version $Id: PSTextPainter.java,v 1.15 2003/01/08 14:03:55 jeremias Exp $ */ public class PSTextPainter implements TextPainter { - private Document fontInfo; + + private Document document; /** * Use the stroking text painter to get the bounds and shape. * Also used as a fallback to draw the string with strokes. */ - protected static final TextPainter PROXY_PAINTER = - StrokingTextPainter.getInstance(); + protected static final TextPainter + PROXY_PAINTER = StrokingTextPainter.getInstance(); /** * Create a new PS text painter with the given font information. - * @param fi the fint info + * @param document the context document */ - public PSTextPainter(Document fi) { - fontInfo = fi; + public PSTextPainter(Document document) { + this.document = document; } /** @@ -120,137 +124,188 @@ public class PSTextPainter implements TextPainter { // System.out.println("PSText paint"); String txt = node.getText(); Point2D loc = node.getLocation(); - - AttributedCharacterIterator aci = - node.getAttributedCharacterIterator(); - // reset position to start of char iterator - if (aci.getBeginIndex() == aci.getEndIndex()) { - return; + + if (hasUnsupportedAttributes(node)) { + PROXY_PAINTER.paint(node, g2d); + } else { + paintTextRuns(node.getTextRuns(), g2d, loc); } - char ch = aci.first(); - if (ch == AttributedCharacterIterator.DONE) { - return; + } + + + private boolean hasUnsupportedAttributes(TextNode node) { + Iterator i = node.getTextRuns().iterator(); + while (i.hasNext()) { + StrokingTextPainter.TextRun + run = (StrokingTextPainter.TextRun)i.next(); + AttributedCharacterIterator aci = run.getACI(); + boolean hasUnsupported = hasUnsupportedAttributes(aci); + if (hasUnsupported) { + return true; + } } + return false; + } + private boolean hasUnsupportedAttributes(AttributedCharacterIterator aci) { + boolean hasunsupported = false; + TextPaintInfo tpi = (TextPaintInfo) aci.getAttribute( GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO); - - if (tpi == null) { - return; - } - - TextNode.Anchor anchor; - anchor = (TextNode.Anchor) aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.ANCHOR_TYPE); + if ((tpi != null) + && ((tpi.strokeStroke != null && tpi.strokePaint != null) + || (tpi.strikethroughStroke != null) + || (tpi.underlineStroke != null) + || (tpi.overlineStroke != null))) { + hasunsupported = true; + } - List gvtFonts; - gvtFonts = (List) aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES); - Paint forg = tpi.fillPaint; - Paint strokePaint = tpi.strokePaint; - Float size = (Float) aci.getAttribute(TextAttribute.SIZE); - if (size == null) { - return; + //Alpha is not supported + Paint foreground = (Paint) aci.getAttribute(TextAttribute.FOREGROUND); + if (foreground instanceof Color) { + Color col = (Color)foreground; + if (col.getAlpha() != 255) { + hasunsupported = true; + } } - Stroke stroke = tpi.strokeStroke; - /* - Float xpos = (Float) aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.X); - Float ypos = (Float) aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.Y); - */ - Float posture = (Float) aci.getAttribute(TextAttribute.POSTURE); - Float taWeight = (Float) aci.getAttribute(TextAttribute.WEIGHT); + Object letSpace = aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.LETTER_SPACING); + if (letSpace != null) { + hasunsupported = true; + } - boolean useStrokePainter = false; + Object wordSpace = aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.WORD_SPACING); + if (wordSpace != null) { + hasunsupported = true; + } + + Object lengthAdjust = aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.LENGTH_ADJUST); + if (lengthAdjust != null) { + hasunsupported = true; + } - if (forg instanceof Color) { - Color col = (Color) forg; - if (col.getAlpha() != 255) { - useStrokePainter = true; - } - g2d.setColor(col); + Object writeMod = aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE); + if (writeMod != null + && !GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE_LTR.equals( + writeMod)) { + hasunsupported = true; } - g2d.setPaint(forg); - g2d.setStroke(stroke); - if (strokePaint != null) { - // need to draw using AttributedCharacterIterator - useStrokePainter = true; + Object vertOr = aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.VERTICAL_ORIENTATION); + if (GVTAttributedCharacterIterator.TextAttribute.ORIENTATION_ANGLE.equals( + vertOr)) { + hasunsupported = true; } + + Object rcDel = aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.TEXT_COMPOUND_DELIMITER); + if (!(rcDel instanceof SVGOMTextElement)) { + hasunsupported = true; //Filter spans + } + + return hasunsupported; + } - if (hasUnsupportedAttributes(aci)) { - useStrokePainter = true; + /** + * Paint a list of text runs on the Graphics2D at a given location. + * @param textRuns the list of text runs + * @param g2d the Graphics2D to paint to + * @param loc the current location of the "cursor" + */ + protected void paintTextRuns(List textRuns, Graphics2D g2d, Point2D loc) { + Point2D currentloc = loc; + Iterator i = textRuns.iterator(); + while (i.hasNext()) { + StrokingTextPainter.TextRun + run = (StrokingTextPainter.TextRun)i.next(); + currentloc = paintTextRun(run, g2d, currentloc); } + } - // text contains unsupported information - if (useStrokePainter) { - PROXY_PAINTER.paint(node, g2d); - return; + /** + * Paint a single text run on the Graphics2D at a given location. + * @param run the text run to paint + * @param g2d the Graphics2D to paint to + * @param loc the current location of the "cursor" + * @return the new location of the "cursor" after painting the text run + */ + protected Point2D paintTextRun(StrokingTextPainter.TextRun run, Graphics2D g2d, Point2D loc) { + AttributedCharacterIterator aci = run.getACI(); + return paintACI(aci, g2d, loc); + } + + /** + * Extract the raw text from an ACI. + * @param aci ACI to inspect + * @return the extracted text + */ + protected String getText(AttributedCharacterIterator aci) { + StringBuffer sb = new StringBuffer(aci.getEndIndex() - aci.getBeginIndex()); + for (char c = aci.first(); c != CharacterIterator.DONE; c = aci.next()) { + sb.append(c); } + return sb.toString(); + } - String style = ((posture != null) && (posture.floatValue() > 0.0)) - ? "italic" : "normal"; - int weight = ((taWeight != null) - && (taWeight.floatValue() > 1.0)) ? Font.BOLD - : Font.NORMAL; + /** + * Paint an ACI on a Graphics2D at a given location. The method has to + * update the location after painting. + * @param aci ACI to paint + * @param g2d Graphics2D to paint on + * @param loc start location + * @return new current location + */ + protected Point2D paintACI(AttributedCharacterIterator aci, Graphics2D g2d, Point2D loc) { + //System.out.println("=============================================="); + //ACIUtils.dumpAttrs(aci); + + aci.first(); - Font fontState = null; - Document fi = fontInfo; - boolean found = false; - String fontFamily = null; - if (gvtFonts != null) { - Iterator i = gvtFonts.iterator(); - while (i.hasNext()) { - GVTFontFamily fam = (GVTFontFamily) i.next(); - if (fam instanceof SVGFontFamily) { - PROXY_PAINTER.paint(node, g2d); - return; - } - fontFamily = fam.getFamilyName(); - if (fi.hasFont(fontFamily, style, weight)) { - String fname = fontInfo.fontLookup(fontFamily, style, - weight); - FontMetrics metrics = fontInfo.getMetricsFor(fname); - int fsize = (int)(size.floatValue() * 1000); - fontState = new Font(fname, metrics, fsize); - found = true; - break; - } - } + updateLocationFromACI(aci, loc); + + TextPaintInfo tpi = (TextPaintInfo) aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO); + + if (tpi == null) { + return loc; } - if (!found) { - String fname = - fontInfo.fontLookup("any", style, Font.NORMAL); - FontMetrics metrics = fontInfo.getMetricsFor(fname); - int fsize = (int)(size.floatValue() * 1000); - fontState = new Font(fname, metrics, fsize); - } else { - if (g2d instanceof PSGraphics2D) { - ((PSGraphics2D) g2d).setOverrideFontState(fontState); - } + + TextNode.Anchor anchor = (TextNode.Anchor)aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.ANCHOR_TYPE); + + //Set up font + List gvtFonts = (List)aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES); + Paint foreground = tpi.fillPaint; + Paint strokePaint = tpi.strokePaint; + Stroke stroke = tpi.strokeStroke; + + Float fontSize = (Float)aci.getAttribute(TextAttribute.SIZE); + if (fontSize == null) { + return loc; } - int fStyle = java.awt.Font.PLAIN; - if (weight == Font.BOLD) { - if (style.equals("italic")) { - fStyle = java.awt.Font.BOLD | java.awt.Font.ITALIC; - } else { - fStyle = java.awt.Font.BOLD; - } - } else { - if (style.equals("italic")) { - fStyle = java.awt.Font.ITALIC; - } else { - fStyle = java.awt.Font.PLAIN; - } + Float posture = (Float)aci.getAttribute(TextAttribute.POSTURE); + Float taWeight = (Float)aci.getAttribute(TextAttribute.WEIGHT); + + if (foreground instanceof Color) { + Color col = (Color)foreground; + g2d.setColor(col); } - java.awt.Font font = new java.awt.Font(fontFamily, fStyle, - (int)(fontState.getFontSize() / 1000)); + g2d.setPaint(foreground); + g2d.setStroke(stroke); + + Font font = makeFont(aci); + java.awt.Font awtFont = makeAWTFont(aci, font); - g2d.setFont(font); + g2d.setFont(awtFont); - float advance = getStringWidth(txt, fontState); + String txt = getText(aci); + float advance = getStringWidth(txt, font); float tx = 0; if (anchor != null) { switch (anchor.getType()) { @@ -259,51 +314,124 @@ public class PSTextPainter implements TextPainter { break; case TextNode.Anchor.ANCHOR_END: tx = -advance; + break; + default: //nop + } + } + + //Finally draw text + if (g2d instanceof PSGraphics2D) { + ((PSGraphics2D) g2d).setOverrideFont(font); + } + try { + g2d.drawString(txt, (float)(loc.getX() + tx), (float)(loc.getY())); + } finally { + if (g2d instanceof PSGraphics2D) { + ((PSGraphics2D) g2d).setOverrideFont(null); } } - g2d.drawString(txt, (float)(loc.getX() + tx), (float)(loc.getY())); + loc.setLocation(loc.getX() + (double)advance, loc.getY()); + return loc; } - private boolean hasUnsupportedAttributes(AttributedCharacterIterator aci) { - boolean hasunsupported = false; - Object letSpace = aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.LETTER_SPACING); - if (letSpace != null) { - hasunsupported = true; + private void updateLocationFromACI( + AttributedCharacterIterator aci, + Point2D loc) { + //Adjust position of span + Float xpos = (Float)aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.X); + Float ypos = (Float)aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.Y); + Float dxpos = (Float)aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.DX); + Float dypos = (Float)aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.DY); + if (xpos != null) { + loc.setLocation(xpos.doubleValue(), loc.getY()); } + if (ypos != null) { + loc.setLocation(loc.getX(), ypos.doubleValue()); + } + if (dxpos != null) { + loc.setLocation(loc.getX() + dxpos.doubleValue(), loc.getY()); + } + if (dypos != null) { + loc.setLocation(loc.getX(), loc.getY() + dypos.doubleValue()); + } + } - Object wordSpace = aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.WORD_SPACING); - if (wordSpace != null) { - hasunsupported = true; - } + private String getStyle(AttributedCharacterIterator aci) { + Float posture = (Float)aci.getAttribute(TextAttribute.POSTURE); + return ((posture != null) && (posture.floatValue() > 0.0)) + ? "italic" + : "normal"; + } - AttributedCharacterIterator.Attribute key; - key = GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE; - Object writeMod = aci.getAttribute(key); - if (!GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE_LTR.equals( - writeMod)) { - hasunsupported = true; + private int getWeight(AttributedCharacterIterator aci) { + Float taWeight = (Float)aci.getAttribute(TextAttribute.WEIGHT); + return ((taWeight != null) && (taWeight.floatValue() > 1.0)) + ? Font.BOLD + : Font.NORMAL; + } + + private Font makeFont(AttributedCharacterIterator aci) { + Float fontSize = (Float)aci.getAttribute(TextAttribute.SIZE); + String style = getStyle(aci); + int weight = getWeight(aci); + + boolean found = false; + String fontFamily = null; + List gvtFonts = (List) aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES); + if (gvtFonts != null) { + Iterator i = gvtFonts.iterator(); + while (i.hasNext()) { + GVTFontFamily fam = (GVTFontFamily) i.next(); + /* (todo) Enable SVG Font painting + if (fam instanceof SVGFontFamily) { + PROXY_PAINTER.paint(node, g2d); + return; + }*/ + fontFamily = fam.getFamilyName(); + if (document.hasFont(fontFamily, style, weight)) { + String fname = document.fontLookup( + fontFamily, style, weight); + FontMetrics metrics = document.getMetricsFor(fname); + int fsize = (int)(fontSize.floatValue() * 1000); + return new Font(fname, metrics, fsize); + } + } } + String fname = document.fontLookup( + "any", style, Font.NORMAL); + FontMetrics metrics = document.getMetricsFor(fname); + int fsize = (int)(fontSize.floatValue() * 1000); + return new Font(fname, metrics, fsize); + } - Object vertOr = aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.VERTICAL_ORIENTATION); - if (GVTAttributedCharacterIterator.TextAttribute.ORIENTATION_ANGLE.equals( - vertOr)) { - hasunsupported = true; + private java.awt.Font makeAWTFont(AttributedCharacterIterator aci, Font font) { + final String style = getStyle(aci); + final int weight = getWeight(aci); + int fStyle = java.awt.Font.PLAIN; + if (weight == Font.BOLD) { + fStyle |= java.awt.Font.BOLD; } - return hasunsupported; + if ("italic".equals(style)) { + fStyle |= java.awt.Font.ITALIC; + } + return new java.awt.Font(font.getFontName(), fStyle, + (int)(font.getFontSize() / 1000)); } - private float getStringWidth(String str, Font fontState) { + private float getStringWidth(String str, Font font) { float wordWidth = 0; - float whitespaceWidth = fontState.getWidth(fontState.mapChar(' ')); + float whitespaceWidth = font.getWidth(font.mapChar(' ')); for (int i = 0; i < str.length(); i++) { float charWidth; char c = str.charAt(i); if (!((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t'))) { - charWidth = fontState.getWidth(fontState.mapChar(c)); + charWidth = font.getWidth(font.mapChar(c)); if (charWidth <= 0) { charWidth = whitespaceWidth; } @@ -336,6 +464,9 @@ public class PSTextPainter implements TextPainter { * @return the bounds of the text */ public Rectangle2D getBounds2D(TextNode node) { + /* (todo) getBounds2D() is too slow + * because it uses the StrokingTextPainter. We should implement this + * method ourselves. */ return PROXY_PAINTER.getBounds2D(node); } diff --git a/src/java/org/apache/fop/render/ps/PSTranscoder.java b/src/java/org/apache/fop/render/ps/PSTranscoder.java index 76f2771c9..d85d8d189 100644 --- a/src/java/org/apache/fop/render/ps/PSTranscoder.java +++ b/src/java/org/apache/fop/render/ps/PSTranscoder.java @@ -1,5 +1,5 @@ /* - * $Id: PDFTranscoder.java,v 1.24 2003/03/07 09:51:26 jeremias Exp $ + * $Id$ * ============================================================================ * The Apache Software License, Version 1.1 * ============================================================================ @@ -51,41 +51,6 @@ package org.apache.fop.render.ps; -import java.awt.geom.AffineTransform; -import java.awt.geom.Rectangle2D; - -import java.awt.Color; - -import java.net.MalformedURLException; -import java.net.URL; - -import java.io.IOException; - -import org.apache.batik.bridge.BridgeContext; -import org.apache.batik.bridge.BridgeException; -import org.apache.batik.bridge.GVTBuilder; -import org.apache.batik.bridge.SVGTextElementBridge; -import org.apache.batik.bridge.ViewBox; - -import org.apache.batik.dom.svg.SVGOMDocument; - -import org.apache.batik.gvt.GraphicsNode; - -import org.apache.batik.transcoder.TranscoderException; -import org.apache.batik.transcoder.TranscoderOutput; -import org.apache.batik.transcoder.image.resources.Messages; - -import org.apache.batik.transcoder.image.ImageTranscoder; - -import org.apache.fop.svg.AbstractFOPTranscoder; - -import org.apache.batik.gvt.TextPainter; -import org.apache.batik.gvt.renderer.StrokingTextPainter; - -import org.w3c.dom.Document; -import org.w3c.dom.svg.SVGDocument; -import org.w3c.dom.svg.SVGSVGElement; - /** * This class enables to transcode an input to a PostScript document. * @@ -112,11 +77,9 @@ import org.w3c.dom.svg.SVGSVGElement; * * @author Keiron Liddle * @author Jeremias Maerki - * @version $Id: PDFTranscoder.java,v 1.24 2003/03/07 09:51:26 jeremias Exp $ + * @version $Id$ */ -public class PSTranscoder extends AbstractFOPTranscoder { - - protected PSDocumentGraphics2D graphics = null; +public class PSTranscoder extends AbstractPSTranscoder { /** * Constructs a new PSTranscoder. @@ -125,63 +88,8 @@ public class PSTranscoder extends AbstractFOPTranscoder { super(); } - /** - * Transcodes the specified Document as an image in the specified output. - * - * @param document the document to transcode - * @param uri the uri of the document or null if any - * @param output the ouput where to transcode - * @exception TranscoderException if an error occured while transcoding - */ - protected void transcode(Document document, String uri, - TranscoderOutput output) - throws TranscoderException { - - graphics = new PSDocumentGraphics2D(false); - - super.transcode(document, uri, output); - - // prepare the image to be painted - int w = (int)(width+.5); - int h = (int)(height+.5); - - try { - graphics.setupDocument(output.getOutputStream(), w, h); - graphics.setSVGDimension(width, height); - - if (hints.containsKey(ImageTranscoder.KEY_BACKGROUND_COLOR)) { - graphics.setBackgroundColor - ((Color)hints.get(ImageTranscoder.KEY_BACKGROUND_COLOR)); - } - graphics.setGraphicContext - (new org.apache.batik.ext.awt.g2d.GraphicContext()); - graphics.setTransform(curTxf); - - this.root.paint(graphics); - - graphics.finish(); - } catch (IOException ex) { - throw new TranscoderException(ex); - } - } - - protected BridgeContext createBridgeContext() { - BridgeContext ctx = new BridgeContext(userAgent); - TextPainter textPainter = null; - textPainter = new StrokingTextPainter(); - ctx.setTextPainter(textPainter); - - SVGTextElementBridge textElementBridge = - new PSTextElementBridge(graphics.getFontInfo()); - ctx.putBridge(textElementBridge); - - //PDFAElementBridge pdfAElementBridge = new PDFAElementBridge(); - //AffineTransform currentTransform = new AffineTransform(1, 0, 0, 1, 0, 0); - //pdfAElementBridge.setCurrentTransform(currentTransform); - //ctx.putBridge(pdfAElementBridge); - - //ctx.putBridge(new PSImageElementBridge()); - return ctx; + protected AbstractPSDocumentGraphics2D createDocumentGraphics2D() { + return new PSDocumentGraphics2D(false); } } diff --git a/src/java/org/apache/fop/render/ps/PSXMLHandler.java b/src/java/org/apache/fop/render/ps/PSXMLHandler.java index c500fe9c4..6197c3864 100644 --- a/src/java/org/apache/fop/render/ps/PSXMLHandler.java +++ b/src/java/org/apache/fop/render/ps/PSXMLHandler.java @@ -137,6 +137,7 @@ public class PSXMLHandler implements XMLHandler { SVGHandler svghandler = new SVGHandler(); svghandler.renderSVGDocument(context, doc, psi); } else { + //nop } } @@ -295,7 +296,9 @@ public class PSXMLHandler implements XMLHandler { GVTBuilder builder = new GVTBuilder(); BridgeContext ctx = new BridgeContext(ua); - PSTextElementBridge tBridge = new PSTextElementBridge(psInfo.getFontInfo()); + PSTextPainter textPainter = new PSTextPainter(psInfo.getFontInfo()); + ctx.setTextPainter(textPainter); + PSTextElementBridge tBridge = new PSTextElementBridge(textPainter); ctx.putBridge(tBridge); //PSAElementBridge aBridge = new PSAElementBridge(); @@ -305,8 +308,6 @@ public class PSXMLHandler implements XMLHandler { //aBridge.setCurrentTransform(transform); //ctx.putBridge(aBridge); - TextPainter textPainter = new PSTextPainter(psInfo.getFontInfo()); - ctx.setTextPainter(textPainter); GraphicsNode root; try { root = builder.build(ctx, doc); @@ -327,12 +328,16 @@ public class PSXMLHandler implements XMLHandler { try { gen.writeln("%SVG graphic start ---"); + gen.saveGraphicsState(); /* * Clip to the svg area. * Note: To have the svg overlay (under) a text area then use * an fo:block-container */ - gen.saveGraphicsState(); + gen.writeln("newpath"); + gen.defineRect(xOffset, yOffset, w, h); + 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. @@ -351,7 +356,8 @@ public class PSXMLHandler implements XMLHandler { if (psInfo.pdfContext == null) { psInfo.pdfContext = psInfo.pdfPage; }*/ - PSGraphics2D graphics = new PSGraphics2D(true, gen); + final boolean textAsShapes = false; + PSGraphics2D graphics = new PSGraphics2D(textAsShapes, gen); graphics.setGraphicContext(new org.apache.batik.ext.awt.g2d.GraphicContext()); //psInfo.pdfState.push(); transform = new AffineTransform(); -- 2.39.5