diff options
author | Vincent Hennebert <vhennebert@apache.org> | 2013-07-29 21:45:20 +0000 |
---|---|---|
committer | Vincent Hennebert <vhennebert@apache.org> | 2013-07-29 21:45:20 +0000 |
commit | f8e822efe1de8bd8192dbb8ff035b9a79f876614 (patch) | |
tree | 8d38f873dd101e6ed0c2ba3b4dfa29e1eb0fdc60 /src/java/org/apache/fop/svg/AbstractFOPTextPainter.java | |
parent | c0b99ad44d0e1409008886e2f687c46f4ac05d9d (diff) | |
download | xmlgraphics-fop-f8e822efe1de8bd8192dbb8ff035b9a79f876614.tar.gz xmlgraphics-fop-f8e822efe1de8bd8192dbb8ff035b9a79f876614.zip |
Directly use FOP fonts to lay out SVG images for PDF, PS and AFP outputs.
The metrics are now taken from FOP configured fonts and no longer from AWT equivalents. That avoids discrepancies in case AWT and FOP use slightly different fonts, or if the font is not installed on the system. That actually also avoids having to install the font on the system.
FOP is also used for the primary layout of text (prior to SVG-specific transforms like translation or rotation) for consistency between SVG and XSL-FO.
This is a joint work from Peter Hancock and myself.
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_FopFontsForSVG@1508208 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/java/org/apache/fop/svg/AbstractFOPTextPainter.java')
-rw-r--r-- | src/java/org/apache/fop/svg/AbstractFOPTextPainter.java | 331 |
1 files changed, 92 insertions, 239 deletions
diff --git a/src/java/org/apache/fop/svg/AbstractFOPTextPainter.java b/src/java/org/apache/fop/svg/AbstractFOPTextPainter.java index 3e16799f4..7df127d3a 100644 --- a/src/java/org/apache/fop/svg/AbstractFOPTextPainter.java +++ b/src/java/org/apache/fop/svg/AbstractFOPTextPainter.java @@ -23,20 +23,15 @@ 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.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.batik.dom.svg.SVGOMTextElement; import org.apache.batik.gvt.TextNode; import org.apache.batik.gvt.TextPainter; import org.apache.batik.gvt.renderer.StrokingTextPainter; @@ -66,15 +61,15 @@ public abstract class AbstractFOPTextPainter implements TextPainter { * 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(); + private final TextPainter proxyTextPainter; /** * Create a new PS text painter with the given font information. * @param nativeTextHandler the NativeTextHandler instance used for text painting */ - public AbstractFOPTextPainter(FOPTextHandler nativeTextHandler) { + public AbstractFOPTextPainter(FOPTextHandler nativeTextHandler, TextPainter proxyTextPainter) { this.nativeTextHandler = nativeTextHandler; + this.proxyTextPainter = proxyTextPainter; } /** @@ -85,19 +80,10 @@ public abstract class AbstractFOPTextPainter implements TextPainter { * @param g2d the Graphics2D to use */ public void paint(TextNode node, Graphics2D g2d) { - Point2D loc = node.getLocation(); - if (!isSupportedGraphics2D(g2d) || hasUnsupportedAttributes(node)) { - if (log.isDebugEnabled()) { - log.debug("painting text node " + node - + " by stroking due to unsupported attributes or an incompatible Graphics2D"); - } - PROXY_PAINTER.paint(node, g2d); - } else { - if (log.isDebugEnabled()) { - log.debug("painting text node " + node + " normally."); - } - paintTextRuns(node.getTextRuns(), g2d, loc); + if (isSupportedGraphics2D(g2d)) { + new TextRunPainter().paintTextRuns(node.getTextRuns(), g2d, node.getLocation()); } + proxyTextPainter.paint(node, g2d); } /** @@ -109,190 +95,99 @@ public abstract class AbstractFOPTextPainter implements TextPainter { */ protected abstract boolean isSupportedGraphics2D(Graphics2D g2d); - 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; - } + private class TextRunPainter { - 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; - } + private Point2D currentLocation; - //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; + public void paintTextRuns(Iterable<StrokingTextPainter.TextRun> textRuns, Graphics2D g2d, + Point2D nodeLocation) { + currentLocation = new Point2D.Double(nodeLocation.getX(), nodeLocation.getY()); + for (StrokingTextPainter.TextRun run : textRuns) { + paintTextRun(run, g2d); } } - 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 anchor - TextNode.Anchor anchor = (TextNode.Anchor)aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.ANCHOR_TYPE); - - // text - String txt = getText(aci); - float advance = getStringWidth(txt, font); - float tx = 0; - if (anchor != null) { - switch (anchor.getType()) { - case TextNode.Anchor.ANCHOR_MIDDLE: - tx = -advance / 2; - break; - case TextNode.Anchor.ANCHOR_END: - tx = -advance; - break; - default: //nop + private void paintTextRun(StrokingTextPainter.TextRun run, Graphics2D g2d) { + AttributedCharacterIterator aci = run.getACI(); + aci.first(); + updateLocationFromACI(aci, currentLocation); + // font + Font font = getFont(aci); + if (font != null) { + nativeTextHandler.setOverrideFont(font); } - } - - // draw string - double x = loc.getX(); - double y = loc.getY(); - try { + // color + TextPaintInfo tpi = (TextPaintInfo) aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO); + if (tpi == null) { + return; + } + Paint foreground = tpi.fillPaint; + if (foreground instanceof Color) { + Color col = (Color) foreground; + g2d.setColor(col); + } + g2d.setPaint(foreground); + // text anchor + TextNode.Anchor anchor = (TextNode.Anchor) aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.ANCHOR_TYPE); + // text + String txt = getText(aci); + double advance = font == null ? run.getLayout().getAdvance2D().getX() : getStringWidth(txt, font); + double tx = 0; + 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 + Point2D outputLocation = g2d.getTransform().transform(currentLocation, null); + double x = outputLocation.getX(); + double y = outputLocation.getY(); try { - nativeTextHandler.drawString(g2d, txt, (float)x + tx, (float)y); - } catch (IOException ioe) { - if (g2d instanceof AFPGraphics2D) { - ((AFPGraphics2D)g2d).handleIOException(ioe); + try { + //TODO draw underline and overline if set + nativeTextHandler.drawString(g2d, txt, (float) (x + tx), (float) y); + //TODO draw strikethrough if set + } catch (IOException ioe) { + if (g2d instanceof AFPGraphics2D) { + ((AFPGraphics2D) g2d).handleIOException(ioe); + } } + } finally { + nativeTextHandler.setOverrideFont(null); + } + currentLocation.setLocation(currentLocation.getX() + advance, currentLocation.getY()); + } + 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()); } - } finally { - nativeTextHandler.setOverrideFont(null); } - loc.setLocation(loc.getX() + advance, loc.getY()); - return loc; } /** @@ -305,36 +200,9 @@ public abstract class AbstractFOPTextPainter implements TextPainter { for (char c = aci.first(); c != CharacterIterator.DONE; c = aci.next()) { sb.append(c); } - aci.first(); 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 Font getFont(AttributedCharacterIterator aci) { Font[] fonts = ACIUtils.findFontsForBatikACI(aci, nativeTextHandler.getFontInfo()); return fonts == null ? null : fonts[0]; @@ -360,21 +228,6 @@ public abstract class AbstractFOPTextPainter implements TextPainter { return wordWidth / 1000f; } - private boolean hasUnsupportedGlyphs(String str, Font font) { - if (font == null) { - return true; - } - 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 @@ -384,7 +237,7 @@ public abstract class AbstractFOPTextPainter implements TextPainter { * @return the outline shape of the text characters */ public Shape getOutline(TextNode node) { - return PROXY_PAINTER.getOutline(node); + return proxyTextPainter.getOutline(node); } /** @@ -399,7 +252,7 @@ public abstract class AbstractFOPTextPainter implements TextPainter { /* (todo) getBounds2D() is too slow * because it uses the StrokingTextPainter. We should implement this * method ourselves. */ - return PROXY_PAINTER.getBounds2D(node); + return proxyTextPainter.getBounds2D(node); } /** @@ -411,7 +264,7 @@ public abstract class AbstractFOPTextPainter implements TextPainter { * @return the bounds of the text */ public Rectangle2D getGeometryBounds(TextNode node) { - return PROXY_PAINTER.getGeometryBounds(node); + return proxyTextPainter.getGeometryBounds(node); } // Methods that have no purpose for PS |