]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Added page master name to IFDocumentHandler.startPage() method.
authorJeremias Maerki <jeremias@apache.org>
Wed, 19 Nov 2008 20:13:48 +0000 (20:13 +0000)
committerJeremias Maerki <jeremias@apache.org>
Wed, 19 Nov 2008 20:13:48 +0000 (20:13 +0000)
Wired together the support for out-of-order rendering (only applicable to PDF) when the intermediate format is not used (in-memory rendering).
Fixed a logical bug in IFRenderer that caused some unneeded code. Glyph adjustments (kerning, letter/word space...) were not done right. All painters fixed/adjusted accordingly.
Started implementation of the PostScript painter: Supports only text and filled rectangles so far. Work in progress...

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

23 files changed:
src/java/META-INF/services/org.apache.fop.render.intermediate.IFDocumentHandler
src/java/org/apache/fop/render/bitmap/TIFFDocumentHandler.java
src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java
src/java/org/apache/fop/render/intermediate/IFDocumentHandler.java
src/java/org/apache/fop/render/intermediate/IFParser.java
src/java/org/apache/fop/render/intermediate/IFRenderer.java
src/java/org/apache/fop/render/intermediate/IFSerializer.java
src/java/org/apache/fop/render/java2d/Java2DPainter.java
src/java/org/apache/fop/render/pcl/PCLDocumentHandler.java
src/java/org/apache/fop/render/pcl/PCLPainter.java
src/java/org/apache/fop/render/pdf/PDFContentGenerator.java
src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java
src/java/org/apache/fop/render/pdf/PDFPainter.java
src/java/org/apache/fop/render/ps/PSConfigurationConstants.java [new file with mode: 0644]
src/java/org/apache/fop/render/ps/PSDocumentHandler.java [new file with mode: 0644]
src/java/org/apache/fop/render/ps/PSDocumentHandlerMaker.java [new file with mode: 0644]
src/java/org/apache/fop/render/ps/PSPainter.java [new file with mode: 0644]
src/java/org/apache/fop/render/ps/PSRenderer.java
src/java/org/apache/fop/render/ps/PSRendererConfigurator.java
src/java/org/apache/fop/render/ps/PSRenderingContext.java [new file with mode: 0644]
src/java/org/apache/fop/render/ps/PSRenderingUtil.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/svg/SVGDocumentHandler.java
src/sandbox/org/apache/fop/render/svg/SVGPrintDocumentHandler.java

index 4e8309fea9f708157c79e7a7da3157d2d2fc832a..a6bf64ddf96d5112d2f10af5819699fa50630603 100644 (file)
@@ -1,3 +1,4 @@
 org.apache.fop.render.pdf.PDFDocumentHandlerMaker\r
 org.apache.fop.render.pcl.PCLDocumentHandlerMaker\r
-org.apache.fop.render.bitmap.TIFFDocumentHandlerMaker
\ No newline at end of file
+org.apache.fop.render.bitmap.TIFFDocumentHandlerMaker\r
+org.apache.fop.render.ps.PSDocumentHandlerMaker
\ No newline at end of file
index 0da1c02fe0d8d6a807e8f8297e86b53c363d2ab1..cdc350c9e4295bef7553773ab3896e17f2270469 100644 (file)
@@ -166,7 +166,8 @@ public class TIFFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler
     }
 
     /** {@inheritDoc} */
-    public void startPage(int index, String name, Dimension size) throws IFException {
+    public void startPage(int index, String name, String pageMasterName, Dimension size)
+                throws IFException {
         this.pageCount++;
         this.currentPageDimensions = new Dimension(size);
     }
index 105a2a426485189cbc6a3201e2dbf3ab59831df2..07d8334d5674394ef8fd6f89b814d2b8261a18e7 100644 (file)
@@ -350,4 +350,19 @@ public abstract class AbstractIFPainter implements IFPainter {
         }
     }
 
+    /**
+     * Converts a transformation matrix from millipoints to points.
+     * @param transform the transformation matrix (in millipoints)
+     * @return the converted transformation matrix (in points)
+     */
+    public static AffineTransform toPoints(AffineTransform transform) {
+        final double[] matrix = new double[6];
+        transform.getMatrix(matrix);
+        //Convert from millipoints to points
+        matrix[4] /= 1000;
+        matrix[5] /= 1000;
+        return new AffineTransform(matrix);
+    }
+
+
 }
