]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Port of the PDF TextPainter to PostScript.
authorJeremias Maerki <jeremias@apache.org>
Tue, 11 Mar 2003 08:42:24 +0000 (08:42 +0000)
committerJeremias Maerki <jeremias@apache.org>
Tue, 11 Mar 2003 08:42:24 +0000 (08:42 +0000)
Support for SEG_QUADTO (curves).
Some support for viewport traits (background and borders).
Submitted by: Zhong Yi <yidaomao@yahoo.com>

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

src/org/apache/fop/render/ps/PSGraphics2D.java
src/org/apache/fop/render/ps/PSProcSets.java
src/org/apache/fop/render/ps/PSRenderer.java
src/org/apache/fop/render/ps/PSTextPainter.java [new file with mode: 0644]
src/org/apache/fop/render/ps/PSXMLHandler.java

index 294060e20914e9cb7507262feeaa6baa161e2ea4..5a27c0fb0a0ca173c7f4888a42f6471ea2dd34ba 100644 (file)
@@ -114,7 +114,10 @@ public class PSGraphics2D extends AbstractGraphics2D {
 
     /** Currently valid FontState */
     protected FontState fontState;
-
+    
+    /** Overriding FontState */
+    protected FontState overrideFontState = null;
+    
     /**
      * the current (internal) font name
      */
@@ -501,8 +504,9 @@ public class PSGraphics2D extends AbstractGraphics2D {
             Shape imclip = getClip();
             writeClip(imclip);
             Color c = getColor();
-            gen.writeln(c.getRed() + " " + c.getGreen() + " " + c.getBlue()
-                             + " setrgbcolor");
+            gen.writeln(gen.formatDouble(c.getRed() / 255.0) + " " 
+                      + gen.formatDouble(c.getGreen() / 255.0) + " " 
+                      + gen.formatDouble(c.getBlue() / 255.0) + " setrgbcolor");
             
             applyPaint(getPaint(), false);
             applyStroke(getStroke());
@@ -533,10 +537,10 @@ public class PSGraphics2D extends AbstractGraphics2D {
                               + " M");
                     break;
                 case PathIterator.SEG_QUADTO:
-                    // psRenderer.write((1000 * PDFNumber.doubleOut(vals[0])) +
-                    // " " + (1000 * PDFNumber.doubleOut(vals[1])) + " " +
-                    // (1000 * PDFNumber.doubleOut(vals[2])) + " " +
-                    // (1000 * PDFNumber.doubleOut(vals[3])) + " y\n");
+                    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");
@@ -585,10 +589,10 @@ public class PSGraphics2D extends AbstractGraphics2D {
                               + " M");
                     break;
                 case PathIterator.SEG_QUADTO:
-                    // psRenderer.write(1000 * PDFNumber.doubleOut(vals[0]) +
-                    // " " + 1000 * PDFNumber.doubleOut(vals[1]) + " " +
-                    // 1000 * PDFNumber.doubleOut(vals[2]) + " " +
-                    // 1000 * PDFNumber.doubleOut(vals[3]) + " y\n");
+                    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");
@@ -803,32 +807,73 @@ public class PSGraphics2D extends AbstractGraphics2D {
      * @see #setClip
      */
     public void drawString(String s, float x, float y) {
-        try {
-            System.out.println("drawString(String)");
-            gen.writeln("BT");
-            Shape imclip = getClip();
-            writeClip(imclip);
-            Color c = getColor();
-            gen.writeln(c.getRed() + " " + c.getGreen() + " " + c.getBlue()
-                             + " setrgbcolor");
-            
-            AffineTransform trans = getTransform();
-            trans.translate(x, y);
-            double[] vals = new double[6];
-            trans.getMatrix(vals);
-            
-            gen.writeln(gen.formatDouble(vals[0]) + " "
-                      + gen.formatDouble(vals[1]) + " "
-                      + gen.formatDouble(vals[2]) + " "
-                      + gen.formatDouble(vals[3]) + " "
-                      + gen.formatDouble(vals[4]) + " "
-                      + gen.formatDouble(vals[5]) + " "
-                      + gen.formatDouble(vals[6]) + " Tm [" + s + "]");
+      try {
+        if (overrideFontState == null) {
+            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";
             
-            gen.writeln("ET");
-        } catch (IOException ioe) {
-            handleIOException(ioe);
+            //try {
+                //fontState = new FontState(n, fontState.getFontMetrics(),siz);
+            //} catch (org.apache.fop.apps.FOPException fope) {
+                //fope.printStackTrace();
+            //}
+        } else {
+            fontState = overrideFontState;
+            overrideFontState = null;
+        }
+        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));
+            } else {
+                String escape = "\\()[]{}";
+                if (escape.indexOf(mch) >= 0) {
+                    sb.append("\\");
+                }
+                sb = sb.append(mch);
+            }
+        }
+
+        String psString = null;
+        psString = " (" + sb.toString() + ") " + " t ";
+
+        gen.writeln(" 1.0 -1.0 scale");
+        gen.writeln(psString);        
+        gen.writeln(" 1.0 -1.0 scale");
+      } catch (IOException ioe) {
+          handleIOException(ioe);
+      }
     }
 
     /**
@@ -917,8 +962,9 @@ public class PSGraphics2D extends AbstractGraphics2D {
             Shape imclip = getClip();
             writeClip(imclip);
             Color c = getColor();
-            gen.writeln(c.getRed() + " " + c.getGreen() + " " + c.getBlue()
-                             + " setrgbcolor");
+            gen.writeln(gen.formatDouble(c.getRed() / 255.0) + " "
+                      + gen.formatDouble(c.getGreen() / 255.0) + " "
+                      + gen.formatDouble(c.getBlue() / 255.0) + " setrgbcolor");
             
             applyPaint(getPaint(), true);
             
@@ -948,10 +994,10 @@ public class PSGraphics2D extends AbstractGraphics2D {
                               + " M");
                     break;
                 case PathIterator.SEG_QUADTO:
-                    // psRenderer.write(1000 * PDFNumber.doubleOut(vals[0]) +
-                    // " " + 1000 * PDFNumber.doubleOut(vals[1]) + " " +
-                    // 1000 * PDFNumber.doubleOut(vals[2]) + " " +
-                    // 1000 * PDFNumber.doubleOut(vals[3]) + " y\n");
+                    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");
@@ -1021,6 +1067,14 @@ public class PSGraphics2D extends AbstractGraphics2D {
         fmg = bi.createGraphics();
     }
 
+    /**
+     * Sets the overrideing font state.
+     * @param infont FontState to set
+     */
+    public void setOverrideFontState(FontState infont) {
+        overrideFontState = infont;
+    }
+    
     /**
      * Gets the font metrics for the specified font.
      * @return    the font metrics for the specified font.
index 3068ec9f2ebdf829628b546f251be51c0c54a9b2..7508e7b9b7e7634d5530a7fc89bceb96c80c9ec4 100644 (file)
@@ -141,7 +141,20 @@ public final class PSProcSets {
         gen.writeln("  Tt setlinewidth stroke");
         gen.writeln("  grestore");
         gen.writeln("} bd");
-
+        
+        gen.writeln("/QUADTO {");
+        gen.writeln("/Y22 exch store");
+        gen.writeln("/X22 exch store");
+        gen.writeln("/Y21 exch store");
+        gen.writeln("/X21 exch store");
+        gen.writeln("currentpoint");
+        gen.writeln("/Y21 load 2 mul add 3 div exch");
+        gen.writeln("/X21 load 2 mul add 3 div exch");
+        gen.writeln("/X21 load 2 mul /X22 load add 3 div");
+        gen.writeln("/Y21 load 2 mul /Y22 load add 3 div");
+        gen.writeln("/X22 load /Y22 load curveto");
+        gen.writeln("} bd");
+        
         gen.writeln("%%EndResource");
     }
 
index 6a5ce3f6f86add30fcebd75baec8e7ccbbfcb101..5b950745c1be49d3d098f7003ae51c6ac4a473c1 100644 (file)
@@ -59,6 +59,9 @@ import java.util.List;
 import java.util.Map;
 
 // FOP
+import org.apache.fop.fo.properties.BackgroundRepeat;
+import org.apache.fop.area.Area;
+import org.apache.fop.area.RegionViewport;
 import org.apache.fop.apps.FOPException;
 import org.apache.fop.area.Block;
 import org.apache.fop.area.BlockViewport;
@@ -73,9 +76,12 @@ import org.apache.fop.fonts.Font;
 import org.apache.fop.layout.FontInfo;
 import org.apache.fop.render.AbstractRenderer;
 import org.apache.fop.render.RendererContext;
-import org.w3c.dom.Document;
 
+import org.apache.fop.image.FopImage;
+import org.apache.fop.image.ImageFactory;
+import org.apache.fop.traits.BorderProps;
 
+import org.w3c.dom.Document;
 /**
  * Renderer that renders to PostScript.
  * <br>
@@ -416,14 +422,15 @@ public class PSRenderer extends AbstractRenderer {
                 {page.getPageNumber(),
                  new Integer(this.currentPageNumber)});
         final Integer zero = new Integer(0);
-        final Long pagewidth = new Long(Math.round(page.getViewArea().getWidth() / 1000f));
-        final Long pageheight = new Long(Math.round(page.getViewArea().getHeight() / 1000f));
+        final Long pagewidth = new Long(Math.round(page.getViewArea().getWidth()));
+        final Long pageheight = new Long(Math.round(page.getViewArea().getHeight()));
         gen.writeDSCComment(DSCConstants.PAGE_BBOX, new Object[]
                 {zero, zero, pagewidth, pageheight});
         gen.writeDSCComment(DSCConstants.BEGIN_PAGE_SETUP);         
         gen.writeln("FOPFonts begin");
-        concatMatrix(1, 0, 0, -1, 0, pageheight.doubleValue());
         gen.writeln("0.001 0.001 scale");
+        concatMatrix(1, 0, 0, -1, 0, pageheight.doubleValue());
+        
         gen.writeDSCComment(DSCConstants.END_PAGE_SETUP);         
         
         //Process page
@@ -629,7 +636,7 @@ public class PSRenderer extends AbstractRenderer {
         
         saveGraphicsState();
         // multiply with current CTM
-        //currentStream.add(CTMHelper.toPDFString(ctm) + " cm\n");
+        //writeln(CTMHelper.toPDFString(ctm) + " cm\n");
         final double matrix[] = ctm.toArray();
         concatMatrix(matrix);
         
@@ -646,7 +653,190 @@ public class PSRenderer extends AbstractRenderer {
         //currentState.pop();
     }
 
+    /**
+     * Handle the viewport traits.
+     * This is used to draw the traits for a viewport.
+     *
+     * @param region the viewport region to handle
+     */
+    protected void handleViewportTraits(RegionViewport region) {
+        currentFontName = "";
+        float startx = 0;
+        float starty = 0;
+        Rectangle2D viewArea = region.getViewArea();
+        float width = (float)(viewArea.getWidth());
+        float height = (float)(viewArea.getHeight());
+        /*
+        Trait.Background back;
+        back = (Trait.Background)region.getTrait(Trait.BACKGROUND);
+        */
+        drawBackAndBorders(region, startx, starty, width, height);
+    }
+
+    /**
+     * Handle block traits.
+     * The block could be any sort of block with any positioning
+     * so this should render the traits such as border and background
+     * in its position.
+     *
+     * @param block the block to render the traits
+     */
+    protected void handleBlockTraits(Block block) {
+        float startx = currentIPPosition;
+        float starty = currentBPPosition;
+        drawBackAndBorders(block, startx, starty,
+                           block.getWidth(), block.getHeight());
+    }
+
+    /**
+     * Draw the background and borders.
+     * This draws the background and border traits for an area given
+     * the position.
+     *
+     * @param block the area to get the traits from
+     * @param startx the start x position
+     * @param starty the start y position
+     * @param width the width of the area
+     * @param height the height of the area
+     */
+    protected void drawBackAndBorders(Area block,
+                                    float startx, float starty, 
+                                    float width, float height) {
+        // draw background then border
+
+        boolean started = false;
+        Trait.Background back;
+        back = (Trait.Background)block.getTrait(Trait.BACKGROUND);
+        if (back != null) {
+            started = true;
+//            closeText();
+            endTextObject();
+            //saveGraphicsState();
+
+            if (back.getColor() != null) {
+                updateColor(back.getColor(), true, null);
+                writeln(startx + " " + starty + " "
+                                  + width + " " + height + " rectfill");
+            }
+            if (back.getURL() != null) {
+                ImageFactory fact = ImageFactory.getInstance();
+                FopImage fopimage = fact.getImage(back.getURL(), userAgent);
+                if (fopimage != null && fopimage.load(FopImage.DIMENSIONS, userAgent)) {
+                    if (back.getRepeat() == BackgroundRepeat.REPEAT) {
+                        // create a pattern for the image
+                    } else {
+                        // place once
+                        Rectangle2D pos;
+                        pos = new Rectangle2D.Float((startx + back.getHoriz()) * 1000,
+                                                    (starty + back.getVertical()) * 1000,
+                                                    fopimage.getWidth() * 1000,
+                                                    fopimage.getHeight() * 1000);
+                       // putImage(back.url, pos);
+                    }
+                }
+            }
+        }
+
+        BorderProps bps = (BorderProps)block.getTrait(Trait.BORDER_BEFORE);
+        if (bps != null) {
+            float endx = startx + width;
+
+            if (!started) {
+                started = true;
+//                closeText();
+                endTextObject();
+                //saveGraphicsState();
+            }
+
+            float bwidth = bps.width ;
+            updateColor(bps.color, false, null);
+            writeln(bwidth + " setlinewidth");
+
+            drawLine(startx, starty + bwidth / 2, endx, starty + bwidth / 2);
+        }
+        bps = (BorderProps)block.getTrait(Trait.BORDER_START);
+        if (bps != null) {
+            float endy = starty + height;
+
+            if (!started) {
+                started = true;
+//                closeText();
+                endTextObject();
+                //saveGraphicsState();
+            }
+
+            float bwidth = bps.width ;
+            updateColor(bps.color, false, null);
+            writeln(bwidth + " setlinewidth");
+
+            drawLine(startx + bwidth / 2, starty, startx + bwidth / 2, endy);
+        }
+        bps = (BorderProps)block.getTrait(Trait.BORDER_AFTER);
+        if (bps != null) {
+            float sy = starty + height;
+            float endx = startx + width;
+
+            if (!started) {
+                started = true;
+//                closeText();
+                endTextObject();
+                //saveGraphicsState();
+            }
+
+            float bwidth = bps.width ;
+            updateColor(bps.color, false, null);
+            writeln(bwidth + " setlinewidth");
+
+            drawLine(startx, sy - bwidth / 2, endx, sy - bwidth / 2);
+        }
+        bps = (BorderProps)block.getTrait(Trait.BORDER_END);
+        if (bps != null) {
+            float sx = startx + width;
+            float endy = starty + height;
+
+            if (!started) {
+                started = true;
+ //               closeText();
+                endTextObject();
+                //saveGraphicsState();
+            }
+
+            float bwidth = bps.width ;
+            updateColor(bps.color, false, null);
+            writeln(bwidth + " setlinewidth");
+            drawLine(sx - bwidth / 2, starty, sx - bwidth / 2, endy);
+        }
+        if (started) {
+            //restoreGraphicsState();
+            beginTextObject();
+            // font last set out of scope in text section
+            currentFontName = "";
+        }
+    }
+
+    /**
+     * Draw a line.
+     *
+     * @param startx the start x position
+     * @param starty the start y position
+     * @param endx the x end position
+     * @param endy the y end position
+     */
+    private void drawLine(float startx, float starty, float endx, float endy) {
+        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");
+    }
+
+    private void updateFont(String name, int size, StringBuffer pdf) {
+
+    }
+   
     /**
      * @see org.apache.fop.render.AbstractRenderer#renderForeignObject(ForeignObject, Rectangle2D)
      */
