]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Substantial refactoring of the PS transcoder (especially the text painting, less...
authorJeremias Maerki <jeremias@apache.org>
Fri, 7 Nov 2003 22:19:19 +0000 (22:19 +0000)
committerJeremias Maerki <jeremias@apache.org>
Fri, 7 Nov 2003 22:19:19 +0000 (22:19 +0000)
Addition of an EPS transcoder

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@196990 13f79535-47bb-0310-9956-ffa450edef68

14 files changed:
src/java/org/apache/fop/render/ps/AbstractPSDocumentGraphics2D.java [new file with mode: 0644]
src/java/org/apache/fop/render/ps/AbstractPSTranscoder.java [new file with mode: 0644]
src/java/org/apache/fop/render/ps/DSCConstants.java
src/java/org/apache/fop/render/ps/EPSDocumentGraphics2D.java [new file with mode: 0644]
src/java/org/apache/fop/render/ps/EPSTranscoder.java [new file with mode: 0644]
src/java/org/apache/fop/render/ps/PSDocumentGraphics2D.java
src/java/org/apache/fop/render/ps/PSGenerator.java
src/java/org/apache/fop/render/ps/PSGraphics2D.java
src/java/org/apache/fop/render/ps/PSProcSets.java
src/java/org/apache/fop/render/ps/PSRenderer.java
src/java/org/apache/fop/render/ps/PSTextElementBridge.java
src/java/org/apache/fop/render/ps/PSTextPainter.java
src/java/org/apache/fop/render/ps/PSTranscoder.java
src/java/org/apache/fop/render/ps/PSXMLHandler.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 (file)
index 0000000..f280612
--- /dev/null
@@ -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 <jtauber@jtauber.com>. For more information on the Apache
+ * Software Foundation, please see <http://www.apache.org/>.
+ */ 
+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 <tt>PSGraphics2D</tt> that
+ * is used to create a full document around the PostScript rendering from
+ * <tt>PSGraphics2D</tt>.
+ *
+ * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a>
+ * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a>
+ * @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 (file)
index 0000000..a02b51c
--- /dev/null
@@ -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 <jtauber@jtauber.com>. For more information on the Apache
+ * Software Foundation, please see <http://www.apache.org/>.
+ */ 
+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.
+ *
+ * <p>Two transcoding hints (<tt>KEY_WIDTH</tt> and
+ * <tt>KEY_HEIGHT</tt>) 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.
+ *
+ * <p>The <tt>KEY_BACKGROUND_COLOR</tt> 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.
+ *
+ * <p>The <tt>KEY_AOI</tt> represents the area of interest to paint
+ * in device space.
+ *
+ * <p>Three additional transcoding hints that act on the SVG
+ * processor can be specified:
+ *
+ * <p><tt>KEY_LANGUAGE</tt> to set the default language to use (may be
+ * used by a &lt;switch> SVG element for example),
+ * <tt>KEY_USER_STYLESHEET_URI</tt> to fix the URI of a user
+ * stylesheet, and <tt>KEY_PIXEL_TO_MM</tt> to specify the pixel to
+ * millimeter conversion factor.
+ *
+ * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a>
+ * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a>
+ * @version $Id$
+ */
+public abstract class AbstractPSTranscoder extends AbstractFOPTranscoder {
+
+    private   Configuration                cfg      = null;
+    protected AbstractPSDocumentGraphics2D graphics = null;
+
+    /**
+     * Constructs a new <tt>AbstractPSTranscoder</tt>.
+     */
+    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);
+        }
+    }
+
+
+}
index 40da7131350b78a628b139a475ddbb2de44f2536..fe85709759493c3b68a643342a111ea58162c42c 100644 (file)
@@ -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 (file)
index 0000000..b38a4ad
--- /dev/null
@@ -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 <jtauber@jtauber.com>. For more information on the Apache
+ * Software Foundation, please see <http://www.apache.org/>.
+ */ 
+package org.apache.fop.render.ps;
+
+import java.io.IOException;
+
+/**
+ * This class is a wrapper for the <tt>AbstractPSDocumentGraphics2D</tt> that
+ * is used to create EPS (Encapsulated PostScript) files instead of PS file.
+ *
+ * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a>
+ * @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 (file)
index 0000000..602268b
--- /dev/null
@@ -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 <jtauber@jtauber.com>. For more information on the Apache
+ * Software Foundation, please see <http://www.apache.org/>.
+ */ 
+package org.apache.fop.render.ps;
+
+/**
+ * This class enables to transcode an input to a EPS document.
+ *
+ * <p>Two transcoding hints (<tt>KEY_WIDTH</tt> and
+ * <tt>KEY_HEIGHT</tt>) 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.
+ *
+ * <p>The <tt>KEY_BACKGROUND_COLOR</tt> 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.
+ *
+ * <p>The <tt>KEY_AOI</tt> represents the area of interest to paint
+ * in device space.
+ *
+ * <p>Three additional transcoding hints that act on the SVG
+ * processor can be specified:
+ *
+ * <p><tt>KEY_LANGUAGE</tt> to set the default language to use (may be
+ * used by a &lt;switch> SVG element for example),
+ * <tt>KEY_USER_STYLESHEET_URI</tt> to fix the URI of a user
+ * stylesheet, and <tt>KEY_PIXEL_TO_MM</tt> to specify the pixel to
+ * millimeter conversion factor.
+ *
+ * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a>
+ * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a>
+ * @version $Id$
+ */
+public class EPSTranscoder extends AbstractPSTranscoder {
+
+    /**
+     * Constructs a new <tt>EPSPSTranscoder</tt>.
+     */
+    public EPSTranscoder() {
+        super();
+    }
+
+    protected AbstractPSDocumentGraphics2D createDocumentGraphics2D() {
+        return new EPSDocumentGraphics2D(false);
+    }
+
+}
index 35c3fc7fd5cb0ef8f0e34b057bdcadb0dcb282ea..3c811d476b4d623e8b57ff82ee79cdcc843cdb40 100644 (file)
@@ -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 <tt>PSGraphics2D</tt> that
@@ -71,16 +66,14 @@ import org.apache.fop.apps.Document;
  *
  * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a>
  * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a>