index 61093769b0e39943fd32519928ed998fdebb7c6d..e3634e9ef28464ec63a39174b2c34d4a3bbdb921 100644 (file)
@@ -189,10 +189,11 @@ public interface IFDocumentHandler {
      * Indicates the start of a new page.
      * @param index the index of the page (0-based)
      * @param name the page name (usually the formatted page number)
+     * @param pageMasterName the name of the simple-page-master that generated this page
      * @param size the size of the page (equivalent to the MediaBox in PDF)
      * @throws IFException if an error occurs while handling this event
      */
-    void startPage(int index, String name, Dimension size) throws IFException;
+    void startPage(int index, String name, String pageMasterName, Dimension size) throws IFException;
 
     /**
      * Indicates the end of a page
index 5eaef13ae5bba962a71700de35ab2526759af930..e514ab889213a1d6b1593ad987fb09d76c488d01 100644 (file)
@@ -332,9 +332,10 @@ public class IFParser implements IFConstants {
             public void startElement(Attributes attributes) throws IFException {
                 int index = Integer.parseInt(attributes.getValue("index"));
                 String name = attributes.getValue("name");
+                String pageMasterName = attributes.getValue("page-master-name");
                 int width = Integer.parseInt(attributes.getValue("width"));
                 int height = Integer.parseInt(attributes.getValue("height"));
-                documentHandler.startPage(index, name, new Dimension(width, height));
+                documentHandler.startPage(index, name, pageMasterName, new Dimension(width, height));
             }
 
             public void endElement() throws IFException {
index eddd5f1bd57a69dc0dbaffff00c24dc084785ef8..fecde8ae436328f0ef31e91f6742f1a72d3a86ff 100644 (file)
@@ -195,6 +195,12 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
         }
     }
 
+    /** {@inheritDoc} */
+    public boolean supportsOutOfOrder() {
+        return (this.documentHandler != null
+                ? this.documentHandler.supportsPagesOutOfOrder() : false);
+    }
+
     /**
      * Creates a default {@code IFDocumentHandler} when none has been set.
      * @return the default IFDocumentHandler
@@ -492,7 +498,8 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
             Dimension dim = new Dimension(
                     (int)Math.ceil(viewArea.getWidth()),
                     (int)Math.ceil(viewArea.getHeight()));
-            documentHandler.startPage(page.getPageIndex(), page.getPageNumberString(), dim);
+            documentHandler.startPage(page.getPageIndex(), page.getPageNumberString(),
+                    page.getSimplePageMasterName(), dim);
             documentHandler.startPageHeader();
             //TODO Handle page header
             documentHandler.endPageHeader();
@@ -867,8 +874,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
             int tws = ((TextArea) space.getParentArea()).getTextWordSpaceAdjust()
                          + 2 * textArea.getTextLetterSpaceAdjust();
             if (tws != 0) {
-                float fontSize = font.getFontSize() / 1000f;
-                textUtil.adjust(Math.round(tws / fontSize * 10));
+                textUtil.adjust(tws);
             }
         }
         super.renderSpace(space);
@@ -891,7 +897,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
         for (int i = 0; i < l; i++) {
             char ch = s.charAt(i);
             textUtil.addChar(ch);
-            float glyphAdjust = 0;
+            int glyphAdjust = 0;
             if (font.hasChar(ch)) {
                 int tls = (i < l - 1 ? parentArea.getTextLetterSpaceAdjust() : 0);
                 glyphAdjust += tls;
@@ -900,9 +906,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
                 glyphAdjust += letterAdjust[i];
             }
 
-            float adjust = glyphAdjust / fontSize;
-
-            textUtil.adjust(Math.round(adjust * 10));
+            textUtil.adjust(glyphAdjust);
         }
     }
 
index 95d1f20fe347a4315bb1a37b64efb8d40a0d44b0..661a2fdddd99afe389dac877824964d8e3cbadb9 100644 (file)
@@ -191,11 +191,12 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler implements
     }
 
     /** {@inheritDoc} */
-    public void startPage(int index, String name, Dimension size) throws IFException {
+    public void startPage(int index, String name, String pageMasterName, Dimension size) throws IFException {
         try {
             AttributesImpl atts = new AttributesImpl();
             addAttribute(atts, "index", Integer.toString(index));
             addAttribute(atts, "name", name);
+            addAttribute(atts, "page-master-name", name);
             addAttribute(atts, "width", Integer.toString(size.width));
             addAttribute(atts, "height", Integer.toString(size.height));
             handler.startElement(EL_PAGE, atts);
index 013be3f869d8b0ead6568ded1c307c415b7a0e46..9da39a319c827c2a64354d0cc8ea84753706fd00 100644 (file)
@@ -233,7 +233,7 @@ public class Java2DPainter extends AbstractIFPainter {
                 glyphAdjust += dx[i + 1];
             }
 
-            cursor.setLocation(cursor.getX() + cw - glyphAdjust, cursor.getY());
+            cursor.setLocation(cursor.getX() + cw + glyphAdjust, cursor.getY());
             gv.setGlyphPosition(i + 1, cursor);
         }
         g2d.drawGlyphVector(gv, x, y);
index 20dc3cac22e665b63df146d1ff20842cc8821086..3e9201da4e9faae072dca64fdd206513cca20a4f 100644 (file)
@@ -173,7 +173,7 @@ public class PCLDocumentHandler extends AbstractBinaryWritingIFDocumentHandler
     }
 
     /** {@inheritDoc} */
-    public void startPage(int index, String name, Dimension size) throws IFException {
+    public void startPage(int index, String name, String pageMasterName, Dimension size) throws IFException {
 
         try {
             //TODO Add support for paper-source and duplex-mode
index b709c28f3604afbe9f9bd4f208d1caca375dd16e..784aca941e9eb0270ca839b429181c82d27af234 100644 (file)
@@ -414,7 +414,7 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants {
                 glyphAdjust += dx[i + 1];
             }
 
-            width += cw - glyphAdjust;
+            width += cw + glyphAdjust;
         }
         int extraWidth = font.getFontSize() / 3;
         boundingRect.setSize(
@@ -447,14 +447,14 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants {
                     g2d.setBackground(Color.LIGHT_GRAY);
                     g2d.clearRect(0, 0, (int)area.getWidth(), (int)area.getHeight());
                 }
-                g2d.translate(0, -y + baselineOffset);
+                g2d.translate(-x, -y + baselineOffset);
 
                 if (DEBUG) {
                     Rectangle rect = new Rectangle(x, y - maxAscent, 3000, maxAscent);
                     g2d.draw(rect);
                     rect = new Rectangle(x, y - ascent, 2000, ascent);
                     g2d.draw(rect);
-                    rect = new Rectangle(x, y, 1000, - descent);
+                    rect = new Rectangle(x, y, 1000, -descent);
                     g2d.draw(rect);
                 }
                 Java2DPainter painter = new Java2DPainter(g2d,
index 35dc0960a1d50329e412346f33b3f878d88c1dd7..70e0f7eb5a2540f501da502e46f72339777930a9 100644 (file)
@@ -185,20 +185,6 @@ public class PDFContentGenerator {
         }
     }
 
-    /**
-     * Converts a transformation matrix from millipoints to points.
-     * @param transform the transformation matrix (in millipoints)
-     * @return the converted transformation matrix (in points)
-     */
-    public AffineTransform toPoints(AffineTransform transform) {
-        final double[] matrix = new double[6];
-        transform.getMatrix(matrix);
-        //Convert from millipoints to points
-        matrix[4] /= 1000;
-        matrix[5] /= 1000;
-        return new AffineTransform(matrix);
-    }
-
     /**
      * Concatenates the given transformation matrix with the current one.
      * @param transform the transformation matrix (in points)
index 8937d0d1d84efa79097b36959cb570fa04bdd995..1358b1c5ecc907715cfce191661beb1e1dfefb0e 100644 (file)
@@ -168,7 +168,8 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
     }
 
     /** {@inheritDoc} */
-    public void startPage(int index, String name, Dimension size) throws IFException {
+    public void startPage(int index, String name, String pageMasterName, Dimension size)
+                throws IFException {
         this.pdfResources = this.pdfDoc.getResources();
 
         this.currentPage = this.pdfDoc.getFactory().makePage(
index 5baaedbead4684d672bcfa9100e80664dcefb885..8e29ded4ceef58355e5e8e309fc7e7a3c48733c1 100644 (file)
@@ -100,7 +100,7 @@ public class PDFPainter extends AbstractIFPainter {
     public void startViewport(AffineTransform transform, Dimension size, Rectangle clipRect)
             throws IFException {
         generator.saveGraphicsState();
-        generator.concatenate(generator.toPoints(transform));
+        generator.concatenate(toPoints(transform));
         if (clipRect != null) {
             clipRect(clipRect);
         }
@@ -114,7 +114,7 @@ public class PDFPainter extends AbstractIFPainter {
     /** {@inheritDoc} */
     public void startGroup(AffineTransform transform) throws IFException {
         generator.saveGraphicsState();
-        generator.concatenate(generator.toPoints(transform));
+        generator.concatenate(toPoints(transform));
     }
 
     /** {@inheritDoc} */
@@ -196,8 +196,8 @@ public class PDFPainter extends AbstractIFPainter {
         if (fill == null) {
             return;
         }
-        generator.endTextObject();
         if (rect.width != 0 && rect.height != 0) {
+            generator.endTextObject();
             if (fill != null) {
                 if (fill instanceof Color) {
                     generator.updateColor((Color)fill, true, null);
@@ -302,7 +302,7 @@ public class PDFPainter extends AbstractIFPainter {
                     //Fixed width space are rendered as spaces so copy/paste works in a reader
                     ch = font.mapChar(CharUtilities.SPACE);
                     int spaceDiff = font.getCharWidth(ch) - font.getCharWidth(orgChar);
-                    glyphAdjust = -(10 * spaceDiff / fontSize);
+                    glyphAdjust = -spaceDiff;
                 } else {
                     ch = font.mapChar(orgChar);
                 }
@@ -314,7 +314,7 @@ public class PDFPainter extends AbstractIFPainter {
             }
 
             if (glyphAdjust != 0) {
-                textutil.adjustGlyphTJ(-glyphAdjust / 10f);
+                textutil.adjustGlyphTJ(-glyphAdjust / fontSize);
             }
 
         }
diff --git a/src/java/org/apache/fop/render/ps/PSConfigurationConstants.java b/src/java/org/apache/fop/render/ps/PSConfigurationConstants.java
new file mode 100644 (file)
index 0000000..6b3550f
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps;
+
+/**
+ * Constants used for configuring PostScript output.
+ */
+public interface PSConfigurationConstants {
+
+    /** Controls the behaviour for landscape pages */
+    String AUTO_ROTATE_LANDSCAPE = "auto-rotate-landscape";
+    /** Controls whether resources are optimized (rather than inlined) */
+    String OPTIMIZE_RESOURCES = "optimize-resources";
+    /** Determines the PostScript language level to be generated */
+    String LANGUAGE_LEVEL = "language-level";
+}
diff --git a/src/java/org/apache/fop/render/ps/PSDocumentHandler.java b/src/java/org/apache/fop/render/ps/PSDocumentHandler.java
new file mode 100644 (file)
index 0000000..8223ec2
--- /dev/null
@@ -0,0 +1,522 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps;
+
+import java.awt.Dimension;
+import java.awt.geom.Rectangle2D;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.transform.Source;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.xmlgraphics.ps.DSCConstants;
+import org.apache.xmlgraphics.ps.PSGenerator;
+import org.apache.xmlgraphics.ps.PSPageDeviceDictionary;
+import org.apache.xmlgraphics.ps.PSProcSets;
+import org.apache.xmlgraphics.ps.PSResource;
+import org.apache.xmlgraphics.ps.dsc.DSCException;
+import org.apache.xmlgraphics.ps.dsc.ResourceTracker;
+import org.apache.xmlgraphics.ps.dsc.events.DSCCommentBoundingBox;
+import org.apache.xmlgraphics.ps.dsc.events.DSCCommentHiResBoundingBox;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.apps.MimeConstants;
+import org.apache.fop.fonts.LazyFont;
+import org.apache.fop.fonts.Typeface;
+import org.apache.fop.render.intermediate.AbstractBinaryWritingIFDocumentHandler;
+import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator;
+import org.apache.fop.render.intermediate.IFException;
+import org.apache.fop.render.intermediate.IFPainter;
+import org.apache.fop.render.ps.extensions.PSExtensionAttachment;
+import org.apache.fop.render.ps.extensions.PSSetPageDevice;
+
+/**
+ * {@code IFDocumentHandler} implementation that produces PostScript.
+ */
+public class PSDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
+
+    /** logging instance */
+    private static Log log = LogFactory.getLog(PSDocumentHandler.class);
+
+    /**
+     * Utility class which enables all sorts of features that are not directly connected to the
+     * normal rendering process.
+     */
+    protected PSRenderingUtil psUtil;
+
+    /** The PostScript generator used to output the PostScript */
+    protected PSGenerator gen;
+
+    /** the temporary file in case of two-pass processing */
+    private File tempFile;
+
+    private int currentPageNumber = 0;
+
+    /** Is used to determine the document's bounding box */
+    private Rectangle2D documentBoundingBox;
+
+    /** Used to temporarily store PSSetupCode instance until they can be written. */
+    private List setupCodeList;
+
+    /** This is a map of PSResource instances of all fonts defined (key: font key) */
+    private Map fontResources;
+    /** This is a map of PSResource instances of all forms (key: uri) */
+    private Map formResources;
+
+    /** encapsulation of dictionary used in setpagedevice instruction **/
+    private PSPageDeviceDictionary pageDeviceDictionary;
+
+    /** This is a collection holding all document header comments */
+    private Collection headerComments;
+
+    /** This is a collection holding all document footer comments */
+    private Collection footerComments;
+
+    /**
+     * Default constructor.
+     */
+    public PSDocumentHandler() {
+    }
+
+    /** {@inheritDoc} */
+    public boolean supportsPagesOutOfOrder() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    public String getMimeType() {
+        return MimeConstants.MIME_POSTSCRIPT;
+    }
+
+    /** {@inheritDoc} */
+    public void setUserAgent(FOUserAgent ua) {
+        super.setUserAgent(ua);
+        this.psUtil = new PSRenderingUtil(ua);
+    }
+
+    /** {@inheritDoc} */
+    public IFDocumentHandlerConfigurator getConfigurator() {
+        return new PSRendererConfigurator(getUserAgent());
+    }
+
+    PSRenderingUtil getPSUtil() {
+        return this.psUtil;
+    }
+
+    /** {@inheritDoc} */
+    public void startDocument() throws IFException {
+        try {
+            if (getUserAgent() == null) {
+                throw new IllegalStateException(
+                        "User agent must be set before starting PostScript generation");
+            }
+            if (this.outputStream == null) {
+                throw new IllegalStateException("OutputStream hasn't been set through setResult()");
+            }
+            OutputStream out;
+            if (psUtil.isOptimizeResources()) {
+                this.tempFile = File.createTempFile("fop", null);
+                out = new java.io.FileOutputStream(this.tempFile);
+                out = new java.io.BufferedOutputStream(out);
+            } else {
+                out = this.outputStream;
+            }
+
+            //Setup for PostScript generation
+            this.gen = new PSGenerator(out) {
+                /** Need to subclass PSGenerator to have better URI resolution */
+                public Source resolveURI(String uri) {
+                    return getUserAgent().resolveURI(uri);
+                }
+            };
+            this.gen.setPSLevel(psUtil.getLanguageLevel());
+            this.currentPageNumber = 0;
+            this.documentBoundingBox = new Rectangle2D.Double();
+
+            //Initial default page device dictionary settings
+            this.pageDeviceDictionary = new PSPageDeviceDictionary();
+            pageDeviceDictionary.setFlushOnRetrieval(!psUtil.isDSCComplianceEnabled());
+            pageDeviceDictionary.put("/ImagingBBox", "null");
+        } catch (IOException e) {
+            throw new IFException("I/O error in startDocument()", e);
+        }
+    }
+
+    private void writeHeader() throws IOException {
+        //PostScript Header
+        gen.writeln(DSCConstants.PS_ADOBE_30);
+        gen.writeDSCComment(DSCConstants.CREATOR, new String[] {getUserAgent().getProducer()});
+        gen.writeDSCComment(DSCConstants.CREATION_DATE, new Object[] {new java.util.Date()});
+        gen.writeDSCComment(DSCConstants.LANGUAGE_LEVEL, new Integer(gen.getPSLevel()));
+        gen.writeDSCComment(DSCConstants.PAGES, new Object[] {DSCConstants.ATEND});
+        gen.writeDSCComment(DSCConstants.BBOX, DSCConstants.ATEND);
+        gen.writeDSCComment(DSCConstants.HIRES_BBOX, DSCConstants.ATEND);
+        gen.writeDSCComment(DSCConstants.DOCUMENT_SUPPLIED_RESOURCES,
+                new Object[] {DSCConstants.ATEND});
+        if (headerComments != null) {
+            for (Iterator iter = headerComments.iterator(); iter.hasNext();) {
+                PSExtensionAttachment comment = (PSExtensionAttachment)iter.next();
+                gen.writeln("%" + comment.getContent());
+            }
+        }
+        gen.writeDSCComment(DSCConstants.END_COMMENTS);
+
+        //Defaults
+        gen.writeDSCComment(DSCConstants.BEGIN_DEFAULTS);
+        gen.writeDSCComment(DSCConstants.END_DEFAULTS);
+
+        //Prolog and Setup written right before the first page-sequence, see startPageSequence()
+        //Do this only once, as soon as we have all the content for the Setup section!
+        //Prolog
+        gen.writeDSCComment(DSCConstants.BEGIN_PROLOG);
+        PSProcSets.writeStdProcSet(gen);
+        PSProcSets.writeEPSProcSet(gen);
+        gen.writeDSCComment(DSCConstants.END_PROLOG);
+
+        //Setup
+        gen.writeDSCComment(DSCConstants.BEGIN_SETUP);
+        PSRenderingUtil.writeSetupCodeList(gen, setupCodeList, "SetupCode");
+        if (!psUtil.isOptimizeResources()) {
+            this.fontResources = PSFontUtils.writeFontDict(gen, fontInfo);
+        } else {
+            gen.commentln("%FOPFontSetup"); //Place-holder, will be replaced in the second pass
+        }
+        gen.writeDSCComment(DSCConstants.END_SETUP);
+    }
+
+    /** {@inheritDoc} */
+    public void endDocumentHeader() throws IFException {
+        try {
+            writeHeader();
+        } catch (IOException ioe) {
+            throw new IFException("I/O error writing the PostScript header", ioe);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void endDocument() throws IFException {
+        try {
+            //Write trailer
+            gen.writeDSCComment(DSCConstants.TRAILER);
+            if (footerComments != null) {
+                for (Iterator iter = footerComments.iterator(); iter.hasNext();) {
+                    PSExtensionAttachment comment = (PSExtensionAttachment)iter.next();
+                    gen.commentln("%" + comment.getContent());
+                }
+                footerComments.clear();
+            }
+            gen.writeDSCComment(DSCConstants.PAGES, new Integer(this.currentPageNumber));
+            new DSCCommentBoundingBox(this.documentBoundingBox).generate(gen);
+            new DSCCommentHiResBoundingBox(this.documentBoundingBox).generate(gen);
+            gen.getResourceTracker().writeResources(false, gen);
+            gen.writeDSCComment(DSCConstants.EOF);
+            gen.flush();
+            log.debug("Rendering to PostScript complete.");
+            if (psUtil.isOptimizeResources()) {
+                IOUtils.closeQuietly(gen.getOutputStream());
+                rewritePostScriptFile();
+            }
+            if (footerComments != null) {
+                headerComments.clear();
+            }
+            if (pageDeviceDictionary != null) {
+                pageDeviceDictionary.clear();
+            }
+        } catch (IOException ioe) {
+            throw new IFException("I/O error in endDocument()", ioe);
+        }
+        super.endDocument();
+    }
+
+    /**
+     * Used for two-pass production. This will rewrite the PostScript file from the temporary
+     * file while adding all needed resources.
+     * @throws IOException In case of an I/O error.
+     */
+    private void rewritePostScriptFile() throws IOException {
+        log.debug("Processing PostScript resources...");
+        long startTime = System.currentTimeMillis();
+        ResourceTracker resTracker = gen.getResourceTracker();
+        InputStream in = new java.io.FileInputStream(this.tempFile);
+        in = new java.io.BufferedInputStream(in);
+        try {
+            try {
+                ResourceHandler.process(getUserAgent(), in, this.outputStream,
+                        this.fontInfo, resTracker, this.formResources,
+                        this.currentPageNumber, this.documentBoundingBox);
+                this.outputStream.flush();
+            } catch (DSCException e) {
+                throw new RuntimeException(e.getMessage());
+            }
+        } finally {
+            IOUtils.closeQuietly(in);
+            if (!this.tempFile.delete()) {
+                this.tempFile.deleteOnExit();
+                log.warn("Could not delete temporary file: " + this.tempFile);
+            }
+        }
+        if (log.isDebugEnabled()) {
+            long duration = System.currentTimeMillis() - startTime;
+            log.debug("Resource Processing complete in " + duration + " ms.");
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void startPageSequence(String id) throws IFException {
+        //nop
+    }
+
+    /** {@inheritDoc} */
+    public void endPageSequence() throws IFException {
+        //nop
+    }
+
+    /** {@inheritDoc} */
+    public void startPage(int index, String name, String pageMasterName, Dimension size)
+                throws IFException {
+        try {
+            if (this.currentPageNumber == 0) {
+                //writeHeader();
+            }
+
+            this.currentPageNumber++;
+
+            gen.getResourceTracker().notifyStartNewPage();
+            gen.getResourceTracker().notifyResourceUsageOnPage(PSProcSets.STD_PROCSET);
+            gen.writeDSCComment(DSCConstants.PAGE, new Object[]
+                    {name,
+                     new Integer(this.currentPageNumber)});
+
+            double pageWidth = size.width / 1000.0;
+            double pageHeight = size.height / 1000.0;
+            boolean rotate = false;
+            List pageSizes = new java.util.ArrayList();
+            if (this.psUtil.isAutoRotateLandscape() && (pageHeight < pageWidth)) {
+                rotate = true;
+                pageSizes.add(new Long(Math.round(pageHeight)));
+                pageSizes.add(new Long(Math.round(pageWidth)));
+            } else {
+                pageSizes.add(new Long(Math.round(pageWidth)));
+                pageSizes.add(new Long(Math.round(pageHeight)));
+            }
+            pageDeviceDictionary.put("/PageSize", pageSizes);
+
+            //TODO Handle extension attachments for the page!!!!!!!
+            /*
+            if (page.hasExtensionAttachments()) {
+                for (Iterator iter = page.getExtensionAttachments().iterator();
+                    iter.hasNext();) {
+                    ExtensionAttachment attachment = (ExtensionAttachment) iter.next();
+                    if (attachment instanceof PSSetPageDevice) {*/
+                        /**
+                         * Extract all PSSetPageDevice instances from the
+                         * attachment list on the s-p-m and add all
+                         * dictionary entries to our internal representation
+                         * of the the page device dictionary.
+                         *//*
+                        PSSetPageDevice setPageDevice = (PSSetPageDevice)attachment;
+                        String content = setPageDevice.getContent();
+                        if (content != null) {
+                            try {
+                                pageDeviceDictionary.putAll(PSDictionary.valueOf(content));
+                            } catch (PSDictionaryFormatException e) {
+                                PSEventProducer eventProducer = PSEventProducer.Provider.get(
+                                        getUserAgent().getEventBroadcaster());
+                                eventProducer.postscriptDictionaryParseError(this, content, e);
+                            }
+                        }
+                    }
+                }
+            }*/
+
+            if (setupCodeList != null) {
+                PSRenderingUtil.writeEnclosedExtensionAttachments(gen, setupCodeList);
+                setupCodeList.clear();
+            }
+            final Integer zero = new Integer(0);
+            Rectangle2D pageBoundingBox = new Rectangle2D.Double();
+            if (rotate) {
+                pageBoundingBox.setRect(0, 0, pageHeight, pageWidth);
+                gen.writeDSCComment(DSCConstants.PAGE_BBOX, new Object[] {
+                        zero, zero, new Long(Math.round(pageHeight)),
+                        new Long(Math.round(pageWidth)) });
+                gen.writeDSCComment(DSCConstants.PAGE_HIRES_BBOX, new Object[] {
+                        zero, zero, new Double(pageHeight),
+                        new Double(pageWidth) });
+                gen.writeDSCComment(DSCConstants.PAGE_ORIENTATION, "Landscape");
+            } else {
+                pageBoundingBox.setRect(0, 0, pageWidth, pageHeight);
+                gen.writeDSCComment(DSCConstants.PAGE_BBOX, new Object[] {
+                        zero, zero, new Long(Math.round(pageWidth)),
+                        new Long(Math.round(pageHeight)) });
+                gen.writeDSCComment(DSCConstants.PAGE_HIRES_BBOX, new Object[] {
+                        zero, zero, new Double(pageWidth),
+                        new Double(pageHeight) });
+                if (psUtil.isAutoRotateLandscape()) {
+                    gen.writeDSCComment(DSCConstants.PAGE_ORIENTATION,
+                            "Portrait");
+                }
+            }
+            this.documentBoundingBox.add(pageBoundingBox);
+            gen.writeDSCComment(DSCConstants.PAGE_RESOURCES,
+                    new Object[] {DSCConstants.ATEND});
+
+            gen.commentln("%FOPSimplePageMaster: " + pageMasterName);
+
+            gen.writeDSCComment(DSCConstants.BEGIN_PAGE_SETUP);
+
+            //TODO Handle extension attachments for the page!!!!!!!
+            /*
+            if (page.hasExtensionAttachments()) {
+                List extensionAttachments = page.getExtensionAttachments();
+                for (int i = 0; i < extensionAttachments.size(); i++) {
+                    Object attObj = extensionAttachments.get(i);
+                    if (attObj instanceof PSExtensionAttachment) {
+                        PSExtensionAttachment attachment = (PSExtensionAttachment)attObj;
+                        if (attachment instanceof PSCommentBefore) {
+                            gen.commentln("%" + attachment.getContent());
+                        } else if (attachment instanceof PSSetupCode) {
+                            gen.writeln(attachment.getContent());
+                        }
+                    }
+                }
+            }*/
+
+            // Write any unwritten changes to page device dictionary
+            if (!pageDeviceDictionary.isEmpty()) {
+                String content = pageDeviceDictionary.getContent();
+                if (psUtil.isSafeSetPageDevice()) {
+                    content += " SSPD";
+                } else {
+                    content += " setpagedevice";
+                }
+                PSRenderingUtil.writeEnclosedExtensionAttachment(gen, new PSSetPageDevice(content));
+            }
+
+            if (rotate) {
+                gen.writeln(Math.round(pageHeight) + " 0 translate");
+                gen.writeln("90 rotate");
+            }
+            gen.concatMatrix(1, 0, 0, -1, 0, pageHeight);
+
+            gen.writeDSCComment(DSCConstants.END_PAGE_SETUP);
+        } catch (IOException ioe) {
+            throw new IFException("I/O error in startPage()", ioe);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public IFPainter startPageContent() throws IFException {
+        return new PSPainter(this);
+    }
+
+    /** {@inheritDoc} */
+    public void endPageContent() throws IFException {
+        try {
+            //Show page
+            gen.writeln("showpage");
+        } catch (IOException ioe) {
+            throw new IFException("I/O error in endPageContent()", ioe);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void endPage() throws IFException {
+        try {
+            gen.writeDSCComment(DSCConstants.PAGE_TRAILER);
+
+            //TODO Handle extension attachments for the page!!!!!!!
+            /*
+            if (page.hasExtensionAttachments()) {
+                List extensionAttachments = page.getExtensionAttachments();
+                for (int i = 0; i < extensionAttachments.size(); i++) {
+                    Object attObj = extensionAttachments.get(i);
+                    if (attObj instanceof PSExtensionAttachment) {
+                        PSExtensionAttachment attachment = (PSExtensionAttachment)attObj;
+                        if (attachment instanceof PSCommentAfter) {
+                            gen.commentln("%" + attachment.getContent());
+                        }
+                    }
+                }
+            }*/
+            gen.getResourceTracker().writeResources(true, gen);
+        } catch (IOException ioe) {
+            throw new IFException("I/O error in endPage()", ioe);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void handleExtensionObject(Object extension) throws IFException {
+        log.debug("Don't know how to handle extension object. Ignoring: "
+                + extension + " (" + extension.getClass().getName() + ")");
+    }
+
+    private String getPostScriptNameForFontKey(String key) {
+        int pos = key.indexOf('_');
+        String postFix = null;
+        if (pos > 0) {
+            postFix = key.substring(pos);
+            key = key.substring(0, pos);
+        }
+        Map fonts = fontInfo.getFonts();
+        Typeface tf = (Typeface)fonts.get(key);
+        if (tf instanceof LazyFont) {
+            tf = ((LazyFont)tf).getRealFont();
+        }
+        if (tf == null) {
+            throw new IllegalStateException("Font not available: " + key);
+        }
+        if (postFix == null) {
+            return tf.getFontName();
+        } else {
+            return tf.getFontName() + postFix;
+        }
+    }
+
+    /**
+     * Returns the PSResource for the given font key.
+     * @param key the font key ("F*")
+     * @return the matching PSResource
+     */
+    protected PSResource getPSResourceForFontKey(String key) {
+        PSResource res = null;
+        if (this.fontResources != null) {
+            res = (PSResource)this.fontResources.get(key);
+        } else {
+            this.fontResources = new java.util.HashMap();
+        }
+        if (res == null) {
+            res = new PSResource(PSResource.TYPE_FONT, getPostScriptNameForFontKey(key));
+            this.fontResources.put(key, res);
+        }
+        return res;
+    }
+
+}
diff --git a/src/java/org/apache/fop/render/ps/PSDocumentHandlerMaker.java b/src/java/org/apache/fop/render/ps/PSDocumentHandlerMaker.java
new file mode 100644 (file)
index 0000000..635cd07
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.apps.MimeConstants;
+import org.apache.fop.render.intermediate.AbstractIFDocumentHandlerMaker;
+import org.apache.fop.render.intermediate.IFDocumentHandler;
+import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator;
+
+/**
+ * Intermediate format document handler factory for PostScript output.
+ */
+public class PSDocumentHandlerMaker extends AbstractIFDocumentHandlerMaker {
+
+    //TODO Revert to normal MIME after stabilization!
+    private static final String[] MIMES = new String[]
+                              {MimeConstants.MIME_POSTSCRIPT + ";mode=painter"};
+
+    /** {@inheritDoc} */
+    public IFDocumentHandler makeIFDocumentHandler(FOUserAgent ua) {
+        PSDocumentHandler handler = new PSDocumentHandler();
+        handler.setUserAgent(ua);
+        return handler;
+    }
+
+    /** {@inheritDoc} */
+    public boolean needsOutputStream() {
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    public String[] getSupportedMimeTypes() {
+        return MIMES;
+    }
+
+    /** {@inheritDoc} */
+    public IFDocumentHandlerConfigurator getConfigurator(FOUserAgent userAgent) {
+        return new PSRendererConfigurator(userAgent);
+    }
+
+}
diff --git a/src/java/org/apache/fop/render/ps/PSPainter.java b/src/java/org/apache/fop/render/ps/PSPainter.java
new file mode 100644 (file)
index 0000000..e43b0f2
--- /dev/null
@@ -0,0 +1,395 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Paint;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.geom.AffineTransform;
+import java.io.IOException;
+import java.util.Map;
+
+import org.w3c.dom.Document;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.xmlgraphics.ps.PSGenerator;
+import org.apache.xmlgraphics.ps.PSResource;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.fonts.Font;
+import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.fonts.FontTriplet;
+import org.apache.fop.fonts.LazyFont;
+import org.apache.fop.fonts.SingleByteFont;
+import org.apache.fop.fonts.Typeface;
+import org.apache.fop.render.RenderingContext;
+import org.apache.fop.render.intermediate.AbstractIFPainter;
+import org.apache.fop.render.intermediate.IFException;
+import org.apache.fop.render.intermediate.IFState;
+import org.apache.fop.traits.BorderProps;
+import org.apache.fop.traits.RuleStyle;
+import org.apache.fop.util.CharUtilities;
+
+/**
+ * IFPainter implementation that produces PostScript.
+ */
+public class PSPainter extends AbstractIFPainter {
+
+    /** logging instance */
+    private static Log log = LogFactory.getLog(PSPainter.class);
+
+    private PSDocumentHandler documentHandler;
+
+    private boolean inTextMode = false;
+
+    /**
+     * Default constructor.
+     * @param documentHandler the parent document handler
+     */
+    public PSPainter(PSDocumentHandler documentHandler) {
+        super();
+        this.documentHandler = documentHandler;
+        this.state = IFState.create();
+    }
+
+    /** {@inheritDoc} */
+    protected FOUserAgent getUserAgent() {
+        return this.documentHandler.getUserAgent();
+    }
+
+    PSRenderingUtil getPSUtil() {
+        return this.documentHandler.psUtil;
+    }
+
+    FontInfo getFontInfo() {
+        return this.documentHandler.getFontInfo();
+    }
+
+    private PSGenerator getGenerator() {
+        return this.documentHandler.gen;
+    }
+
+    /** {@inheritDoc} */
+    public void startViewport(AffineTransform transform, Dimension size, Rectangle clipRect)
+            throws IFException {
+        try {
+            PSGenerator generator = getGenerator();
+            saveGraphicsState();
+            generator.concatMatrix(toPoints(transform));
+        } catch (IOException ioe) {
+            throw new IFException("I/O error in startViewport()", ioe);
+        }
+        if (clipRect != null) {
+            clipRect(clipRect);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void endViewport() throws IFException {
+        try {
+            restoreGraphicsState();
+        } catch (IOException ioe) {
+            throw new IFException("I/O error in endViewport()", ioe);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void startGroup(AffineTransform transform) throws IFException {
+        try {
+            PSGenerator generator = getGenerator();
+            saveGraphicsState();
+            generator.concatMatrix(toPoints(transform));
+        } catch (IOException ioe) {
+            throw new IFException("I/O error in startGroup()", ioe);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void endGroup() throws IFException {
+        try {
+            restoreGraphicsState();
+        } catch (IOException ioe) {
+            throw new IFException("I/O error in endGroup()", ioe);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void drawImage(String uri, Rectangle rect, Map foreignAttributes) throws IFException {
+        //TODO Implement me
+    }
+
+    /** {@inheritDoc} */
+    protected RenderingContext createRenderingContext() {
+        PSRenderingContext psContext = new PSRenderingContext(
+                getUserAgent(), getFontInfo());
+        return psContext;
+    }
+
+    /** {@inheritDoc} */
+    public void drawImage(Document doc, Rectangle rect, Map foreignAttributes) throws IFException {
+        drawImageUsingDocument(doc, rect);
+    }
+
+    /** {@inheritDoc} */
+    public void clipRect(Rectangle rect) throws IFException {
+        try {
+            PSGenerator generator = getGenerator();
+            endTextObject();
+            generator.defineRect(rect.x / 1000.0, rect.y / 1000.0,
+                    rect.width / 1000.0, rect.height / 1000.0);
+            generator.writeln("clip newpath");
+        } catch (IOException ioe) {
+            throw new IFException("I/O error in clipRect()", ioe);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void fillRect(Rectangle rect, Paint fill) throws IFException {
+        if (fill == null) {
+            return;
+        }
+        if (rect.width != 0 && rect.height != 0) {
+            try {
+                endTextObject();
+                PSGenerator generator = getGenerator();
+                if (fill != null) {
+                    if (fill instanceof Color) {
+                        generator.useColor((Color)fill);
+                    } else {
+                        throw new UnsupportedOperationException("Non-Color paints NYI");
+                    }
+                }
+                generator.defineRect(rect.x / 1000.0, rect.y / 1000.0,
+                        rect.width / 1000.0, rect.height / 1000.0);
+                generator.writeln("fill");
+            } catch (IOException ioe) {
+                throw new IFException("I/O error in fillRect()", ioe);
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void drawBorderRect(Rectangle rect, BorderProps before, BorderProps after,
+            BorderProps start, BorderProps end) throws IFException {
+        if (before != null || after != null || start != null || end != null) {
+            try {
+                //TODO Implement me
+                endTextObject();
+                //this.borderPainter.drawBorders(rect, before, after, start, end);
+            } catch (IOException ioe) {
+                throw new IFException("I/O error in drawBorderRect()", ioe);
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void drawLine(Point start, Point end, int width, Color color, RuleStyle style)
+                throws IFException {
+        try {
+            //TODO Implement me
+            endTextObject();
+            //this.borderPainter.drawLine(start, end, width, color, style);
+        } catch (IOException ioe) {
+            throw new IFException("I/O error in drawLine()", ioe);
+        }
+    }
+
+    private Typeface getTypeface(String fontName) {
+        if (fontName == null) {
+            throw new NullPointerException("fontName must not be null");
+        }
+        Typeface tf = (Typeface)getFontInfo().getFonts().get(fontName);
+        if (tf instanceof LazyFont) {
+            tf = ((LazyFont)tf).getRealFont();
+        }
+        return tf;
+    }
+
+    /**
+     * Saves the graphics state of the rendering engine.
+     * @throws IOException if an I/O error occurs
+     */
+    protected void saveGraphicsState() throws IOException {
+        endTextObject();
+        getGenerator().saveGraphicsState();
+    }
+
+    /**
+     * Restores the last graphics state of the rendering engine.
+     * @throws IOException if an I/O error occurs
+     */
+    protected void restoreGraphicsState() throws IOException {
+        endTextObject();
+        getGenerator().restoreGraphicsState();
+    }
+
+    /**
+     * Indicates the beginning of a text object.
+     * @throws IOException if an I/O error occurs
+     */
+    protected void beginTextObject() throws IOException {
+        if (!inTextMode) {
+            PSGenerator generator = getGenerator();
+            generator.saveGraphicsState();
+            generator.writeln("BT");
+            inTextMode = true;
+        }
+    }
+
+    /**
+     * Indicates the end of a text object.
+     * @throws IOException if an I/O error occurs
+     */
+    protected void endTextObject() throws IOException {
+        if (inTextMode) {
+            inTextMode = false;
+            PSGenerator generator = getGenerator();
+            generator.writeln("ET");
+            generator.restoreGraphicsState();
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void drawText(int x, int y, int[] dx, int[] dy, String text) throws IFException {
+        try {
+            //Note: dy is currently ignored
+            PSGenerator generator = getGenerator();
+            generator.useColor(state.getTextColor());
+            beginTextObject();
+            FontTriplet triplet = new FontTriplet(
+                    state.getFontFamily(), state.getFontStyle(), state.getFontWeight());
+            //TODO Ignored: state.getFontVariant()
+            //TODO Opportunity for font caching if font state is more heavily used
+            String fontKey = getFontInfo().getInternalFontKey(triplet);
+            int sizeMillipoints = state.getFontSize();
+            float fontSize = sizeMillipoints / 1000f;
+
+            // This assumes that *all* CIDFonts use a /ToUnicode mapping
+            Typeface tf = getTypeface(fontKey);
+            SingleByteFont singleByteFont = null;
+            if (tf instanceof SingleByteFont) {
+                singleByteFont = (SingleByteFont)tf;
+            }
+            Font font = getFontInfo().getFontInstance(triplet, sizeMillipoints);
+            //String fontName = font.getFontName();
+
+            PSResource res = this.documentHandler.getPSResourceForFontKey(fontKey);
+            generator.useFont("/" + res.getName(), fontSize);
+            generator.getResourceTracker().notifyResourceUsageOnPage(res);
+            //textutil.updateTf(fontKey, fontSize, tf.isMultiByte());
+
+            generator.writeln("1 0 0 -1 " + generator.formatDouble(x / 1000.0)
+                    + " " + generator.formatDouble(y / 1000.0) + " Tm");
+            //textutil.writeTextMatrix(new AffineTransform(1, 0, 0, -1, x / 1000f, y / 1000f));
+
+            int textLen = text.length();
+            if (singleByteFont != null && singleByteFont.hasAdditionalEncodings()) {
+                //Analyze string and split up in order to paint in different sub-fonts/encodings
+                int start = 0;
+                int currentEncoding = -1;
+                for (int i = 0; i < textLen; i++) {
+                    char c = text.charAt(i);
+                    char mapped = tf.mapChar(c);
+                    int encoding = mapped / 256;
+                    if (currentEncoding != encoding) {
+                        if (i > 0) {
+                            writeText(text, start, i - start, dx, dy, font, tf);
+                        }
+                        if (encoding == 0) {
+                            useFont(fontKey, sizeMillipoints);
+                        } else {
+                            useFont(fontKey + "_" + Integer.toString(encoding), sizeMillipoints);
+                        }
+                        currentEncoding = encoding;
+                        start = i;
+                    }
+                }
+                writeText(text, start, textLen - start, dx, dy, font, tf);
+            } else {
+                //Simple single-font painting
+                useFont(fontKey, sizeMillipoints);
+                writeText(text, 0, textLen, dx, dy, font, tf);
+            }
+        } catch (IOException ioe) {
+            throw new IFException("I/O error in drawText()", ioe);
+        }
+    }
+
+    private void writeText(String text, int start, int len, int[] dx, int[] dy,
+            Font font, Typeface tf) throws IOException {
+        PSGenerator generator = getGenerator();
+        int end = start + len;
+        int initialSize = len;
+        initialSize += initialSize / 2;
+        StringBuffer sb = new StringBuffer(initialSize);
+        sb.append("(");
+        int[] offsets = new int[len];
+        int dxl = (dx != null ? dx.length : 0);
+        for (int i = start; i < end; i++) {
+            char orgChar = text.charAt(i);
+            char ch;
+            int cw;
+            if (CharUtilities.isFixedWidthSpace(orgChar)) {
+                //Fixed width space are rendered as spaces so copy/paste works in a reader
+                ch = font.mapChar(CharUtilities.SPACE);
+                //int spaceDiff = font.getCharWidth(ch) - font.getCharWidth(orgChar);
+                //glyphAdjust = -(spaceDiff);
+            } else {
+                ch = font.mapChar(orgChar);
+                //cw = tf.getWidth(ch, font.getFontSize()) / 1000;
+            }
+
+            cw = font.getCharWidth(orgChar);
+            int glyphAdjust = 0;
+            if (dx != null && i < dxl - 1) {
+                glyphAdjust += dx[i + 1];
+            }
+            offsets[i - start] = cw + glyphAdjust;
+            char codepoint = (char)(ch % 256);
+            PSGenerator.escapeChar(codepoint, sb);
+        }
+        sb.append(")" + PSGenerator.LF + "[");
+        for (int i = 0; i < len; i++) {
+            if (i > 0) {
+                if (i % 8 == 0) {
+                    sb.append(PSGenerator.LF);
+                } else {
+                    sb.append(" ");
+                }
+            }
+            sb.append(generator.formatDouble(offsets[i] / 1000f));
+        }
+        sb.append("]" + PSGenerator.LF + "xshow");
+        generator.writeln(sb.toString());
+    }
+
+    private void useFont(String key, int size) throws IOException {
+        PSResource res = this.documentHandler.getPSResourceForFontKey(key);
+        PSGenerator generator = getGenerator();
+        generator.useFont("/" + res.getName(), size / 1000f);
+        generator.getResourceTracker().notifyResourceUsageOnPage(res);
+    }
+
+
+}
index 4ccb0bd520822c8b94d4b47db25998400f3259f7..83dbd4b62585ac217a24159aac487f52b889dbe0 100644 (file)
@@ -28,7 +28,6 @@ import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.LineNumberReader;
 import java.io.OutputStream;
 import java.util.Collection;
 import java.util.Iterator;
@@ -128,7 +127,7 @@ import org.apache.fop.util.ColorUtil;
  * @version $Id$
  */
 public class PSRenderer extends AbstractPathOrientedRenderer
-            implements ImageAdapter, PSSupportedFlavors {
+            implements ImageAdapter, PSSupportedFlavors, PSConfigurationConstants {
 
     /** logging instance */
     private static Log log = LogFactory.getLog(PSRenderer.class);
@@ -136,17 +135,9 @@ public class PSRenderer extends AbstractPathOrientedRenderer
     /** The MIME type for PostScript */
     public static final String MIME_TYPE = "application/postscript";
 
-    private static final String AUTO_ROTATE_LANDSCAPE = "auto-rotate-landscape";
-    private static final String OPTIMIZE_RESOURCES = "optimize-resources";
-    private static final String LANGUAGE_LEVEL = "language-level";
-
     /** The application producing the PostScript */
     private int currentPageNumber = 0;
 
-    private final boolean enableComments = true;
-    private boolean autoRotateLandscape = false;
-    private int languageLevel = PSGenerator.DEFAULT_LANGUAGE_LEVEL;
-
     /** the OutputStream the PS file is written to */
     private OutputStream outputStream;
     /** the temporary file in case of two-pass processing */
@@ -154,8 +145,6 @@ public class PSRenderer extends AbstractPathOrientedRenderer
 
     /** The PostScript generator used to output the PostScript */
     protected PSGenerator gen;
-    /** Determines whether the PS file is generated in two passes to minimize file size */
-    private boolean twoPassGeneration = false;
     private boolean ioTrouble = false;
 
     private boolean inTextMode = false;
@@ -171,14 +160,11 @@ public class PSRenderer extends AbstractPathOrientedRenderer
     /** encapsulation of dictionary used in setpagedevice instruction **/
     private PSPageDeviceDictionary pageDeviceDictionary;
 
-    /** Whether or not the safe set page device macro will be used or not */
-    private boolean safeSetPageDevice = false;
-
     /**
-     * Whether or not PostScript Document Structuring Conventions (DSC) compliant output are
-     * enforced.
+     * Utility class which enables all sorts of features that are not directly connected to the
+     * normal rendering process.
      */
-    private boolean dscCompliant = true;
+    protected PSRenderingUtil psUtil;
 
     /** Is used to determine the document's bounding box */
     private Rectangle2D documentBoundingBox;
@@ -192,39 +178,11 @@ public class PSRenderer extends AbstractPathOrientedRenderer
     /** {@inheritDoc} */
     public void setUserAgent(FOUserAgent agent) {
         super.setUserAgent(agent);
-        Object obj;
-        obj = agent.getRendererOptions().get(AUTO_ROTATE_LANDSCAPE);
-        if (obj != null) {
-            setAutoRotateLandscape(booleanValueOf(obj));
-        }
-        obj = agent.getRendererOptions().get(LANGUAGE_LEVEL);
-        if (obj != null) {
-            setLanguageLevel(intValueOf(obj));
-        }
-        obj = agent.getRendererOptions().get(OPTIMIZE_RESOURCES);
-        if (obj != null) {
-            setOptimizeResources(booleanValueOf(obj));
-        }
+        this.psUtil = new PSRenderingUtil(getUserAgent());
     }
 
-    private boolean booleanValueOf(Object obj) {
-        if (obj instanceof Boolean) {
-            return ((Boolean)obj).booleanValue();
-        } else if (obj instanceof String) {
-            return Boolean.valueOf((String)obj).booleanValue();
-        } else {
-            throw new IllegalArgumentException("Boolean or \"true\" or \"false\" expected.");
-        }
-    }
-
-    private int intValueOf(Object obj) {
-        if (obj instanceof Integer) {
-            return ((Integer)obj).intValue();
-        } else if (obj instanceof String) {
-            return Integer.parseInt((String)obj);
-        } else {
-            throw new IllegalArgumentException("Integer or String with a number expected.");
-        }
+    PSRenderingUtil getPSUtil() {
+        return this.psUtil;
     }
 
     /**
@@ -233,12 +191,12 @@ public class PSRenderer extends AbstractPathOrientedRenderer
      *              a "wider-than-long" page by 90 degrees.
      */
     public void setAutoRotateLandscape(boolean value) {
-        this.autoRotateLandscape = value;
+        getPSUtil().setAutoRotateLandscape(value);
     }
 
     /** @return true if the renderer is configured to rotate landscape pages */
     public boolean isAutoRotateLandscape() {
-        return this.autoRotateLandscape;
+        return getPSUtil().isAutoRotateLandscape();
     }
 
     /**
@@ -246,11 +204,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer
      * @param level the language level (currently allowed: 2 or 3)
      */
     public void setLanguageLevel(int level) {
-        if (level == 2 || level == 3) {
-            this.languageLevel = level;
-        } else {
-            throw new IllegalArgumentException("Only language levels 2 or 3 are allowed/supported");
-        }
+        getPSUtil().setLanguageLevel(level);
     }
 
     /**
@@ -258,7 +212,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer
      * @return the language level
      */
     public int getLanguageLevel() {
-        return this.languageLevel;
+        return getPSUtil().getLanguageLevel();
     }
 
     /**
@@ -268,12 +222,12 @@ public class PSRenderer extends AbstractPathOrientedRenderer
      * @param value true to enable the resource optimization
      */
     public void setOptimizeResources(boolean value) {
-        this.twoPassGeneration = value;
+        getPSUtil().setOptimizeResources(value);
     }
 
     /** @return true if the renderer does two passes to optimize PostScript resources */
     public boolean isOptimizeResources() {
-        return this.twoPassGeneration;
+        return getPSUtil().isOptimizeResources();
     }
 
     /** {@inheritDoc} */
@@ -316,12 +270,15 @@ public class PSRenderer extends AbstractPathOrientedRenderer
      * @param comment Comment to write
      */
     protected void comment(String comment) {
-        if (this.enableComments) {
+        try {
             if (comment.startsWith("%")) {
+                gen.commentln(comment);
                 writeln(comment);
             } else {
-                writeln("%" + comment);
+                gen.commentln("%" + comment);
             }
+        } catch (IOException ioe) {
+            handleIOTrouble(ioe);
         }
     }
 
@@ -931,7 +888,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer
 
         //Initial default page device dictionary settings
         this.pageDeviceDictionary = new PSPageDeviceDictionary();
-        pageDeviceDictionary.setFlushOnRetrieval(!this.dscCompliant);
+        pageDeviceDictionary.setFlushOnRetrieval(!getPSUtil().isDSCComplianceEnabled());
         pageDeviceDictionary.put("/ImagingBBox", "null");
     }
 
@@ -969,7 +926,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer
 
         //Setup
         gen.writeDSCComment(DSCConstants.BEGIN_SETUP);
-        writeSetupCodeList(setupCodeList, "SetupCode");
+        PSRenderingUtil.writeSetupCodeList(gen, setupCodeList, "SetupCode");
         if (!isOptimizeResources()) {
             this.fontResources = PSFontUtils.writeFontDict(gen, fontInfo);
         } else {
@@ -980,16 +937,6 @@ public class PSRenderer extends AbstractPathOrientedRenderer
 
     /** {@inheritDoc} */
     public void stopRenderer() throws IOException {
-        //Notify resource usage for font which are not supplied
-        /* done in useFont now
-        Map fonts = fontInfo.getUsedFonts();
-        Iterator e = fonts.keySet().iterator();
-        while (e.hasNext()) {
-            String key = (String)e.next();
-            PSResource res = (PSResource)this.fontResources.get(key);
-            gen.notifyResourceUsage(res);
-        }*/
-
         //Write trailer
         gen.writeDSCComment(DSCConstants.TRAILER);
         if (footerComments != null) {
@@ -1102,34 +1049,6 @@ public class PSRenderer extends AbstractPathOrientedRenderer
         super.processOffDocumentItem(oDI);
     }
 
-    /**
-     * Formats and writes a List of PSSetupCode instances to the output stream.
-     * @param setupCodeList a List of PSSetupCode instances
-     * @param type the type of code section
-     */
-    private void writeSetupCodeList(List setupCodeList, String type) throws IOException {
-        if (setupCodeList != null) {
-            Iterator i = setupCodeList.iterator();
-            while (i.hasNext()) {
-                PSSetupCode setupCode = (PSSetupCode)i.next();
-                gen.commentln("%FOPBegin" + type + ": ("
-                        + (setupCode.getName() != null ? setupCode.getName() : "")
-                        + ")");
-                LineNumberReader reader = new LineNumberReader(
-                        new java.io.StringReader(setupCode.getContent()));
-                String line;
-                while ((line = reader.readLine()) != null) {
-                    line = line.trim();
-                    if (line.length() > 0) {
-                        gen.writeln(line.trim());
-                    }
-                }
-                gen.commentln("%FOPEnd" + type);
-                i.remove();
-            }
-        }
-    }
-
     /** {@inheritDoc} */
     public void renderPage(PageViewport page)
             throws IOException, FOPException {
@@ -1151,7 +1070,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer
         double pageHeight = Math.round(page.getViewArea().getHeight()) / 1000f;
         boolean rotate = false;
         List pageSizes = new java.util.ArrayList();
-        if (this.autoRotateLandscape && (pageHeight < pageWidth)) {
+        if (getPSUtil().isAutoRotateLandscape() && (pageHeight < pageWidth)) {
             rotate = true;
             pageSizes.add(new Long(Math.round(pageHeight)));
             pageSizes.add(new Long(Math.round(pageWidth)));
@@ -1189,7 +1108,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer
 
         try {
             if (setupCodeList != null) {
-                writeEnclosedExtensionAttachments(setupCodeList);
+                PSRenderingUtil.writeEnclosedExtensionAttachments(gen, setupCodeList);
                 setupCodeList.clear();
             }
         } catch (IOException e) {
@@ -1214,7 +1133,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer
             gen.writeDSCComment(DSCConstants.PAGE_HIRES_BBOX, new Object[] {
                     zero, zero, new Double(pageWidth),
                     new Double(pageHeight) });
-            if (autoRotateLandscape) {
+            if (getPSUtil().isAutoRotateLandscape()) {
                 gen.writeDSCComment(DSCConstants.PAGE_ORIENTATION,
                         "Portrait");
             }
@@ -1245,12 +1164,12 @@ public class PSRenderer extends AbstractPathOrientedRenderer
         // Write any unwritten changes to page device dictionary
         if (!pageDeviceDictionary.isEmpty()) {
             String content = pageDeviceDictionary.getContent();
-            if (safeSetPageDevice) {
+            if (getPSUtil().isSafeSetPageDevice()) {
                 content += " SSPD";
             } else {
                 content += " setpagedevice";
             }
-            writeEnclosedExtensionAttachment(new PSSetPageDevice(content));
+            PSRenderingUtil.writeEnclosedExtensionAttachment(gen, new PSSetPageDevice(content));
         }
 
         if (rotate) {
@@ -1632,55 +1551,6 @@ public class PSRenderer extends AbstractPathOrientedRenderer
         return MIME_TYPE;
     }
 
-    /**
-     * Formats and writes a PSExtensionAttachment to the output stream.
-     *
-     * @param attachment an PSExtensionAttachment instance
-     */
-    private void writeEnclosedExtensionAttachment(PSExtensionAttachment attachment)
-            throws IOException {
-        String info = "";
-        if (attachment instanceof PSSetupCode) {
-            PSSetupCode setupCodeAttach = (PSSetupCode)attachment;
-            String name = setupCodeAttach.getName();
-            if (name != null) {
-                info += ": (" + name + ")";
-            }
-        }
-        String type = attachment.getType();
-        gen.commentln("%FOPBegin" + type + info);
-        LineNumberReader reader = new LineNumberReader(
-                new java.io.StringReader(attachment.getContent()));
-        String line;
-        while ((line = reader.readLine()) != null) {
-            line = line.trim();
-            if (line.length() > 0) {
-                gen.writeln(line);
-            }
-        }
-        gen.commentln("%FOPEnd" + type);
-    }
-
-    /**
-     * Formats and writes a Collection of PSExtensionAttachment instances to
-     * the output stream.
-     *
-     * @param attachmentCollection
-     *            a Collection of PSExtensionAttachment instances
-     */
-    private void writeEnclosedExtensionAttachments(Collection attachmentCollection)
-            throws IOException {
-        Iterator iter = attachmentCollection.iterator();
-        while (iter.hasNext()) {
-            PSExtensionAttachment attachment = (PSExtensionAttachment)iter
-                    .next();
-            if (attachment != null) {
-                writeEnclosedExtensionAttachment(attachment);
-            }
-            iter.remove();
-        }
-    }
-
     /**
      * Sets whether or not the safe set page device macro should be used
      * (as opposed to directly invoking setpagedevice) when setting the
@@ -1694,7 +1564,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer
      * device macro call (default is false).
      */
     public void setSafeSetPageDevice(boolean safeSetPageDevice) {
-        this.safeSetPageDevice = safeSetPageDevice;
+        getPSUtil().setSafeSetPageDevice(safeSetPageDevice);
     }
 
     /**
@@ -1711,7 +1581,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer
      * @param dscCompliant boolean value (default is true)
      */
     public void setDSCCompliant(boolean dscCompliant) {
-        this.dscCompliant = dscCompliant;
+        getPSUtil().setDSCComplianceEnabled(dscCompliant);
     }
 
 }
index 867888ea542df4a3e4553ebb7342f1c62c39da82..7ea4fe54772dbe01d9b9b60fc33a171a92865add 100644 (file)
 
 package org.apache.fop.render.ps;
 
+import java.util.List;
+
 import org.apache.avalon.framework.configuration.Configuration;
+
+import org.apache.xmlgraphics.ps.PSGenerator;
+
 import org.apache.fop.apps.FOPException;
 import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.fonts.CustomFontCollection;
+import org.apache.fop.fonts.FontCollection;
+import org.apache.fop.fonts.FontEventAdapter;
+import org.apache.fop.fonts.FontEventListener;
+import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.fonts.FontManager;
+import org.apache.fop.fonts.FontResolver;
+import org.apache.fop.fonts.base14.Base14FontCollection;
+import org.apache.fop.render.DefaultFontResolver;
 import org.apache.fop.render.PrintRendererConfigurator;
 import org.apache.fop.render.Renderer;
-import org.apache.xmlgraphics.ps.PSGenerator;
+import org.apache.fop.render.intermediate.IFDocumentHandler;
+import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator;
 
 /**
  * Postscript renderer config
  */
-public class PSRendererConfigurator extends PrintRendererConfigurator {
+public class PSRendererConfigurator extends PrintRendererConfigurator
+            implements IFDocumentHandlerConfigurator {
 
     /**
      * Default constructor
@@ -50,23 +66,58 @@ public class PSRendererConfigurator extends PrintRendererConfigurator {
             super.configure(renderer);
 
             PSRenderer psRenderer = (PSRenderer)renderer;
+            configure(psRenderer.getPSUtil(), cfg);
+        }
+    }
 
-            psRenderer.setAutoRotateLandscape(
-                cfg.getChild("auto-rotate-landscape").getValueAsBoolean(false));
-            Configuration child;
-            child = cfg.getChild("language-level");
-            if (child != null) {
-                psRenderer.setLanguageLevel(child.getValueAsInteger(
-                        PSGenerator.DEFAULT_LANGUAGE_LEVEL));
-            }
-            child = cfg.getChild("optimize-resources");
-            if (child != null) {
-                psRenderer.setOptimizeResources(child.getValueAsBoolean(false));
-            }
-            psRenderer.setSafeSetPageDevice(
-                cfg.getChild("safe-set-page-device").getValueAsBoolean(false));
-            psRenderer.setDSCCompliant(
-                cfg.getChild("dsc-compliant").getValueAsBoolean(true));
+    private void configure(PSRenderingUtil psUtil, Configuration cfg) {
+        psUtil.setAutoRotateLandscape(
+            cfg.getChild("auto-rotate-landscape").getValueAsBoolean(false));
+        Configuration child;
+        child = cfg.getChild("language-level");
+        if (child != null) {
+            psUtil.setLanguageLevel(child.getValueAsInteger(
+                    PSGenerator.DEFAULT_LANGUAGE_LEVEL));
+        }
+        child = cfg.getChild("optimize-resources");
+        if (child != null) {
+            psUtil.setOptimizeResources(child.getValueAsBoolean(false));
         }
+        psUtil.setSafeSetPageDevice(
+            cfg.getChild("safe-set-page-device").getValueAsBoolean(false));
+        psUtil.setDSCComplianceEnabled(
+            cfg.getChild("dsc-compliant").getValueAsBoolean(true));
+    }
+
+    /** {@inheritDoc} */
+    public void configure(IFDocumentHandler documentHandler) throws FOPException {
+        Configuration cfg = super.getRendererConfig(documentHandler.getMimeType());
+        if (cfg != null) {
+            PSDocumentHandler psDocumentHandler = (PSDocumentHandler)documentHandler;
+            configure(psDocumentHandler.getPSUtil(), cfg);
+        }
+
+    }
+
+    /** {@inheritDoc} */
+    public void setupFontInfo(IFDocumentHandler documentHandler, FontInfo fontInfo)
+            throws FOPException {
+        FontManager fontManager = userAgent.getFactory().getFontManager();
+        List fontCollections = new java.util.ArrayList();
+        fontCollections.add(new Base14FontCollection(fontManager.isBase14KerningEnabled()));
+
+        Configuration cfg = super.getRendererConfig(documentHandler.getMimeType());
+        if (cfg != null) {
+            FontResolver fontResolver = new DefaultFontResolver(userAgent);
+            FontEventListener listener = new FontEventAdapter(
+                    userAgent.getEventBroadcaster());
+            List fontList = buildFontList(cfg, fontResolver, listener);
+            fontCollections.add(new CustomFontCollection(fontResolver, fontList));
+        }
+
+        fontManager.setup(fontInfo,
+                (FontCollection[])fontCollections.toArray(
+                        new FontCollection[fontCollections.size()]));
+        documentHandler.setFontInfo(fontInfo);
     }
 }
diff --git a/src/java/org/apache/fop/render/ps/PSRenderingContext.java b/src/java/org/apache/fop/render/ps/PSRenderingContext.java
new file mode 100644 (file)
index 0000000..63f00ea
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps;
+
+import org.apache.xmlgraphics.util.MimeConstants;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.render.AbstractRenderingContext;
+
+/**
+ * Rendering context for PostScript production.
+ */
+public class PSRenderingContext extends AbstractRenderingContext {
+
+    private FontInfo fontInfo;
+
+    /**
+     * Main constructor.
+     * @param userAgent the user agent
+     * @param fontInfo the font list
+     */
+    public PSRenderingContext(FOUserAgent userAgent,
+            FontInfo fontInfo) {
+        super(userAgent);
+        this.fontInfo = fontInfo;
+    }
+
+    /** {@inheritDoc} */
+    public String getMimeType() {
+        return MimeConstants.MIME_POSTSCRIPT;
+    }
+
+    /**
+     * Returns the font list.
+     * @return the font list
+     */
+    public FontInfo getFontInfo() {
+        return this.fontInfo;
+    }
+
+}
diff --git a/src/java/org/apache/fop/render/ps/PSRenderingUtil.java b/src/java/org/apache/fop/render/ps/PSRenderingUtil.java
new file mode 100644 (file)
index 0000000..a7fe569
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps;
+
+import java.io.IOException;
+import java.io.LineNumberReader;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.xmlgraphics.ps.PSGenerator;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.render.ps.extensions.PSExtensionAttachment;
+import org.apache.fop.render.ps.extensions.PSSetupCode;
+
+/**
+ * Utility class which enables all sorts of features that are not directly connected to the
+ * normal rendering process.
+ */
+public class PSRenderingUtil implements PSConfigurationConstants {
+
+    private FOUserAgent userAgent;
+
+    /** Whether or not the safe set page device macro will be used or not */
+    private boolean safeSetPageDevice = false;
+
+    /**
+     * Whether or not PostScript Document Structuring Conventions (DSC) compliant output are
+     * enforced.
+     */
+    private boolean dscCompliant = true;
+
+    private boolean autoRotateLandscape = false;
+    private int languageLevel = PSGenerator.DEFAULT_LANGUAGE_LEVEL;
+
+    /** Determines whether the PS file is generated in two passes to minimize file size */
+    private boolean optimizeResources = false;
+
+    PSRenderingUtil(FOUserAgent userAgent) {
+        this.userAgent = userAgent;
+        initialize();
+    }
+
+    private void initialize() {
+        Object obj;
+        obj = userAgent.getRendererOptions().get(AUTO_ROTATE_LANDSCAPE);
+        if (obj != null) {
+            setAutoRotateLandscape(booleanValueOf(obj));
+        }
+        obj = userAgent.getRendererOptions().get(LANGUAGE_LEVEL);
+        if (obj != null) {
+            setLanguageLevel(intValueOf(obj));
+        }
+        obj = userAgent.getRendererOptions().get(OPTIMIZE_RESOURCES);
+        if (obj != null) {
+            setOptimizeResources(booleanValueOf(obj));
+        }
+    }
+
+    private boolean booleanValueOf(Object obj) {
+        if (obj instanceof Boolean) {
+            return ((Boolean)obj).booleanValue();
+        } else if (obj instanceof String) {
+            return Boolean.valueOf((String)obj).booleanValue();
+        } else {
+            throw new IllegalArgumentException("Boolean or \"true\" or \"false\" expected.");
+        }
+    }
+
+    private int intValueOf(Object obj) {
+        if (obj instanceof Integer) {
+            return ((Integer)obj).intValue();
+        } else if (obj instanceof String) {
+            return Integer.parseInt((String)obj);
+        } else {
+            throw new IllegalArgumentException("Integer or String with a number expected.");
+        }
+    }
+
+    /**
+     * Formats and writes a List of PSSetupCode instances to the output stream.
+     * @param gen the PS generator
+     * @param setupCodeList a List of PSSetupCode instances
+     * @param type the type of code section
+     * @throws IOException if an I/O error occurs.
+     */
+    public static void writeSetupCodeList(PSGenerator gen, List setupCodeList, String type)
+            throws IOException {
+        if (setupCodeList != null) {
+            Iterator i = setupCodeList.iterator();
+            while (i.hasNext()) {
+                PSSetupCode setupCode = (PSSetupCode)i.next();
+                gen.commentln("%FOPBegin" + type + ": ("
+                        + (setupCode.getName() != null ? setupCode.getName() : "")
+                        + ")");
+                LineNumberReader reader = new LineNumberReader(
+                        new java.io.StringReader(setupCode.getContent()));
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    line = line.trim();
+                    if (line.length() > 0) {
+                        gen.writeln(line.trim());
+                    }
+                }
+                gen.commentln("%FOPEnd" + type);
+                i.remove();
+            }
+        }
+    }
+
+    /**
+     * Formats and writes a Collection of PSExtensionAttachment instances to
+     * the output stream. The instances are removed from the collection when they
+     * have been written.
+     *
+     * @param gen the PS generator
+     * @param attachmentCollection
+     *            a Collection of PSExtensionAttachment instances
+     * @throws IOException if an I/O error occurs.
+     */
+    public static void writeEnclosedExtensionAttachments(PSGenerator gen,
+            Collection attachmentCollection) throws IOException {
+        Iterator iter = attachmentCollection.iterator();
+        while (iter.hasNext()) {
+            PSExtensionAttachment attachment = (PSExtensionAttachment)iter.next();
+            if (attachment != null) {
+                writeEnclosedExtensionAttachment(gen, attachment);
+            }
+            iter.remove();
+        }
+    }
+
+    /**
+     * Formats and writes a PSExtensionAttachment to the output stream.
+     *
+     * @param gen the PS generator
+     * @param attachment an PSExtensionAttachment instance
+     * @throws IOException if an I/O error occurs.
+     */
+    public static void writeEnclosedExtensionAttachment(PSGenerator gen,
+                PSExtensionAttachment attachment) throws IOException {
+        String info = "";
+        if (attachment instanceof PSSetupCode) {
+            PSSetupCode setupCodeAttach = (PSSetupCode)attachment;
+            String name = setupCodeAttach.getName();
+            if (name != null) {
+                info += ": (" + name + ")";
+            }
+        }
+        String type = attachment.getType();
+        gen.commentln("%FOPBegin" + type + info);
+        LineNumberReader reader = new LineNumberReader(
+                new java.io.StringReader(attachment.getContent()));
+        String line;
+        while ((line = reader.readLine()) != null) {
+            line = line.trim();
+            if (line.length() > 0) {
+                gen.writeln(line);
+            }
+        }
+        gen.commentln("%FOPEnd" + type);
+    }
+
+    /**
+     * Sets whether or not PostScript Document Structuring Conventions (DSC) compliance are
+     * enforced.
+     * <p>
+     * It can cause problems (unwanted PostScript subsystem initgraphics/erasepage calls)
+     * on some printers when the pagedevice is set.  If this causes problems on a
+     * particular implementation then use this setting with a 'false' value to try and
+     * minimize the number of setpagedevice calls in the PostScript document output.
+     * <p>
+     * Set this value to false if you experience unwanted blank pages in your
+     * PostScript output.
+     * @param value boolean value (default is true)
+     */
+    public void setSafeSetPageDevice(boolean value) {
+        this.safeSetPageDevice = value;
+    }
+
+    /**
+     * Indicates whether the "safe setpagedevice" mode is active.
+     * See {@code #setSafeSetPageDevice(boolean)} for more information.
+     * @return true if active
+     */
+    public boolean isSafeSetPageDevice() {
+        return this.safeSetPageDevice;
+    }
+
+    /**
+     * Sets whether or not the safe set page device macro should be used
+     * (as opposed to directly invoking setpagedevice) when setting the
+     * PostScript page device.
+     * <p>
+     * This option is a useful option when you want to guard against the possibility
+     * of invalid/unsupported PostScript key/values being placed in the page device.
+     * <p>
+     * @param value setting to false and the renderer will make a
+     *          standard "setpagedevice" call, setting to true will make a safe set page
+     *          device macro call (default is false).
+     */
+    public void setDSCComplianceEnabled(boolean value) {
+        this.dscCompliant = value;
+    }
+
+    public boolean isDSCComplianceEnabled() {
+        return this.dscCompliant;
+    }
+
+    /**
+     * Controls whether landscape pages should be rotated.
+     * @param value true to enable the rotation
+     */
+    public void setAutoRotateLandscape(boolean value) {
+        this.autoRotateLandscape = value;
+    }
+
+    /**
+     * Indicates whether landscape pages are rotated.
+     * @return true if landscape pages are to be rotated
+     */
+    public boolean isAutoRotateLandscape() {
+        return autoRotateLandscape;
+    }
+
+    /**
+     * Sets the PostScript language level.
+     * @param level the PostScript language level (Only 2 and 3 are currently supported)
+     */
+    public void setLanguageLevel(int level) {
+        if (level == 2 || level == 3) {
+            this.languageLevel = level;
+        } else {
+            throw new IllegalArgumentException("Only language levels 2 or 3 are allowed/supported");
+        }
+    }
+
+    /**
+     * Indicates the selected PostScript language level.
+     * @return the PostScript language level
+     */
+    public int getLanguageLevel() {
+        return languageLevel;
+    }
+
+    /**
+     * Controls whether PostScript resources are optimized in a second pass over the document.
+     * Enable this to obtain smaller PostScript files.
+     * @param value true to enable resource optimization
+     */
+    public void setOptimizeResources(boolean value) {
+        this.optimizeResources = value;
+    }
+
+    /**
+     * Indicates whether PostScript resources are optimized in a second pass over the document.
+     * @return true if resource optimization is enabled
+     */
+    public boolean isOptimizeResources() {
+        return optimizeResources;
+    }
+
+
+}
index bd3dbc4e17df7080222b1b6d009c1c5da88b834c..4012b7e97468b8babef55a9483c769a7d6761d05 100644 (file)
@@ -149,7 +149,8 @@ public class SVGDocumentHandler extends AbstractSVGDocumentHandler {
     }
 
     /** {@inheritDoc} */
-    public void startPage(int index, String name, Dimension size) throws IFException {
+    public void startPage(int index, String name, String pageMasterName, Dimension size)
+                throws IFException {
         OutputStream out;
         try {
             if (index == 0) {
index ee69f95aedd8a5ee87fea58b8e56e5784c21fb4d..25a7f3e7d12d387b11a50a10cead04dc998a958a 100644 (file)
@@ -111,7 +111,8 @@ public class SVGPrintDocumentHandler extends AbstractSVGDocumentHandler {
     }
 
     /** {@inheritDoc} */
-    public void startPage(int index, String name, Dimension size) throws IFException {
+    public void startPage(int index, String name, String pageMasterName, Dimension size)
+                throws IFException {
         try {
             AttributesImpl atts = new AttributesImpl();
             /*