@@ -677,6 +867,8 @@ public class PSRenderer extends AbstractRenderer {
                             new Integer(currentBlockIPPosition + (int) pos.getX()));
         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);
diff --git a/src/org/apache/fop/render/ps/PSTextPainter.java b/src/org/apache/fop/render/ps/PSTextPainter.java
new file mode 100644 (file)
index 0000000..6f2aab6
--- /dev/null
@@ -0,0 +1,435 @@
+/*
+ * $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.Graphics2D;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.Font;
+
+import java.text.AttributedCharacterIterator;
+import java.awt.font.TextAttribute;
+import java.awt.Shape;
+import java.awt.Paint;
+import java.awt.Stroke;
+import java.awt.Color;
+import java.util.List;
+import java.util.Iterator;
+
+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.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.layout.FontState;
+import org.apache.fop.layout.FontInfo;
+
+/**
+ * Renders the attributed character iterator of a <tt>TextNode</tt>.
+ * This class draws the text directly into the PSGraphics2D so that
+ * the text is not drawn using shapes which makes the PS files larger.
+ * If the text is simple enough to draw then it sets the font and calls
+ * drawString. If the text is complex or the cannot be translated
+ * into a simple drawString the StrokingTextPainter is used instead.
+ *
+ * @todo handle underline, overline and strikethrough
+ * @todo use drawString(AttributedCharacterIterator iterator...) for some
+ *
+ * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a>
+ * @version $Id: PSTextPainter.java,v 1.15 2003/01/08 14:03:55 jeremias Exp $
+ */
+public class PSTextPainter implements TextPainter {
+    private FontInfo fontInfo;
+
+    /**
+     * 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();
+
+    /**
+     * Create a new PS text painter with the given font information.
+     * @param fi the fint info
+     */
+    public PSTextPainter(FontInfo fi) {
+        fontInfo = fi;
+    }
+
+    /**
+     * Paints the specified attributed character iterator using the
+     * specified Graphics2D and context and font context.
+     * @param node the TextNode to paint
+     * @param g2d the Graphics2D to use
+     */
+    public void paint(TextNode node, Graphics2D g2d) {
+        // 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;
+        }
+        char ch = aci.first();
+        if (ch == AttributedCharacterIterator.DONE) {
+            return;
+        }
+        TextNode.Anchor anchor;
+        anchor = (TextNode.Anchor) aci.getAttribute(
+                      GVTAttributedCharacterIterator.TextAttribute.ANCHOR_TYPE);
+
+        List gvtFonts;
+        gvtFonts = (List) aci.getAttribute(
+                      GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES);
+        Paint forg = (Paint) aci.getAttribute(TextAttribute.FOREGROUND);
+        Paint strokePaint;
+        strokePaint = (Paint) aci.getAttribute(
+                     GVTAttributedCharacterIterator.TextAttribute.STROKE_PAINT);
+        Float size = (Float) aci.getAttribute(TextAttribute.SIZE);
+        if (size == null) {
+            return;
+        }
+        Stroke stroke = (Stroke) aci.getAttribute(
+                          GVTAttributedCharacterIterator.TextAttribute.STROKE);
+        /*
+        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);
+
+        boolean useStrokePainter = false;
+
+        if (forg instanceof Color) {
+            Color col = (Color) forg;
+            if (col.getAlpha() != 255) {
+                useStrokePainter = true;
+            }
+            g2d.setColor(col);
+        }
+        g2d.setPaint(forg);
+        g2d.setStroke(stroke);
+
+        if (strokePaint != null) {
+            // need to draw using AttributedCharacterIterator
+            useStrokePainter = true;
+        }
+
+        if (hasUnsupportedAttributes(aci)) {
+            useStrokePainter = true;
+        }
+
+        // text contains unsupported information
+        if (useStrokePainter) {
+            PROXY_PAINTER.paint(node, g2d);
+            return;
+        }
+
+        String style = ((posture != null) && (posture.floatValue() > 0.0))
+                       ? "italic" : "normal";
+        int weight = ((taWeight != null)
+                       &&  (taWeight.floatValue() > 1.0)) ? FontInfo.BOLD
+                       : FontInfo.NORMAL;
+
+        FontState fontState = null;
+        FontInfo 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 FontState(fname, metrics, fsize);
+                    found = true;
+                    break;
+                }
+            }
+        }
+        if (!found) {
+            String fname =
+              fontInfo.fontLookup("any", style, FontInfo.NORMAL);
+            FontMetrics metrics = fontInfo.getMetricsFor(fname);
+            int fsize = (int)(size.floatValue() * 1000);
+            fontState = new FontState(fname, metrics, fsize);
+        } else {
+            if (g2d instanceof PSGraphics2D) {
+                ((PSGraphics2D) g2d).setOverrideFontState(fontState);
+            }
+        }
+        int fStyle = Font.PLAIN;
+        if (weight == FontInfo.BOLD) {
+            if (style.equals("italic")) {
+                fStyle = Font.BOLD | Font.ITALIC;
+            } else {
+                fStyle = Font.BOLD;
+            }
+        } else {
+            if (style.equals("italic")) {
+                fStyle = Font.ITALIC;
+            } else {
+                fStyle = Font.PLAIN;
+            }
+        }
+        Font font = new Font(fontFamily, fStyle,
+                             (int)(fontState.getFontSize() / 1000));
+
+        g2d.setFont(font);
+
+        float advance = getStringWidth(txt, fontState);
+        float tx = 0;
+        if (anchor != null) {
+            switch (anchor.getType()) {
+                case TextNode.Anchor.ANCHOR_MIDDLE:
+                    tx = -advance / 2;
+                    break;
+                case TextNode.Anchor.ANCHOR_END:
+                    tx = -advance;
+            }
+        }
+        g2d.drawString(txt, (float)(loc.getX() + tx), (float)(loc.getY()));
+    }
+
+    private boolean hasUnsupportedAttributes(AttributedCharacterIterator aci) {
+        boolean hasunsupported = false;
+        Object letSpace = aci.getAttribute(
+                            GVTAttributedCharacterIterator.TextAttribute.LETTER_SPACING);
+        if (letSpace != null) {
+            hasunsupported = true;
+        }
+
+        Object wordSpace = aci.getAttribute(
+                             GVTAttributedCharacterIterator.TextAttribute.WORD_SPACING);
+        if (wordSpace != null) {
+            hasunsupported = true;
+        }
+
+        AttributedCharacterIterator.Attribute key;
+        key = GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE;
+        Object writeMod = aci.getAttribute(key);
+        if (!GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE_LTR.equals(
+                  writeMod)) {
+            hasunsupported = true;
+        }
+
+        Object vertOr = aci.getAttribute(
+                          GVTAttributedCharacterIterator.TextAttribute.VERTICAL_ORIENTATION);
+        if (GVTAttributedCharacterIterator.TextAttribute.ORIENTATION_ANGLE.equals(
+                  vertOr)) {
+            hasunsupported = true;
+        }
+        return hasunsupported;
+    }
+
+    private float getStringWidth(String str, FontState fontState) {
+        float wordWidth = 0;
+        float whitespaceWidth = fontState.getWidth(fontState.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));
+                if (charWidth <= 0) {
+                    charWidth = whitespaceWidth;
+                }
+            } else {
+                charWidth = whitespaceWidth;
+            }
+            wordWidth += charWidth;
+        }
+        return wordWidth / 1000f;
+    }
+
+    /**
+     * Get the outline shape of the text characters.
+     * This uses the StrokingTextPainter to get the outline
+     * shape since in theory it should be the same.
+     *
+     * @param node the text node
+     * @return the outline shape of the text characters
+     */
+    public Shape getOutline(TextNode node) {
+        return PROXY_PAINTER.getOutline(node);
+    }
+
+    /**
+     * Get the bounds.
+     * This uses the StrokingTextPainter to get the bounds
+     * since in theory it should be the same.
+     *
+     * @param node the text node
+     * @return the bounds of the text
+     */
+    public Rectangle2D getBounds2D(TextNode node) {
+        return PROXY_PAINTER.getBounds2D(node);
+    }
+
+    /**
+     * Get the geometry bounds.
+     * This uses the StrokingTextPainter to get the bounds
+     * since in theory it should be the same.
+     * @param node the text node
+     * @return the bounds of the text
+     */
+    public Rectangle2D getGeometryBounds(TextNode node) {
+        return PROXY_PAINTER.getGeometryBounds(node);
+    }
+
+    // Methods that have no purpose for PS
+
+    /**
+     * Get the mark.
+     * This does nothing since the output is pdf and not interactive.
+     * @param node the text node
+     * @param pos the position
+     * @param all select all
+     * @return null
+     */
+    public Mark getMark(TextNode node, int pos, boolean all) {
+        System.out.println("PSText getMark");
+        return null;
+    }
+
+    /**
+     * Select at.
+     * This does nothing since the output is pdf and not interactive.
+     * @param x the x position
+     * @param y the y position
+     * @param node the text node
+     * @return null
+     */
+    public Mark selectAt(double x, double y, TextNode node) {
+        System.out.println("PSText selectAt");
+        return null;
+    }
+
+    /**
+     * Select to.
+     * This does nothing since the output is pdf and not interactive.
+     * @param x the x position
+     * @param y the y position
+     * @param beginMark the start mark
+     * @return null
+     */
+    public Mark selectTo(double x, double y, Mark beginMark) {
+        System.out.println("PSText selectTo");
+        return null;
+    }
+
+    /**
+     * Selec first.
+     * This does nothing since the output is pdf and not interactive.
+     * @param node the text node
+     * @return null
+     */
+    public Mark selectFirst(TextNode node) {
+        System.out.println("PSText selectFirst");
+        return null;
+    }
+
+    /**
+     * Select last.
+     * This does nothing since the output is pdf and not interactive.
+     * @param node the text node
+     * @return null
+     */
+    public Mark selectLast(TextNode node) {
+        System.out.println("PSText selectLast");
+        return null;
+    }
+
+    /**
+     * Get selected.
+     * This does nothing since the output is pdf and not interactive.
+     * @param start the start mark
+     * @param finish the finish mark
+     * @return null
+     */
+    public int[] getSelected(Mark start, Mark finish) {
+        System.out.println("PSText getSelected");
+        return null;
+    }
+
+    /**
+     * Get the highlighted shape.
+     * This does nothing since the output is pdf and not interactive.
+     * @param beginMark the start mark
+     * @param endMark the end mark
+     * @return null
+     */
+    public Shape getHighlightShape(Mark beginMark, Mark endMark) {
+        System.out.println("PSText getHighlightShape");
+        return null;
+    }
+
+}
+
+
index 626e7839fb0137723fce12b6750fd18d5d1b2b68..d1053fb30c9a8935be2c442406bb6ff6521aca1a 100644 (file)
  */ 
 package org.apache.fop.render.ps;
 
