]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Removed AbstractGraphics2DImagePainter and Graphics2DImagePainterGOCA (painting prepa...
authorAdrian Cumiskey <acumiskey@apache.org>
Tue, 18 Nov 2008 14:13:51 +0000 (14:13 +0000)
committerAdrian Cumiskey <acumiskey@apache.org>
Tue, 18 Nov 2008 14:13:51 +0000 (14:13 +0000)
Adjusted AFPGraphics2D to TextHandler changes in XG commons.
Factored an AbstractFOPTextPainter which is extended by AFPTextPainter.

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

14 files changed:
src/java/org/apache/fop/afp/AFPDataObjectFactory.java
src/java/org/apache/fop/afp/AFPGraphics2D.java
src/java/org/apache/fop/afp/Graphics2DImagePainterGOCA.java [deleted file]
src/java/org/apache/fop/afp/modca/PreprocessPresentationObject.java
src/java/org/apache/fop/afp/svg/AFPBridgeContext.java
src/java/org/apache/fop/afp/svg/AFPTextHandler.java
src/java/org/apache/fop/afp/svg/AFPTextPainter.java
src/java/org/apache/fop/image/loader/batik/Graphics2DImagePainterImpl.java
src/java/org/apache/fop/render/AbstractGraphics2DImagePainter.java [deleted file]
src/java/org/apache/fop/render/afp/AFPImageHandlerGraphics2D.java
src/java/org/apache/fop/render/afp/AFPRenderer.java
src/java/org/apache/fop/render/afp/AFPSVGHandler.java
src/java/org/apache/fop/svg/AbstractFOPTextPainter.java [new file with mode: 0644]
src/java/org/apache/fop/svg/FOPTextHandler.java [new file with mode: 0644]

index d2e5a7a6237bdc8a77a03fbbb32a8389acb0bca3..c333f5987913a534a35a34dce3092f39f862d3ff 100644 (file)
@@ -130,6 +130,8 @@ public class AFPDataObjectFactory {
         // paint to graphics object
         Graphics2DImagePainter painter = graphicsObjectInfo.getPainter();
         Rectangle2D area = graphicsObjectInfo.getArea();
+        g2d.scale(1, -1);
+        g2d.translate(0, -area.getHeight());
         painter.paint(g2d, area);
 
         // return painted graphics object
index 2494eba69eb6fd5cc22dda25326d8341cfbe778b..0a8161a3b19c84329ea67063b80bb78d69550f6a 100644 (file)
@@ -88,7 +88,7 @@ public class AFPGraphics2D extends AbstractGraphics2D implements NativeImageHand
     private GraphicsObject graphicsObj = null;
 
     /** Fallback text handler */
-    protected TextHandler fallbackTextHandler = new StrokingTextHandler(this);
+    protected TextHandler fallbackTextHandler = new StrokingTextHandler();
 
     /** Custom text handler */
     protected TextHandler customTextHandler = null;
@@ -367,9 +367,9 @@ public class AFPGraphics2D extends AbstractGraphics2D implements NativeImageHand
     public void drawString(String str, float x, float y) {
         try {
             if (customTextHandler != null && !textAsShapes) {
-                customTextHandler.drawString(str, x, y);
+                customTextHandler.drawString(this, str, x, y);
             } else {
-                fallbackTextHandler.drawString(str, x, y);
+                fallbackTextHandler.drawString(this, str, x, y);
             }
         } catch (IOException ioe) {
             handleIOException(ioe);
diff --git a/src/java/org/apache/fop/afp/Graphics2DImagePainterGOCA.java b/src/java/org/apache/fop/afp/Graphics2DImagePainterGOCA.java
deleted file mode 100644 (file)
index 6a75385..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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.afp;
-
-import java.awt.Dimension;
-import java.awt.Graphics2D;
-import java.awt.geom.Rectangle2D;
-
-import org.apache.batik.bridge.BridgeContext;
-import org.apache.batik.gvt.GraphicsNode;
-import org.apache.fop.image.loader.batik.Graphics2DImagePainterImpl;
-import org.apache.xmlgraphics.java2d.Graphics2DPainterPreparator;
-
-/**
- * Graphics2DImagePainter implementation for GOCA
- */
-public class Graphics2DImagePainterGOCA extends Graphics2DImagePainterImpl {
-
-    /**
-     * Main Constructor
-     *
-     * @param root the graphics node root
-     * @param ctx the bridge context
-     * @param imageSize the image size
-     */
-    public Graphics2DImagePainterGOCA(GraphicsNode root, BridgeContext ctx, Dimension imageSize) {
-        super(root, ctx, imageSize);
-    }
-
-    /** {@inheritDoc} */
-    protected Graphics2DPainterPreparator getPreparator() {
-        return new Graphics2DPainterPreparator() {
-
-            /** {@inheritdoc} */
-            public void prepare(Graphics2D g2d, Rectangle2D area) {
-                double tx = area.getX();
-                double ty = area.getHeight() + area.getY();
-                if (tx != 0 || ty != 0) {
-                    g2d.translate(tx, ty);
-                }
-
-                float iw = (float) ctx.getDocumentSize().getWidth();
-                float ih = (float) ctx.getDocumentSize().getHeight();
-                float w = (float) area.getWidth();
-                float h = (float) area.getHeight();
-                float sx = w / iw;
-                float sy = -(h / ih);
-                if (sx != 1.0 || sy != 1.0) {
-                    g2d.scale(sx, sy);
-                }
-            }
-        };
-    }
-}
\ No newline at end of file
index 279b3120105f5e2b85ca0e2ed12b8f661ef56759..72e26166218f7b4f61d3558dbf577050db969683 100644 (file)
@@ -138,8 +138,7 @@ public class PreprocessPresentationObject extends AbstractTripletStructuredObjec
         }
         os.write(data);
 
-        // Triplets
-        super.writeContent(os);
+        writeTriplets(os);
     }
 
 }
index 5434b5b9dd79df6357e034945bf5c584c33ff156..039c1ab915b6f368e3433b7d2581184f71df294c 100644 (file)
@@ -81,7 +81,7 @@ public class AFPBridgeContext extends AbstractFOPBridgeContext {
         super.registerSVGBridges();
 
         if (fontInfo != null) {
-            AFPTextHandler textHandler = new AFPTextHandler(g2d);
+            AFPTextHandler textHandler = new AFPTextHandler(fontInfo);
             g2d.setCustomTextHandler(textHandler);
 
             TextPainter textPainter = new AFPTextPainter(textHandler);
index d78e5c16e6402ebae49d13faa8a74a85209ba391..f44fde2696fd8ad8bc1d207734e6fc93644d0c09 100644 (file)
@@ -20,6 +20,7 @@
 package org.apache.fop.afp.svg;
 
 import java.awt.Color;
+import java.awt.Graphics2D;
 import java.io.IOException;
 
 import org.apache.commons.logging.Log;
@@ -32,29 +33,30 @@ import org.apache.fop.afp.fonts.AFPPageFonts;
 import org.apache.fop.afp.modca.GraphicsObject;
 import org.apache.fop.fonts.Font;
 import org.apache.fop.fonts.FontInfo;
-import org.apache.xmlgraphics.java2d.TextHandler;
+import org.apache.fop.svg.FOPTextHandler;
 
 /**
  * Specialized TextHandler implementation that the AFPGraphics2D class delegates to to paint text
  * using AFP GOCA text operations.
  */
-public class AFPTextHandler implements TextHandler {
+public class AFPTextHandler implements FOPTextHandler {
 
     /** logging instance */
     private static Log log = LogFactory.getLog(AFPTextHandler.class);
 
-    private AFPGraphics2D g2d = null;
-
     /** Overriding FontState */
     protected Font overrideFont = null;
 
+    /** Font information */
+    private final FontInfo fontInfo;
+
     /**
      * Main constructor.
      *
-     * @param g2d the AFPGraphics2D instance
+     * @param fontInfo the AFPGraphics2D instance
      */
-    public AFPTextHandler(AFPGraphics2D g2d) {
-        this.g2d = g2d;
+    public AFPTextHandler(FontInfo fontInfo) {
+        this.fontInfo = fontInfo;
     }
 
     /**
@@ -63,21 +65,20 @@ public class AFPTextHandler implements TextHandler {
      * @return the FontInfo object
      */
     public FontInfo getFontInfo() {
-        return g2d.getFontInfo();
+        return fontInfo;
     }
 
     /**
      * Registers a page font
      *
      * @param internalFontName the internal font name
+     * @param internalFontName the internal font name
      * @param fontSize the font size
      * @return a font reference
      */
-    private int registerPageFont(String internalFontName, int fontSize) {
+    private int registerPageFont(AFPPageFonts pageFonts, String internalFontName, int fontSize) {
         FontInfo fontInfo = getFontInfo();
         AFPFont afpFont = (AFPFont)fontInfo.getFonts().get(internalFontName);
-        AFPPaintingState paintingState = g2d.getPaintingState();
-        AFPPageFonts pageFonts = paintingState.getPageFonts();
         // register if necessary
         AFPFontAttributes afpFontAttributes = pageFonts.registerFont(
                 internalFontName,
@@ -87,14 +88,21 @@ public class AFPTextHandler implements TextHandler {
         return afpFontAttributes.getFontReference();
     }
 
+    /** {@inheritDoc} */
+    public void drawString(String text, float x, float y) throws IOException {
+        // TODO Remove me after removing the deprecated method in TextHandler.
+        throw new UnsupportedOperationException("Deprecated method!");
+    }
+
     /**
      * Add a text string to the current data object of the AFP datastream.
      * The text is painted using text operations.
      *
      * {@inheritDoc}
      */
-    public void drawString(String str, float x, float y) throws IOException {
+    public void drawString(Graphics2D g, String str, float x, float y) throws IOException {
         log.debug("drawString() str=" + str + ", x=" + x + ", y=" + y);
+        AFPGraphics2D g2d = (AFPGraphics2D)g;
         GraphicsObject graphicsObj = g2d.getGraphicsObject();
         Color color = g2d.getColor();
 
@@ -106,10 +114,11 @@ public class AFPTextHandler implements TextHandler {
 
         // set the character set
         int fontReference = 0;
+        AFPPageFonts pageFonts = paintingState.getPageFonts();
         if (overrideFont != null) {
             String internalFontName = overrideFont.getFontName();
             int fontSize = overrideFont.getFontSize();
-            fontReference = registerPageFont(internalFontName, fontSize);
+            fontReference = registerPageFont(pageFonts, internalFontName, fontSize);
         } else {
             java.awt.Font awtFont = g2d.getFont();
 //            AffineTransform fontTransform = awtFont.getTransform();
@@ -117,7 +126,7 @@ public class AFPTextHandler implements TextHandler {
             Font fopFont = fontInfo.getFontInstanceForAWTFont(awtFont);
             String internalFontName = fopFont.getFontName();
             int fontSize = fopFont.getFontSize();
-            fontReference = registerPageFont(internalFontName, fontSize);
+            fontReference = registerPageFont(pageFonts, internalFontName, fontSize);
         }
         graphicsObj.setCharacterSet(fontReference);
 
index a5758732a608cb8fc192642a8acd148beaa12ac1..c6a38b1b5f10b8719e456ba61cc9bd7ca982db30 100644 (file)
 
 package org.apache.fop.afp.svg;
 
-import java.awt.Color;
-import java.awt.Graphics2D;
-import java.awt.Paint;
-import java.awt.Shape;
-import java.awt.font.TextAttribute;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Point2D;
-import java.awt.geom.Rectangle2D;
-import java.io.IOException;
-import java.text.AttributedCharacterIterator;
-import java.text.CharacterIterator;
-import java.util.Iterator;
-import java.util.List;
-
-import org.apache.batik.dom.svg.SVGOMTextElement;
-import org.apache.batik.gvt.TextNode;
-import org.apache.batik.gvt.TextPainter;
-import org.apache.batik.gvt.font.GVTFontFamily;
-import org.apache.batik.gvt.renderer.StrokingTextPainter;
-import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
-import org.apache.batik.gvt.text.Mark;
-import org.apache.batik.gvt.text.TextPaintInfo;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.fop.afp.AFPGraphics2D;
-import org.apache.fop.fonts.Font;
-import org.apache.fop.fonts.FontInfo;
-import org.apache.fop.fonts.FontTriplet;
+import org.apache.fop.svg.AbstractFOPTextPainter;
+import org.apache.fop.svg.FOPTextHandler;
 
 
 /**
@@ -57,472 +31,14 @@ import org.apache.fop.fonts.FontTriplet;
  * drawString. If the text is complex or the cannot be translated
  * into a simple drawString the StrokingTextPainter is used instead.
  */
-public class AFPTextPainter implements TextPainter {
-
-    /** the logger for this class */
-    protected Log log = LogFactory.getLog(AFPTextPainter.class);
-
-    private final AFPTextHandler nativeTextHandler;
-
-    /**
-     * 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();
+public class AFPTextPainter extends AbstractFOPTextPainter {
 
     /**
-     * Create a new PS text painter with the given font information.
+     * Create a new text painter with the given font information.
      * @param nativeTextHandler the NativeTextHandler instance used for text painting
      */
-    public AFPTextPainter(AFPTextHandler nativeTextHandler) {
-        this.nativeTextHandler = nativeTextHandler;
-    }
-
-    /**
-     * 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) {
-        Point2D loc = node.getLocation();
-        log.debug("painting text node " + node);
-        if (hasUnsupportedAttributes(node)) {
-            log.debug("hasUnsuportedAttributes");
-            PROXY_PAINTER.paint(node, g2d);
-        } else {
-            log.debug("allAttributesSupported");
-            paintTextRuns(node.getTextRuns(), g2d, loc);
-        }
-    }
-
-    private boolean hasUnsupportedAttributes(TextNode node) {
-        Iterator iter = node.getTextRuns().iterator();
-        while (iter.hasNext()) {
-            StrokingTextPainter.TextRun
-                    run = (StrokingTextPainter.TextRun)iter.next();
-            AttributedCharacterIterator aci = run.getACI();
-            boolean hasUnsupported = hasUnsupportedAttributes(aci);
-            if (hasUnsupported) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private boolean hasUnsupportedAttributes(AttributedCharacterIterator aci) {
-        boolean hasUnsupported = false;
-
-        Font font = getFont(aci);
-        String text = getText(aci);
-        if (hasUnsupportedGlyphs(text, font)) {
-            log.trace("-> Unsupported glyphs found");
-            hasUnsupported = true;
-        }
-
-        TextPaintInfo tpi = (TextPaintInfo) aci.getAttribute(
-            GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO);
-        if ((tpi != null)
-                && ((tpi.strokeStroke != null && tpi.strokePaint != null)
-                    || (tpi.strikethroughStroke != null)
-                    || (tpi.underlineStroke != null)
-                    || (tpi.overlineStroke != null))) {
-                        log.trace("-> under/overlines etc. found");
-            hasUnsupported = true;
-        }
-
-        //Alpha is not supported
-        Paint foreground = (Paint) aci.getAttribute(TextAttribute.FOREGROUND);
-        if (foreground instanceof Color) {
-            Color col = (Color)foreground;
-            if (col.getAlpha() != 255) {
-                log.trace("-> transparency found");
-                hasUnsupported = true;
-            }
-        }
-
-        Object letSpace = aci.getAttribute(
-                            GVTAttributedCharacterIterator.TextAttribute.LETTER_SPACING);
-        if (letSpace != null) {
-            log.trace("-> letter spacing found");
-            hasUnsupported = true;
-        }
-
-        Object wordSpace = aci.getAttribute(
-                             GVTAttributedCharacterIterator.TextAttribute.WORD_SPACING);
-        if (wordSpace != null) {
-            log.trace("-> word spacing found");
-            hasUnsupported = true;
-        }
-
-        Object lengthAdjust = aci.getAttribute(
-                            GVTAttributedCharacterIterator.TextAttribute.LENGTH_ADJUST);
-        if (lengthAdjust != null) {
-            log.trace("-> length adjustments found");
-            hasUnsupported = true;
-        }
-
-        Object writeMod = aci.getAttribute(
-                GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE);
-        if (writeMod != null
-            && !GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE_LTR.equals(
-                  writeMod)) {
-            log.trace("-> Unsupported writing modes found");
-            hasUnsupported = true;
-        }
-
-        Object vertOr = aci.getAttribute(
-                GVTAttributedCharacterIterator.TextAttribute.VERTICAL_ORIENTATION);
-        if (GVTAttributedCharacterIterator.TextAttribute.ORIENTATION_ANGLE.equals(
-                  vertOr)) {
-            log.trace("-> vertical orientation found");
-            hasUnsupported = true;
-        }
-
-        Object rcDel = aci.getAttribute(
-                GVTAttributedCharacterIterator.TextAttribute.TEXT_COMPOUND_DELIMITER);
-        //Batik 1.6 returns null here which makes it impossible to determine whether this can
-        //be painted or not, i.e. fall back to stroking. :-(
-        if (rcDel != null && !(rcDel instanceof SVGOMTextElement)) {
-            log.trace("-> spans found");
-            hasUnsupported = true; //Filter spans
-        }
-
-        if (hasUnsupported) {
-            log.trace("Unsupported attributes found in ACI, using StrokingTextPainter");
-        }
-        return hasUnsupported;
-    }
-
-    /**
-     * Paint a list of text runs on the Graphics2D at a given location.
-     * @param textRuns the list of text runs
-     * @param g2d the Graphics2D to paint to
-     * @param loc the current location of the "cursor"
-     */
-    protected void paintTextRuns(List textRuns, Graphics2D g2d, Point2D loc) {
-        Point2D currentloc = loc;
-        Iterator i = textRuns.iterator();
-        while (i.hasNext()) {
-            StrokingTextPainter.TextRun
-                    run = (StrokingTextPainter.TextRun)i.next();
-            currentloc = paintTextRun(run, g2d, currentloc);
-        }
-    }
-
-    /**
-     * Paint a single text run on the Graphics2D at a given location.
-     * @param run the text run to paint
-     * @param g2d the Graphics2D to paint to
-     * @param loc the current location of the "cursor"
-     * @return the new location of the "cursor" after painting the text run
-     */
-    protected Point2D paintTextRun(StrokingTextPainter.TextRun run, Graphics2D g2d, Point2D loc) {
-        AttributedCharacterIterator aci = run.getACI();
-        aci.first();
-
-        updateLocationFromACI(aci, loc);
-        AffineTransform at = g2d.getTransform();
-        loc = at.transform(loc, null);
-
-        // font
-        Font font = getFont(aci);
-        if (font != null) {
-            nativeTextHandler.setOverrideFont(font);
-        }
-
-        // color
-        TextPaintInfo tpi = (TextPaintInfo) aci.getAttribute(
-                GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO);
-        if (tpi == null) {
-            return loc;
-        }
-        Paint foreground = tpi.fillPaint;
-        if (foreground instanceof Color) {
-            Color col = (Color)foreground;
-            g2d.setColor(col);
-        }
-        g2d.setPaint(foreground);
-
-        // text
-        String txt = getText(aci);
-        float advance = getStringWidth(txt, font);
-        float tx = 0;
-        TextNode.Anchor anchor = (TextNode.Anchor)aci.getAttribute(
-                GVTAttributedCharacterIterator.TextAttribute.ANCHOR_TYPE);
-        if (anchor != null) {
-            switch (anchor.getType()) {
-            case TextNode.Anchor.ANCHOR_MIDDLE:
-                tx = -advance / 2;
-                break;
-            case TextNode.Anchor.ANCHOR_END:
-                tx = -advance;
-                break;
-            default: //nop
-            }
-        }
-
-        // draw string
-        double x = loc.getX();
-        double y = loc.getY();
-        try {
-            try {
-                nativeTextHandler.drawString(txt, (float)x + tx, (float)y);
-            } catch (IOException ioe) {
-                if (g2d instanceof AFPGraphics2D) {
-                    ((AFPGraphics2D)g2d).handleIOException(ioe);
-                }
-            }
-        } finally {
-            nativeTextHandler.setOverrideFont(null);
-        }
-        loc.setLocation(loc.getX() + advance, loc.getY());
-        return loc;
-    }
-
-    /**
-     * Extract the raw text from an ACI.
-     * @param aci ACI to inspect
-     * @return the extracted text
-     */
-    protected String getText(AttributedCharacterIterator aci) {
-        StringBuffer sb = new StringBuffer(aci.getEndIndex() - aci.getBeginIndex());
-        for (char c = aci.first(); c != CharacterIterator.DONE; c = aci.next()) {
-            sb.append(c);
-        }
-        return sb.toString();
-    }
-
-    private void updateLocationFromACI(
-                AttributedCharacterIterator aci,
-                Point2D loc) {
-        //Adjust position of span
-        Float xpos = (Float)aci.getAttribute(
-                GVTAttributedCharacterIterator.TextAttribute.X);
-        Float ypos = (Float)aci.getAttribute(
-                GVTAttributedCharacterIterator.TextAttribute.Y);
-        Float dxpos = (Float)aci.getAttribute(
-                GVTAttributedCharacterIterator.TextAttribute.DX);
-        Float dypos = (Float)aci.getAttribute(
-                GVTAttributedCharacterIterator.TextAttribute.DY);
-        if (xpos != null) {
-            loc.setLocation(xpos.doubleValue(), loc.getY());
-        }
-        if (ypos != null) {
-            loc.setLocation(loc.getX(), ypos.doubleValue());
-        }
-        if (dxpos != null) {
-            loc.setLocation(loc.getX() + dxpos.doubleValue(), loc.getY());
-        }
-        if (dypos != null) {
-            loc.setLocation(loc.getX(), loc.getY() + dypos.doubleValue());
-        }
-    }
-
-    private String getStyle(AttributedCharacterIterator aci) {
-        Float posture = (Float)aci.getAttribute(TextAttribute.POSTURE);
-        return ((posture != null) && (posture.floatValue() > 0.0))
-                       ? Font.STYLE_ITALIC
-                       : Font.STYLE_NORMAL;
-    }
-
-    private int getWeight(AttributedCharacterIterator aci) {
-        Float taWeight = (Float)aci.getAttribute(TextAttribute.WEIGHT);
-        return ((taWeight != null) &&  (taWeight.floatValue() > 1.0))
-                       ? Font.WEIGHT_BOLD
-                       : Font.WEIGHT_NORMAL;
-    }
-
-    private Font getFont(AttributedCharacterIterator aci) {
-        Float fontSize = (Float)aci.getAttribute(TextAttribute.SIZE);
-        String style = getStyle(aci);
-        int weight = getWeight(aci);
-
-        FontInfo fontInfo = nativeTextHandler.getFontInfo();
-        String fontFamily = null;
-        List gvtFonts = (List) aci.getAttribute(
-                      GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES);
-        if (gvtFonts != null) {
-            Iterator i = gvtFonts.iterator();
-            while (i.hasNext()) {
-                GVTFontFamily fam = (GVTFontFamily) i.next();
-                /* (todo) Enable SVG Font painting
-                if (fam instanceof SVGFontFamily) {
-                    PROXY_PAINTER.paint(node, g2d);
-                    return;
-                }*/
-                fontFamily = fam.getFamilyName();
-                if (fontInfo.hasFont(fontFamily, style, weight)) {
-                    FontTriplet triplet = fontInfo.fontLookup(
-                            fontFamily, style, weight);
-                    int fsize = (int)(fontSize.floatValue() * 1000);
-                    return fontInfo.getFontInstance(triplet, fsize);
-                }
-            }
-        }
-        FontTriplet triplet = fontInfo.fontLookup("any", style, Font.WEIGHT_NORMAL);
-        int fsize = (int)(fontSize.floatValue() * 1000);
-        return fontInfo.getFontInstance(triplet, fsize);
-    }
-
-    private float getStringWidth(String str, Font font) {
-        float wordWidth = 0;
-        float whitespaceWidth = font.getWidth(font.mapChar(' '));
-
-        for (int i = 0; i < str.length(); i++) {
-            float charWidth;
-            char c = str.charAt(i);
-            if (!((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t'))) {
-                charWidth = font.getWidth(font.mapChar(c));
-                if (charWidth <= 0) {
-                    charWidth = whitespaceWidth;
-                }
-            } else {
-                charWidth = whitespaceWidth;
-            }
-            wordWidth += charWidth;
-        }
-        return wordWidth / 1000f;
-    }
-
-    private boolean hasUnsupportedGlyphs(String str, Font font) {
-        for (int i = 0; i < str.length(); i++) {
-            char c = str.charAt(i);
-            if (!((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t'))) {
-                if (!font.hasChar(c)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * 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) {
-        /* (todo) getBounds2D() is too slow
-         * because it uses the StrokingTextPainter. We should implement this
-         * method ourselves. */
-        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 AFP 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) {
-        return null;
-    }
-
-    /**
-     * Select at.
-     * This does nothing since the output is AFP 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) {
-        return null;
-    }
-
-    /**
-     * Select to.
-     * This does nothing since the output is AFP 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) {
-        return null;
-    }
-
-    /**
-     * Selec first.
-     * This does nothing since the output is AFP and not interactive.
-     *
-     * @param node the text node
-     * @return null
-     */
-    public Mark selectFirst(TextNode node) {
-        return null;
-    }
-
-    /**
-     * Select last.
-     * This does nothing since the output is AFP and not interactive.
-     *
-     * @param node the text node
-     * @return null
-     */
-    public Mark selectLast(TextNode node) {
-        return null;
-    }
-
-    /**
-     * Get selected.
-     * This does nothing since the output is AFP and not interactive.
-     *
-     * @param start the start mark
-     * @param finish the finish mark
-     * @return null
-     */
-    public int[] getSelected(Mark start, Mark finish) {
-        return null;
-    }
-
-    /**
-     * Get the highlighted shape.
-     * This does nothing since the output is AFP and not interactive.
-     *
-     * @param beginMark the start mark
-     * @param endMark the end mark
-     * @return null
-     */
-    public Shape getHighlightShape(Mark beginMark, Mark endMark) {
-        return null;
+    public AFPTextPainter(FOPTextHandler nativeTextHandler) {
+        super(nativeTextHandler);
     }
 
 }
index 87907eddfde1984937c36f0038a3ce79e1618ce5..ea92f748b407e966089856258166c54e248884f7 100644 (file)
@@ -6,15 +6,18 @@ import java.awt.geom.Rectangle2D;
 
 import org.apache.batik.bridge.BridgeContext;
 import org.apache.batik.gvt.GraphicsNode;
-import org.apache.fop.render.AbstractGraphics2DImagePainter;
-import org.apache.xmlgraphics.java2d.Graphics2DPainterPreparator;
+
+import org.apache.xmlgraphics.java2d.Graphics2DImagePainter;
 
 /**
  * A generic graphics 2D image painter implementation
  */
-public class Graphics2DImagePainterImpl extends AbstractGraphics2DImagePainter {
+public class Graphics2DImagePainterImpl implements Graphics2DImagePainter {
 
+    private final GraphicsNode root;
+    /** the Batik bridge context */
     protected final BridgeContext ctx;
+    /** the intrinsic size of the image */
     protected final Dimension imageSize;
 
     /**
@@ -25,7 +28,7 @@ public class Graphics2DImagePainterImpl extends AbstractGraphics2DImagePainter {
      * @param imageSize the image size
      */
     public Graphics2DImagePainterImpl(GraphicsNode root, BridgeContext ctx, Dimension imageSize) {
-        super(root);
+        this.root = root;
         this.imageSize = imageSize;
         this.ctx = ctx;
     }
@@ -35,30 +38,30 @@ public class Graphics2DImagePainterImpl extends AbstractGraphics2DImagePainter {
         return imageSize;
     }
 
-    /** {@inheritDoc} */
-    protected Graphics2DPainterPreparator getPreparator() {
-        return new Graphics2DPainterPreparator() {
+    private void prepare(Graphics2D g2d, Rectangle2D area) {
+        // If no viewbox is defined in the svg file, a viewbox of 100x100 is
+        // assumed, as defined in SVGUserAgent.getViewportSize()
+        double tx = area.getX();
+        double ty = area.getY();
+        if (tx != 0 || ty != 0) {
+            g2d.translate(tx, ty);
+        }
 
-            public void prepare(Graphics2D g2d, Rectangle2D area) {
-                // If no viewbox is defined in the svg file, a viewbox of 100x100 is
-                // assumed, as defined in SVGUserAgent.getViewportSize()
-                double tx = area.getX();
-                double ty = area.getY();
-                if (tx != 0 || ty != 0) {
-                    g2d.translate(tx, ty);
-                }
+        float iw = (float) ctx.getDocumentSize().getWidth();
+        float ih = (float) ctx.getDocumentSize().getHeight();
+        float w = (float) area.getWidth();
+        float h = (float) area.getHeight();
+        float sx = w / iw;
+        float sy = h / ih;
+        if (sx != 1.0 || sy != 1.0) {
+            g2d.scale(sx, sy);
+        }
+    }
 
-                float iw = (float) ctx.getDocumentSize().getWidth();
-                float ih = (float) ctx.getDocumentSize().getHeight();
-                float w = (float) area.getWidth();
-                float h = (float) area.getHeight();
-                float sx = w / iw;
-                float sy = h / ih;
-                if (sx != 1.0 || sy != 1.0) {
-                    g2d.scale(sx, sy);
-                }
-            }
-        };
+    /** {@inheritDoc} */
+    public void paint(Graphics2D g2d, Rectangle2D area) {
+        prepare(g2d, area);
+        root.paint(g2d);
     }
 
 }
\ No newline at end of file
diff --git a/src/java/org/apache/fop/render/AbstractGraphics2DImagePainter.java b/src/java/org/apache/fop/render/AbstractGraphics2DImagePainter.java
deleted file mode 100644 (file)
index 4a3c7d9..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-package org.apache.fop.render;
-
-import java.awt.Graphics2D;
-import java.awt.geom.Rectangle2D;
-
-import org.apache.batik.gvt.GraphicsNode;
-import org.apache.xmlgraphics.java2d.Graphics2DPainterPreparator;
-
-/**
- * An initializable graphics 2D image painter
- */
-public abstract class AbstractGraphics2DImagePainter
-    implements org.apache.xmlgraphics.java2d.Graphics2DImagePainter {
-
-    private final GraphicsNode root;
-
-    protected Graphics2DPainterPreparator preparator;
-
-    /**
-     * Main constructor
-     *
-     * @param root a graphics node root
-     */
-    public AbstractGraphics2DImagePainter(GraphicsNode root) {
-        this.root = root;
-    }
-
-    /**
-     * Sets the graphics 2D painter preparator
-     *
-     * @param initializer the graphics 2D preparator
-     */
-    public void setPreparator(Graphics2DPainterPreparator preparator) {
-        this.preparator = preparator;
-    }
-
-    /**
-     * Returns the graphics 2D painter preparator
-     *
-     * @return the graphics 2D painter preparator
-     */
-    protected Graphics2DPainterPreparator getPreparator() {
-        return this.preparator;
-    }
-
-    /** {@inheritDoc} */
-    public void paint(Graphics2D g2d, Rectangle2D area) {
-        Graphics2DPainterPreparator preparator = getPreparator();
-        if (preparator != null) {
-            preparator.prepare(g2d, area);
-        }
-        root.paint(g2d);
-    }
-
-}
index e8fe1f4f1b483a583980151aff4293944b718ef5..fa3f00cdb969781e2dcfcdb479b8c5479385d357 100644 (file)
 
 package org.apache.fop.render.afp;
 
-import java.awt.geom.AffineTransform;
 import java.io.IOException;
 
 import org.apache.fop.afp.AFPDataObjectInfo;
 import org.apache.fop.afp.AFPGraphics2D;
 import org.apache.fop.afp.AFPGraphicsObjectInfo;
-import org.apache.fop.afp.AFPObjectAreaInfo;
 import org.apache.fop.afp.AFPPaintingState;
 import org.apache.fop.afp.AFPResourceInfo;
 import org.apache.fop.afp.AFPResourceLevel;
@@ -51,7 +49,8 @@ public class AFPImageHandlerGraphics2D extends AFPImageHandler {
     public AFPDataObjectInfo generateDataObjectInfo(
             AFPRendererImageInfo rendererImageInfo) throws IOException {
 
-        AFPRendererContext rendererContext = (AFPRendererContext)rendererImageInfo.getRendererContext();
+        AFPRendererContext rendererContext
+            = (AFPRendererContext)rendererImageInfo.getRendererContext();
         AFPInfo afpInfo = rendererContext.getInfo();
         ImageGraphics2D imageG2D = (ImageGraphics2D)rendererImageInfo.getImage();
         Graphics2DImagePainter painter = imageG2D.getGraphics2DImagePainter();
@@ -67,11 +66,11 @@ public class AFPImageHandlerGraphics2D extends AFPImageHandler {
             return null;
         } else {
             AFPGraphicsObjectInfo graphicsObjectInfo
-            = (AFPGraphicsObjectInfo)super.generateDataObjectInfo(rendererImageInfo);
+                = (AFPGraphicsObjectInfo)super.generateDataObjectInfo(rendererImageInfo);
 
             AFPResourceInfo resourceInfo = graphicsObjectInfo.getResourceInfo();
             //level not explicitly set/changed so default to inline for GOCA graphic objects
-            // (due to a bug in the IBM AFP Workbench Viewer (2.04.01.07) - hard copy works just fine)
+            // (due to a bug in the IBM AFP Workbench Viewer (2.04.01.07), hard copy works just fine)
             if (!resourceInfo.levelChanged()) {
                 resourceInfo.setLevel(new AFPResourceLevel(AFPResourceLevel.INLINE));
             }
@@ -86,22 +85,9 @@ public class AFPImageHandlerGraphics2D extends AFPImageHandler {
 
             graphicsObjectInfo.setGraphics2D(g2d);
 
-            // translate to current location
-            AFPPaintingState paintingState = afpInfo.getPaintingState();
-            AffineTransform at = paintingState.getData().getTransform();
-            g2d.translate(at.getTranslateX(), at.getTranslateY());
-
             // set painter
             graphicsObjectInfo.setPainter(painter);
 
-            // invert y-axis for GOCA
-            final int sx = 1;
-            final int sy = -1;
-            AFPObjectAreaInfo objectAreaInfo = graphicsObjectInfo.getObjectAreaInfo();
-            int height = objectAreaInfo.getHeight();
-            g2d.translate(0, height);
-            g2d.scale(sx, sy);
-
             return graphicsObjectInfo;
         }
     }
index ede556ecb9c4c8d22d5ce64f52ab42dee63fc3e1..b85dd96f98e33bae9652f9cbf3115337a05b08dd 100644 (file)
@@ -332,37 +332,6 @@ public class AFPRenderer extends AbstractPathOrientedRenderer {
         dataStream.endPage();
     }
 
-    /** {@inheritDoc} */
-    public void clip() {
-        // TODO
-        log.debug("NYI clip()");
-    }
-
-    /** {@inheritDoc} */
-    public void clipRect(float x, float y, float width, float height) {
-        // TODO
-        log.debug("NYI clipRect(x=" + x + ",y=" + y
-                    + ",width=" + width + ", height=" + height + ")");
-    }
-
-    /** {@inheritDoc} */
-    public void moveTo(float x, float y) {
-        // TODO
-        log.debug("NYI moveTo(x=" + x + ",y=" + y + ")");
-    }
-
-    /** {@inheritDoc} */
-    public void lineTo(float x, float y) {
-        // TODO
-        log.debug("NYI lineTo(x=" + x + ",y=" + y + ")");
-    }
-
-    /** {@inheritDoc} */
-    public void closePath() {
-        // TODO
-        log.debug("NYI closePath()");
-    }
-
     /** {@inheritDoc} */
     public void drawBorderLine(float x1, float y1, float x2, float y2,
             boolean horz, boolean startOrBefore, int style, Color col) {
@@ -535,18 +504,6 @@ public class AFPRenderer extends AbstractPathOrientedRenderer {
         paintingState.pop();
     }
 
-    /** Indicates the beginning of a text object. */
-    public void beginTextObject() {
-        //TODO PDF specific maybe?
-        log.debug("NYI beginTextObject()");
-    }
-
-    /** Indicates the end of a text object. */
-    public void endTextObject() {
-        //TODO PDF specific maybe?
-        log.debug("NYI endTextObject()");
-    }
-
     /** {@inheritDoc} */
     public void renderImage(Image image, Rectangle2D pos) {
         drawImage(image.getURL(), pos, image.getForeignAttributes());
@@ -809,4 +766,47 @@ public class AFPRenderer extends AbstractPathOrientedRenderer {
         concatenateTransformationMatrix(at);
     }
 
+    /** {@inheritDoc} */
+    public void clip() {
+        // TODO
+//        log.debug("NYI clip()");
+    }
+
+    /** {@inheritDoc} */
+    public void clipRect(float x, float y, float width, float height) {
+        // TODO
+//        log.debug("NYI clipRect(x=" + x + ",y=" + y
+//                    + ",width=" + width + ", height=" + height + ")");
+    }
+
+    /** {@inheritDoc} */
+    public void moveTo(float x, float y) {
+        // TODO
+//        log.debug("NYI moveTo(x=" + x + ",y=" + y + ")");
+    }
+
+    /** {@inheritDoc} */
+    public void lineTo(float x, float y) {
+        // TODO
+//        log.debug("NYI lineTo(x=" + x + ",y=" + y + ")");
+    }
+
+    /** {@inheritDoc} */
+    public void closePath() {
+        // TODO
+//        log.debug("NYI closePath()");
+    }
+
+    /** Indicates the beginning of a text object. */
+    public void beginTextObject() {
+        //TODO PDF specific maybe?
+//        log.debug("NYI beginTextObject()");
+    }
+
+    /** Indicates the end of a text object. */
+    public void endTextObject() {
+        //TODO PDF specific maybe?
+//        log.debug("NYI endTextObject()");
+    }
+
 }
index 2800686d9e5dd8972cecbe0ece804179870782ca..bf74b405316fb2c263e398c5ec7b6c8d39f10230 100644 (file)
@@ -34,10 +34,10 @@ import org.apache.fop.afp.AFPPaintingState;
 import org.apache.fop.afp.AFPResourceInfo;
 import org.apache.fop.afp.AFPResourceManager;
 import org.apache.fop.afp.AFPUnitConverter;
-import org.apache.fop.afp.Graphics2DImagePainterGOCA;
 import org.apache.fop.afp.svg.AFPBridgeContext;
 import org.apache.fop.apps.FOUserAgent;
 import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.image.loader.batik.Graphics2DImagePainterImpl;
 import org.apache.fop.render.AbstractGenericSVGHandler;
 import org.apache.fop.render.Renderer;
 import org.apache.fop.render.RendererContext;
@@ -67,9 +67,6 @@ public class AFPSVGHandler extends AbstractGenericSVGHandler {
         }
     }
 
-    private static final int X = 0;
-    private static final int Y = 1;
-
     /**
      * Render the SVG document.
      *
@@ -101,7 +98,7 @@ public class AFPSVGHandler extends AbstractGenericSVGHandler {
         }
 
         // Create a new AFPGraphics2D
-        final boolean textAsShapes = false;
+        final boolean textAsShapes = afpInfo.strokeText();
         AFPGraphics2D g2d = afpInfo.createGraphics2D(textAsShapes);
 
         AFPPaintingState paintingState = g2d.getPaintingState();
@@ -220,7 +217,7 @@ public class AFPSVGHandler extends AbstractGenericSVGHandler {
             painter = super.createGraphics2DImagePainter(root, ctx, imageSize);
         } else {
             // paint as GOCA Graphics
-            painter = new Graphics2DImagePainterGOCA(root, ctx, imageSize);
+            painter = new Graphics2DImagePainterImpl(root, ctx, imageSize);
         }
         return painter;
     }
diff --git a/src/java/org/apache/fop/svg/AbstractFOPTextPainter.java b/src/java/org/apache/fop/svg/AbstractFOPTextPainter.java
new file mode 100644 (file)
index 0000000..560c76c
--- /dev/null
@@ -0,0 +1,528 @@
+/*
+ * 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.svg;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Paint;
+import java.awt.Shape;
+import java.awt.font.TextAttribute;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.io.IOException;
+import java.text.AttributedCharacterIterator;
+import java.text.CharacterIterator;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.batik.dom.svg.SVGOMTextElement;
+import org.apache.batik.gvt.TextNode;
+import org.apache.batik.gvt.TextPainter;
+import org.apache.batik.gvt.font.GVTFontFamily;
+import org.apache.batik.gvt.renderer.StrokingTextPainter;
+import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
+import org.apache.batik.gvt.text.Mark;
+import org.apache.batik.gvt.text.TextPaintInfo;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.fop.afp.AFPGraphics2D;
+import org.apache.fop.fonts.Font;
+import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.fonts.FontTriplet;
+
+
+/**
+ * Renders the attributed character iterator of a <tt>TextNode</tt>.
+ * This class draws the text directly into the Graphics2D so that
+ * the text is not drawn using shapes.
+ * 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.
+ */
+public abstract class AbstractFOPTextPainter implements TextPainter {
+
+    /** the logger for this class */
+    protected Log log = LogFactory.getLog(AbstractFOPTextPainter.class);
+
+    private final FOPTextHandler nativeTextHandler;
+
+    /**
+     * 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 nativeTextHandler the NativeTextHandler instance used for text painting
+     */
+    public AbstractFOPTextPainter(FOPTextHandler nativeTextHandler) {
+        this.nativeTextHandler = nativeTextHandler;
+    }
+
+    /**
+     * 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) {
+        Point2D loc = node.getLocation();
+        log.debug("painting text node " + node);
+        if (hasUnsupportedAttributes(node)) {
+            log.debug("hasUnsuportedAttributes");
+            PROXY_PAINTER.paint(node, g2d);
+        } else {
+            log.debug("allAttributesSupported");
+            paintTextRuns(node.getTextRuns(), g2d, loc);
+        }
+    }
+
+    private boolean hasUnsupportedAttributes(TextNode node) {
+        Iterator iter = node.getTextRuns().iterator();
+        while (iter.hasNext()) {
+            StrokingTextPainter.TextRun
+                    run = (StrokingTextPainter.TextRun)iter.next();
+            AttributedCharacterIterator aci = run.getACI();
+            boolean hasUnsupported = hasUnsupportedAttributes(aci);
+            if (hasUnsupported) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean hasUnsupportedAttributes(AttributedCharacterIterator aci) {
+        boolean hasUnsupported = false;
+
+        Font font = getFont(aci);
+        String text = getText(aci);
+        if (hasUnsupportedGlyphs(text, font)) {
+            log.trace("-> Unsupported glyphs found");
+            hasUnsupported = true;
+        }
+
+        TextPaintInfo tpi = (TextPaintInfo) aci.getAttribute(
+            GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO);
+        if ((tpi != null)
+                && ((tpi.strokeStroke != null && tpi.strokePaint != null)
+                    || (tpi.strikethroughStroke != null)
+                    || (tpi.underlineStroke != null)
+                    || (tpi.overlineStroke != null))) {
+                        log.trace("-> under/overlines etc. found");
+            hasUnsupported = true;
+        }
+
+        //Alpha is not supported
+        Paint foreground = (Paint) aci.getAttribute(TextAttribute.FOREGROUND);
+        if (foreground instanceof Color) {
+            Color col = (Color)foreground;
+            if (col.getAlpha() != 255) {
+                log.trace("-> transparency found");
+                hasUnsupported = true;
+            }
+        }
+
+        Object letSpace = aci.getAttribute(
+                            GVTAttributedCharacterIterator.TextAttribute.LETTER_SPACING);
+        if (letSpace != null) {
+            log.trace("-> letter spacing found");
+            hasUnsupported = true;
+        }
+
+        Object wordSpace = aci.getAttribute(
+                             GVTAttributedCharacterIterator.TextAttribute.WORD_SPACING);
+        if (wordSpace != null) {
+            log.trace("-> word spacing found");
+            hasUnsupported = true;
+        }
+
+        Object lengthAdjust = aci.getAttribute(
+                            GVTAttributedCharacterIterator.TextAttribute.LENGTH_ADJUST);
+        if (lengthAdjust != null) {
+            log.trace("-> length adjustments found");
+            hasUnsupported = true;
+        }
+
+        Object writeMod = aci.getAttribute(
+                GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE);
+        if (writeMod != null
+            && !GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE_LTR.equals(
+                  writeMod)) {
+            log.trace("-> Unsupported writing modes found");
+            hasUnsupported = true;
+        }
+
+        Object vertOr = aci.getAttribute(
+                GVTAttributedCharacterIterator.TextAttribute.VERTICAL_ORIENTATION);
+        if (GVTAttributedCharacterIterator.TextAttribute.ORIENTATION_ANGLE.equals(
+                  vertOr)) {
+            log.trace("-> vertical orientation found");
+            hasUnsupported = true;
+        }
+
+        Object rcDel = aci.getAttribute(
+                GVTAttributedCharacterIterator.TextAttribute.TEXT_COMPOUND_DELIMITER);
+        //Batik 1.6 returns null here which makes it impossible to determine whether this can
+        //be painted or not, i.e. fall back to stroking. :-(
+        if (rcDel != null && !(rcDel instanceof SVGOMTextElement)) {
+            log.trace("-> spans found");
+            hasUnsupported = true; //Filter spans
+        }
+
+        if (hasUnsupported) {
+            log.trace("Unsupported attributes found in ACI, using StrokingTextPainter");
+        }
+        return hasUnsupported;
+    }
+
+    /**
+     * Paint a list of text runs on the Graphics2D at a given location.
+     * @param textRuns the list of text runs
+     * @param g2d the Graphics2D to paint to
+     * @param loc the current location of the "cursor"
+     */
+    protected void paintTextRuns(List textRuns, Graphics2D g2d, Point2D loc) {
+        Point2D currentloc = loc;
+        Iterator i = textRuns.iterator();
+        while (i.hasNext()) {
+            StrokingTextPainter.TextRun
+                    run = (StrokingTextPainter.TextRun)i.next();
+            currentloc = paintTextRun(run, g2d, currentloc);
+        }
+    }
+
+    /**
+     * Paint a single text run on the Graphics2D at a given location.
+     * @param run the text run to paint
+     * @param g2d the Graphics2D to paint to
+     * @param loc the current location of the "cursor"
+     * @return the new location of the "cursor" after painting the text run
+     */
+    protected Point2D paintTextRun(StrokingTextPainter.TextRun run, Graphics2D g2d, Point2D loc) {
+        AttributedCharacterIterator aci = run.getACI();
+        aci.first();
+
+        updateLocationFromACI(aci, loc);
+        AffineTransform at = g2d.getTransform();
+        loc = at.transform(loc, null);
+
+        // font
+        Font font = getFont(aci);
+        if (font != null) {
+            nativeTextHandler.setOverrideFont(font);
+        }
+
+        // color
+        TextPaintInfo tpi = (TextPaintInfo) aci.getAttribute(
+                GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO);
+        if (tpi == null) {
+            return loc;
+        }
+        Paint foreground = tpi.fillPaint;
+        if (foreground instanceof Color) {
+            Color col = (Color)foreground;
+            g2d.setColor(col);
+        }
+        g2d.setPaint(foreground);
+
+        // text
+        String txt = getText(aci);
+        float advance = getStringWidth(txt, font);
+        float tx = 0;
+        TextNode.Anchor anchor = (TextNode.Anchor)aci.getAttribute(
+                GVTAttributedCharacterIterator.TextAttribute.ANCHOR_TYPE);
+        if (anchor != null) {
+            switch (anchor.getType()) {
+            case TextNode.Anchor.ANCHOR_MIDDLE:
+                tx = -advance / 2;
+                break;
+            case TextNode.Anchor.ANCHOR_END:
+                tx = -advance;
+                break;
+            default: //nop
+            }
+        }
+
+        // draw string
+        double x = loc.getX();
+        double y = loc.getY();
+        try {
+            try {
+                nativeTextHandler.drawString(g2d, txt, (float)x + tx, (float)y);
+            } catch (IOException ioe) {
+                if (g2d instanceof AFPGraphics2D) {
+                    ((AFPGraphics2D)g2d).handleIOException(ioe);
+                }
+            }
+        } finally {
+            nativeTextHandler.setOverrideFont(null);
+        }
+        loc.setLocation(loc.getX() + advance, loc.getY());
+        return loc;
+    }
+
+    /**
+     * Extract the raw text from an ACI.
+     * @param aci ACI to inspect
+     * @return the extracted text
+     */
+    protected String getText(AttributedCharacterIterator aci) {
+        StringBuffer sb = new StringBuffer(aci.getEndIndex() - aci.getBeginIndex());
+        for (char c = aci.first(); c != CharacterIterator.DONE; c = aci.next()) {
+            sb.append(c);
+        }
+        return sb.toString();
+    }
+
+    private void updateLocationFromACI(
+                AttributedCharacterIterator aci,
+                Point2D loc) {
+        //Adjust position of span
+        Float xpos = (Float)aci.getAttribute(
+                GVTAttributedCharacterIterator.TextAttribute.X);
+        Float ypos = (Float)aci.getAttribute(
+                GVTAttributedCharacterIterator.TextAttribute.Y);
+        Float dxpos = (Float)aci.getAttribute(
+                GVTAttributedCharacterIterator.TextAttribute.DX);
+        Float dypos = (Float)aci.getAttribute(
+                GVTAttributedCharacterIterator.TextAttribute.DY);
+        if (xpos != null) {
+            loc.setLocation(xpos.doubleValue(), loc.getY());
+        }
+        if (ypos != null) {
+            loc.setLocation(loc.getX(), ypos.doubleValue());
+        }
+        if (dxpos != null) {
+            loc.setLocation(loc.getX() + dxpos.doubleValue(), loc.getY());
+        }
+        if (dypos != null) {
+            loc.setLocation(loc.getX(), loc.getY() + dypos.doubleValue());
+        }
+    }
+
+    private String getStyle(AttributedCharacterIterator aci) {
+        Float posture = (Float)aci.getAttribute(TextAttribute.POSTURE);
+        return ((posture != null) && (posture.floatValue() > 0.0))
+                       ? Font.STYLE_ITALIC
+                       : Font.STYLE_NORMAL;
+    }
+
+    private int getWeight(AttributedCharacterIterator aci) {
+        Float taWeight = (Float)aci.getAttribute(TextAttribute.WEIGHT);
+        return ((taWeight != null) &&  (taWeight.floatValue() > 1.0))
+                       ? Font.WEIGHT_BOLD
+                       : Font.WEIGHT_NORMAL;
+    }
+
+    private Font getFont(AttributedCharacterIterator aci) {
+        Float fontSize = (Float)aci.getAttribute(TextAttribute.SIZE);
+        String style = getStyle(aci);
+        int weight = getWeight(aci);
+
+        FontInfo fontInfo = nativeTextHandler.getFontInfo();
+        String fontFamily = null;
+        List gvtFonts = (List) aci.getAttribute(
+                      GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES);
+        if (gvtFonts != null) {
+            Iterator i = gvtFonts.iterator();
+            while (i.hasNext()) {
+                GVTFontFamily fam = (GVTFontFamily) i.next();
+                /* (todo) Enable SVG Font painting
+                if (fam instanceof SVGFontFamily) {
+                    PROXY_PAINTER.paint(node, g2d);
+                    return;
+                }*/
+                fontFamily = fam.getFamilyName();
+                if (fontInfo.hasFont(fontFamily, style, weight)) {
+                    FontTriplet triplet = fontInfo.fontLookup(
+                            fontFamily, style, weight);
+                    int fsize = (int)(fontSize.floatValue() * 1000);
+                    return fontInfo.getFontInstance(triplet, fsize);
+                }
+            }
+        }
+        FontTriplet triplet = fontInfo.fontLookup("any", style, Font.WEIGHT_NORMAL);
+        int fsize = (int)(fontSize.floatValue() * 1000);
+        return fontInfo.getFontInstance(triplet, fsize);
+    }
+
+    private float getStringWidth(String str, Font font) {
+        float wordWidth = 0;
+        float whitespaceWidth = font.getWidth(font.mapChar(' '));
+
+        for (int i = 0; i < str.length(); i++) {
+            float charWidth;
+            char c = str.charAt(i);
+            if (!((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t'))) {
+                charWidth = font.getWidth(font.mapChar(c));
+                if (charWidth <= 0) {
+                    charWidth = whitespaceWidth;
+                }
+            } else {
+                charWidth = whitespaceWidth;
+            }
+            wordWidth += charWidth;
+        }
+        return wordWidth / 1000f;
+    }
+
+    private boolean hasUnsupportedGlyphs(String str, Font font) {
+        for (int i = 0; i < str.length(); i++) {
+            char c = str.charAt(i);
+            if (!((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t'))) {
+                if (!font.hasChar(c)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 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) {
+        /* (todo) getBounds2D() is too slow
+         * because it uses the StrokingTextPainter. We should implement this
+         * method ourselves. */
+        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 AFP 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) {
+        return null;
+    }
+
+    /**
+     * Select at.
+     * This does nothing since the output is AFP 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) {
+        return null;
+    }
+
+    /**
+     * Select to.
+     * This does nothing since the output is AFP 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) {
+        return null;
+    }
+
+    /**
+     * Selec first.
+     * This does nothing since the output is AFP and not interactive.
+     *
+     * @param node the text node
+     * @return null
+     */
+    public Mark selectFirst(TextNode node) {
+        return null;
+    }
+
+    /**
+     * Select last.
+     * This does nothing since the output is AFP and not interactive.
+     *
+     * @param node the text node
+     * @return null
+     */
+    public Mark selectLast(TextNode node) {
+        return null;
+    }
+
+    /**
+     * Get selected.
+     * This does nothing since the output is AFP and not interactive.
+     *
+     * @param start the start mark
+     * @param finish the finish mark
+     * @return null
+     */
+    public int[] getSelected(Mark start, Mark finish) {
+        return null;
+    }
+
+    /**
+     * Get the highlighted shape.
+     * This does nothing since the output is AFP and not interactive.
+     *
+     * @param beginMark the start mark
+     * @param endMark the end mark
+     * @return null
+     */
+    public Shape getHighlightShape(Mark beginMark, Mark endMark) {
+        return null;
+    }
+
+}
diff --git a/src/java/org/apache/fop/svg/FOPTextHandler.java b/src/java/org/apache/fop/svg/FOPTextHandler.java
new file mode 100644 (file)
index 0000000..8fa9eee
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * 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.svg;
+
+public interface FOPTextHandler extends org.apache.xmlgraphics.java2d.TextHandler {
+
+    void setOverrideFont(org.apache.fop.fonts.Font font);
+
+    org.apache.fop.fonts.FontInfo getFontInfo();
+}