]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Moved AFPTextElementBridge and AFPTextPainter to org.apache.fop.afp since they have...
authorAdrian Cumiskey <acumiskey@apache.org>
Mon, 27 Oct 2008 11:23:53 +0000 (11:23 +0000)
committerAdrian Cumiskey <acumiskey@apache.org>
Mon, 27 Oct 2008 11:23:53 +0000 (11:23 +0000)
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_AFPGOCAResources@708140 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/fop/afp/AFPTextElementBridge.java [new file with mode: 0644]
src/java/org/apache/fop/afp/AFPTextPainter.java [new file with mode: 0644]
src/java/org/apache/fop/render/afp/AFPImageGraphics2DFactory.java
src/java/org/apache/fop/render/afp/AFPSVGHandler.java
src/java/org/apache/fop/render/afp/AFPTextElementBridge.java [deleted file]
src/java/org/apache/fop/render/afp/AFPTextPainter.java [deleted file]

diff --git a/src/java/org/apache/fop/afp/AFPTextElementBridge.java b/src/java/org/apache/fop/afp/AFPTextElementBridge.java
new file mode 100644 (file)
index 0000000..2bfccf4
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * 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 org.apache.batik.bridge.SVGTextElementBridge;
+import org.apache.batik.bridge.BridgeContext;
+import org.apache.batik.gvt.GraphicsNode;
+import org.apache.batik.gvt.TextNode;
+import org.apache.batik.gvt.TextPainter;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+/**
+ * Bridge class for the &lt;text> element.
+ * This bridge will use the direct text painter if the text
+ * for the element is simple.
+ */
+public class AFPTextElementBridge extends SVGTextElementBridge {
+    
+    private AFPTextPainter textPainter;
+
+    /**
+     * Constructs a new bridge for the &lt;text> element.
+     * @param textPainter the text painter to use
+     */
+    public AFPTextElementBridge(AFPTextPainter textPainter) {
+        this.textPainter = textPainter;
+    }
+
+    /**
+     * Create a text element bridge.
+     * This set the text painter on the node if the text is simple.
+     * @param ctx the bridge context
+     * @param e the svg element
+     * @return the text graphics node created by the super class
+     */
+    public GraphicsNode createGraphicsNode(BridgeContext ctx, Element e) {
+        GraphicsNode node = super.createGraphicsNode(ctx, e);
+        if (node != null && isSimple(ctx, e, node)) {
+            ((TextNode)node).setTextPainter(getTextPainter());
+        }
+        return node;
+    }
+
+    private TextPainter getTextPainter() {
+        return this.textPainter;
+    }
+
+    /**
+     * Check if text element contains simple text.
+     * This checks the children of the text element to determine
+     * if the text is simple. The text is simple if it can be rendered
+     * with basic text drawing algorithms. This means there are no
+     * alternate characters, the font is known and there are no effects
+     * applied to the text.
+     *
+     * @param ctx the bridge context
+     * @param element the svg text element
+     * @param node the graphics node
+     * @return true if this text is simple of false if it cannot be
+     *         easily rendered using normal drawString on the PDFGraphics2D
+     */
+    private boolean isSimple(BridgeContext ctx, Element element, GraphicsNode node) {
+        for (Node n = element.getFirstChild();
+                n != null;
+                n = n.getNextSibling()) {
+
+            switch (n.getNodeType()) {
+            case Node.ELEMENT_NODE:
+
+                if (n.getLocalName().equals(SVG_TSPAN_TAG)
+                        || n.getLocalName().equals(SVG_ALT_GLYPH_TAG)) {
+                    return false;
+                } else if (n.getLocalName().equals(SVG_TEXT_PATH_TAG)) {
+                    return false;
+                } else if (n.getLocalName().equals(SVG_TREF_TAG)) {
+                    return false;
+                }
+                break;
+            case Node.TEXT_NODE:
+            case Node.CDATA_SECTION_NODE:
+            default:
+            }
+        }
+
+        /*if (CSSUtilities.convertFilter(element, node, ctx) != null) {
+            return false;
+        }*/
+
+        return true;
+    }
+}
+
diff --git a/src/java/org/apache/fop/afp/AFPTextPainter.java b/src/java/org/apache/fop/afp/AFPTextPainter.java
new file mode 100644 (file)
index 0000000..d0d54fd
--- /dev/null
@@ -0,0 +1,515 @@
+/*
+ * 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.Graphics2D;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.text.AttributedCharacterIterator;
+import java.text.CharacterIterator;
+import java.awt.font.TextAttribute;
+import java.awt.Shape;
+import java.awt.Paint;
+import java.awt.Color;
+import java.io.IOException;
+import java.util.List;
+import java.util.Iterator;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.fop.fonts.Font;
+import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.fonts.FontTriplet;
+
+import org.apache.batik.dom.svg.SVGOMTextElement;
+import org.apache.batik.gvt.text.Mark;
+import org.apache.batik.gvt.TextPainter;
+import org.apache.batik.gvt.TextNode;
+import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
+import org.apache.batik.gvt.text.TextPaintInfo;
+import org.apache.batik.gvt.font.GVTFontFamily;
+import org.apache.batik.gvt.renderer.StrokingTextPainter;
+
+
+/**
+ * Renders the attributed character iterator of a <tt>TextNode</tt>.
+ * This class draws the text directly into the AFPGraphics2D 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 class AFPTextPainter implements TextPainter {
+    
+    /** the logger for this class */
+    protected Log log = LogFactory.getLog(AFPTextPainter.class);
+    
+    private 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();
+
+    /**
+     * Create a new PS 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;
+        
+        String text = getText(aci);
+        Font font = makeFont(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);
+        loc = g2d.getTransform().transform(loc, null);
+
+        // font
+        Font font = makeFont(aci);
+        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);
+        
+        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
+        try {
+            try {
+                nativeTextHandler.drawString(txt, (float)(loc.getX() + tx), (float)(loc.getY()));
+            } catch (IOException ioe) {
+                if (g2d instanceof AFPGraphics2D) {
+                    ((AFPGraphics2D)g2d).handleIOException(ioe);
+                }
+            }
+        } finally {
+            nativeTextHandler.setOverrideFont(null);
+        }
+        loc.setLocation(loc.getX() + (double)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))
+                       ? "italic" 
+                       : "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 makeFont(AttributedCharacterIterator aci) {
+        Float fontSize = (Float)aci.getAttribute(TextAttribute.SIZE);
+        if (fontSize == null) {
+            fontSize = new Float(10.0f);
+        }
+        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 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) {
+        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) {
+        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) {
+        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) {
+        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) {
+        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) {
+        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) {
+        return null;
+    }
+
+}
index d76e26d57c0191555aa16f994d0c246bfc42ab51..81d00bd1035eb14d5518e26620dfa409df23a289 100644 (file)
@@ -33,7 +33,9 @@ import org.apache.fop.afp.AFPObjectAreaInfo;
 import org.apache.fop.afp.AFPResourceInfo;
 import org.apache.fop.afp.AFPResourceLevel;
 import org.apache.fop.afp.AFPState;
+import org.apache.fop.afp.AFPTextElementBridge;
 import org.apache.fop.afp.AFPTextHandler;
+import org.apache.fop.afp.AFPTextPainter;
 import org.apache.fop.image.loader.batik.GenericGraphics2DImagePainter;
 import org.apache.fop.render.RendererContext;
 import org.apache.fop.svg.SVGUserAgent;
index 57837fb4f3c384d7088ab9bac67149c86f7894f8..de2786215095df578024f88ca536931d365a5740 100644 (file)
@@ -39,7 +39,9 @@ import org.apache.fop.afp.AFPObjectAreaInfo;
 import org.apache.fop.afp.AFPResourceInfo;
 import org.apache.fop.afp.AFPResourceManager;
 import org.apache.fop.afp.AFPState;
+import org.apache.fop.afp.AFPTextElementBridge;
 import org.apache.fop.afp.AFPTextHandler;
+import org.apache.fop.afp.AFPTextPainter;
 import org.apache.fop.afp.AFPUnitConverter;
 import org.apache.fop.fo.extensions.ExtensionElementMapping;
 import org.apache.fop.render.AbstractGenericSVGHandler;
diff --git a/src/java/org/apache/fop/render/afp/AFPTextElementBridge.java b/src/java/org/apache/fop/render/afp/AFPTextElementBridge.java
deleted file mode 100644 (file)
index fc26801..0000000
+++ /dev/null
@@ -1,110 +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.render.afp;
-
-import org.apache.batik.bridge.SVGTextElementBridge;
-import org.apache.batik.bridge.BridgeContext;
-import org.apache.batik.gvt.GraphicsNode;
-import org.apache.batik.gvt.TextNode;
-import org.apache.batik.gvt.TextPainter;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-/**
- * Bridge class for the &lt;text> element.
- * This bridge will use the direct text painter if the text
- * for the element is simple.
- */
-public class AFPTextElementBridge extends SVGTextElementBridge {
-    
-    private AFPTextPainter textPainter;
-
-    /**
-     * Constructs a new bridge for the &lt;text> element.
-     * @param textPainter the text painter to use
-     */
-    public AFPTextElementBridge(AFPTextPainter textPainter) {
-        this.textPainter = textPainter;
-    }
-
-    /**
-     * Create a text element bridge.
-     * This set the text painter on the node if the text is simple.
-     * @param ctx the bridge context
-     * @param e the svg element
-     * @return the text graphics node created by the super class
-     */
-    public GraphicsNode createGraphicsNode(BridgeContext ctx, Element e) {
-        GraphicsNode node = super.createGraphicsNode(ctx, e);
-        if (node != null && isSimple(ctx, e, node)) {
-            ((TextNode)node).setTextPainter(getTextPainter());
-        }
-        return node;
-    }
-
-    private TextPainter getTextPainter() {
-        return this.textPainter;
-    }
-
-    /**
-     * Check if text element contains simple text.
-     * This checks the children of the text element to determine
-     * if the text is simple. The text is simple if it can be rendered
-     * with basic text drawing algorithms. This means there are no
-     * alternate characters, the font is known and there are no effects
-     * applied to the text.
-     *
-     * @param ctx the bridge context
-     * @param element the svg text element
-     * @param node the graphics node
-     * @return true if this text is simple of false if it cannot be
-     *         easily rendered using normal drawString on the PDFGraphics2D
-     */
-    private boolean isSimple(BridgeContext ctx, Element element, GraphicsNode node) {
-        for (Node n = element.getFirstChild();
-                n != null;
-                n = n.getNextSibling()) {
-
-            switch (n.getNodeType()) {
-            case Node.ELEMENT_NODE:
-
-                if (n.getLocalName().equals(SVG_TSPAN_TAG)
-                        || n.getLocalName().equals(SVG_ALT_GLYPH_TAG)) {
-                    return false;
-                } else if (n.getLocalName().equals(SVG_TEXT_PATH_TAG)) {
-                    return false;
-                } else if (n.getLocalName().equals(SVG_TREF_TAG)) {
-                    return false;
-                }
-                break;
-            case Node.TEXT_NODE:
-            case Node.CDATA_SECTION_NODE:
-            default:
-            }
-        }
-
-        /*if (CSSUtilities.convertFilter(element, node, ctx) != null) {
-            return false;
-        }*/
-
-        return true;
-    }
-}
-
diff --git a/src/java/org/apache/fop/render/afp/AFPTextPainter.java b/src/java/org/apache/fop/render/afp/AFPTextPainter.java
deleted file mode 100644 (file)
index 7cc96d0..0000000
+++ /dev/null
@@ -1,517 +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.render.afp;
-
-import java.awt.Graphics2D;
-import java.awt.geom.Point2D;
-import java.awt.geom.Rectangle2D;
-import java.text.AttributedCharacterIterator;
-import java.text.CharacterIterator;
-import java.awt.font.TextAttribute;
-import java.awt.Shape;
-import java.awt.Paint;
-import java.awt.Color;
-import java.io.IOException;
-import java.util.List;
-import java.util.Iterator;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.fop.afp.AFPGraphics2D;
-import org.apache.fop.afp.AFPTextHandler;
-import org.apache.fop.fonts.Font;
-import org.apache.fop.fonts.FontInfo;
-import org.apache.fop.fonts.FontTriplet;
-
-import org.apache.batik.dom.svg.SVGOMTextElement;
-import org.apache.batik.gvt.text.Mark;
-import org.apache.batik.gvt.TextPainter;
-import org.apache.batik.gvt.TextNode;
-import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
-import org.apache.batik.gvt.text.TextPaintInfo;
-import org.apache.batik.gvt.font.GVTFontFamily;
-import org.apache.batik.gvt.renderer.StrokingTextPainter;
-
-
-/**
- * Renders the attributed character iterator of a <tt>TextNode</tt>.
- * This class draws the text directly into the AFPGraphics2D 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 class AFPTextPainter implements TextPainter {
-    
-    /** the logger for this class */
-    protected Log log = LogFactory.getLog(AFPTextPainter.class);
-    
-    private 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();
-
-    /**
-     * Create a new PS 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;
-        
-        String text = getText(aci);
-        Font font = makeFont(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);
-        loc = g2d.getTransform().transform(loc, null);
-
-        // font
-        Font font = makeFont(aci);
-        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);
-        
-        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
-        try {
-            try {
-                nativeTextHandler.drawString(txt, (float)(loc.getX() + tx), (float)(loc.getY()));
-            } catch (IOException ioe) {
-                if (g2d instanceof AFPGraphics2D) {
-                    ((AFPGraphics2D)g2d).handleIOException(ioe);
-                }
-            }
-        } finally {
-            nativeTextHandler.setOverrideFont(null);
-        }
-        loc.setLocation(loc.getX() + (double)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))
-                       ? "italic" 
-                       : "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 makeFont(AttributedCharacterIterator aci) {
-        Float fontSize = (Float)aci.getAttribute(TextAttribute.SIZE);
-        if (fontSize == null) {
-            fontSize = new Float(10.0f);
-        }
-        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 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) {
-        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) {
-        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) {
-        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) {
-        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) {
-        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) {
-        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) {
-        return null;
-    }
-
-}