-import org.apache.fop.render.XMLHandler;
-import org.apache.fop.render.RendererContext;
-import org.apache.fop.svg.SVGUserAgent;
-import org.apache.fop.layout.FontInfo;
+// Java
+import java.awt.geom.AffineTransform;
+import java.io.IOException;
 
+// DOM
 import org.w3c.dom.Document;
+import org.w3c.dom.svg.SVGDocument;
+import org.w3c.dom.svg.SVGSVGElement;
 
+// Batik
 import org.apache.batik.bridge.GVTBuilder;
 import org.apache.batik.bridge.BridgeContext;
 import org.apache.batik.bridge.ViewBox;
-
 import org.apache.batik.gvt.GraphicsNode;
+import org.apache.batik.gvt.TextPainter;
 
-import org.w3c.dom.svg.SVGDocument;
-import org.w3c.dom.svg.SVGSVGElement;
-
-import java.awt.geom.AffineTransform;
-import java.io.IOException;
+// FOP
+import org.apache.fop.render.XMLHandler;
+import org.apache.fop.render.RendererContext;
+import org.apache.fop.svg.SVGUserAgent;
+import org.apache.fop.layout.FontInfo;
 
 /**
  * PostScript XML handler.
@@ -300,7 +303,9 @@ public class PSXMLHandler implements XMLHandler {
             transform.translate(xOffset / 1000f, yOffset / 1000f);
             //aBridge.setCurrentTransform(transform);
             //ctx.putBridge(aBridge);
-
+          
+            TextPainter textPainter = new PSTextPainter(psInfo.getFontInfo());
+            ctx.setTextPainter(textPainter);            
             GraphicsNode root;
             try {
                 root = builder.build(ctx, doc);