- * @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);
-        }
-    }
-
 }
 
index 988d53bd698425987cb3a221f1852126de7d7d67..156c771492ae717933b4a262a60cbc6a15935ccd 100644 (file)
  */ 
 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
index 98aa6fdbf99791ce466be8342899f8ccb36a3518..b017c0ad7aca7c237093ac0953768dba44748eab 100644 (file)
 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 <tt>AbstractGraphics2D</tt> 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 <code>String</code>,
      * using the current <code>Font</code> and <code>Paint</code> 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);
+    }
+    */
+
 }
index f1b40156bbc804734a389a1f5091eb3dacd59b40..55c7947f5a014423f47439801e80c5a79f8344dc 100644 (file)
@@ -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");
index c8b55debb8fa8f59d91d60de58388730b3209feb..3f216c921447e2607ea66c47cfcd3a4a9fa7fd73 100644 (file)
@@ -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);
-
     }
 
-
-
-
 }
index 3a4604839ca2656a2e2705fabd315ea025f24428..515822413cc423b578056b308f8ca7f8d71a33f1 100644 (file)
  */
 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 &lt;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:
             }
         }
 
index ca12f70edb558fe97856012d265f79fc3b994d8f..5cb30e9d954cc065115e808b78649264e9b0bbdf 100644 (file)
@@ -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 <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a>
+ * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a>
  * @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);
     }
 
index 76f2771c9aa5725e90d3c5b007062d65aedda266..d85d8d189231730442ce4f41ddeae3ddc90d1f63 100644 (file)
@@ -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
  * ============================================================================
 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 <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a>
  * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a>
- * @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 <tt>PSTranscoder</tt>.
@@ -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);
     }
 
 }
index c500fe9c491e615ba4971f43854d61e80b94a55d..6197c3864bf5d5ef2e89f3ba3fdf05b382492ad8 100644 (file)
@@ -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();