diff options
author | Jeremias Maerki <jeremias@apache.org> | 2009-04-20 06:50:59 +0000 |
---|---|---|
committer | Jeremias Maerki <jeremias@apache.org> | 2009-04-20 06:50:59 +0000 |
commit | e471816c05743e7e1517710cedb5cc256901d3e6 (patch) | |
tree | ee9d62bf08460fcca71b28b24c5de80cf3a25e96 /src/java/org/apache/fop/svg/PDFTextPainter.java | |
parent | 5deedc814cedf4e1c2ffa746a22bdba34b031d5d (diff) | |
download | xmlgraphics-fop-e471816c05743e7e1517710cedb5cc256901d3e6.tar.gz xmlgraphics-fop-e471816c05743e7e1517710cedb5cc256901d3e6.zip |
Bugzilla #47000:
Added a custom text painter for rendering SVG text using text operators when rendering to PostScript or EPS. Text is no longer painted as shapes, thus creating much smaller files.
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@766594 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/java/org/apache/fop/svg/PDFTextPainter.java')
-rw-r--r-- | src/java/org/apache/fop/svg/PDFTextPainter.java | 363 |
1 files changed, 119 insertions, 244 deletions
diff --git a/src/java/org/apache/fop/svg/PDFTextPainter.java b/src/java/org/apache/fop/svg/PDFTextPainter.java index 85447a4f9..dddf61a6e 100644 --- a/src/java/org/apache/fop/svg/PDFTextPainter.java +++ b/src/java/org/apache/fop/svg/PDFTextPainter.java @@ -25,28 +25,18 @@ import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; -import java.awt.font.TextAttribute; import java.awt.geom.AffineTransform; import java.awt.geom.Ellipse2D; import java.awt.geom.GeneralPath; import java.awt.geom.Point2D; -import java.lang.reflect.Method; import java.text.AttributedCharacterIterator; -import java.util.Iterator; -import java.util.List; -import org.apache.batik.bridge.SVGFontFamily; -import org.apache.batik.gvt.font.GVTFont; -import org.apache.batik.gvt.font.GVTFontFamily; import org.apache.batik.gvt.font.GVTGlyphVector; -import org.apache.batik.gvt.renderer.StrokingTextPainter; -import org.apache.batik.gvt.text.GVTAttributedCharacterIterator; import org.apache.batik.gvt.text.TextPaintInfo; import org.apache.batik.gvt.text.TextSpanLayout; import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontInfo; -import org.apache.fop.fonts.FontTriplet; import org.apache.fop.util.CharUtilities; /** @@ -59,193 +49,159 @@ import org.apache.fop.util.CharUtilities; * * @version $Id$ */ -public class PDFTextPainter extends StrokingTextPainter { +class PDFTextPainter extends NativeTextPainter { private static final boolean DEBUG = false; - private final boolean strokeText = false; - private final FontInfo fontInfo; - /** * Create a new PDF text painter with the given font information. * @param fi the font info */ public PDFTextPainter(FontInfo fi) { - fontInfo = fi; + super(fi); } /** {@inheritDoc} */ - protected void paintTextRuns(List textRuns, Graphics2D g2d) { - if (DEBUG) { - System.out.println("paintTextRuns: count = " + textRuns.size()); - //fontInfo.dumpAllTripletsToSystemOut(); - } - if (!(g2d instanceof PDFGraphics2D) || strokeText) { - super.paintTextRuns(textRuns, g2d); + protected boolean isSupported(Graphics2D g2d) { + return g2d instanceof PDFGraphics2D; + } + + /** {@inheritDoc} */ + protected void paintTextRun(TextRun textRun, Graphics2D g2d) { + AttributedCharacterIterator runaci = textRun.getACI(); + runaci.first(); + + TextPaintInfo tpi = (TextPaintInfo)runaci.getAttribute(PAINT_INFO); + if (tpi == null || !tpi.visible) { return; } + if ((tpi != null) && (tpi.composite != null)) { + g2d.setComposite(tpi.composite); + } + + //------------------------------------ + TextSpanLayout layout = textRun.getLayout(); + logTextRun(runaci, layout); + CharSequence chars = collectCharacters(runaci); + runaci.first(); //Reset ACI + final PDFGraphics2D pdf = (PDFGraphics2D)g2d; PDFTextUtil textUtil = new PDFTextUtil(pdf.fontInfo) { protected void write(String code) { pdf.currentStream.write(code); } }; - for (int i = 0; i < textRuns.size(); i++) { - TextRun textRun = (TextRun)textRuns.get(i); - AttributedCharacterIterator runaci = textRun.getACI(); - runaci.first(); - TextPaintInfo tpi = (TextPaintInfo)runaci.getAttribute(PAINT_INFO); - if (tpi == null || !tpi.visible) { - continue; - } - if ((tpi != null) && (tpi.composite != null)) { - g2d.setComposite(tpi.composite); - } + if (DEBUG) { + log.debug("Text: " + chars); + pdf.currentStream.write("%Text: " + chars + "\n"); + } - //------------------------------------ - TextSpanLayout layout = textRun.getLayout(); - if (DEBUG) { - int charCount = runaci.getEndIndex() - runaci.getBeginIndex(); - System.out.println("================================================"); - System.out.println("New text run:"); - System.out.println("char count: " + charCount); - System.out.println("range: " - + runaci.getBeginIndex() + " - " + runaci.getEndIndex()); - System.out.println("glyph count: " + layout.getGlyphCount()); //=getNumGlyphs() - } - //Gather all characters of the run - StringBuffer chars = new StringBuffer(); - for (runaci.first(); runaci.getIndex() < runaci.getEndIndex();) { - chars.append(runaci.current()); - runaci.next(); - } - runaci.first(); - if (DEBUG) { - System.out.println("Text: " + chars); - pdf.currentStream.write("%Text: " + chars + "\n"); - } + GeneralPath debugShapes = null; + if (DEBUG) { + debugShapes = new GeneralPath(); + } - GeneralPath debugShapes = null; - if (DEBUG) { - debugShapes = new GeneralPath(); - } + Font[] fonts = findFonts(runaci); + if (fonts == null || fonts.length == 0) { + //Draw using Java2D when no native fonts are available + textRun.getLayout().draw(g2d); + return; + } - Font[] fonts = findFonts(runaci); - if (fonts == null || fonts.length == 0) { - //Draw using Java2D - textRun.getLayout().draw(g2d); + textUtil.saveGraphicsState(); + textUtil.concatMatrix(g2d.getTransform()); + Shape imclip = g2d.getClip(); + pdf.writeClip(imclip); + + applyColorAndPaint(tpi, pdf); + + textUtil.beginTextObject(); + textUtil.setFonts(fonts); + boolean stroke = (tpi.strokePaint != null) + && (tpi.strokeStroke != null); + textUtil.setTextRenderingMode(tpi.fillPaint != null, stroke, false); + + AffineTransform localTransform = new AffineTransform(); + Point2D prevPos = null; + double prevVisibleCharWidth = 0.0; + GVTGlyphVector gv = layout.getGlyphVector(); + for (int index = 0, c = gv.getNumGlyphs(); index < c; index++) { + char ch = chars.charAt(index); + boolean visibleChar = gv.isGlyphVisible(index) + || (CharUtilities.isAnySpace(ch) && !CharUtilities.isZeroWidthSpace(ch)); + logCharacter(ch, layout, index, visibleChar); + if (!visibleChar) { continue; } + Point2D glyphPos = gv.getGlyphPosition(index); - textUtil.saveGraphicsState(); - textUtil.concatMatrix(g2d.getTransform()); - Shape imclip = g2d.getClip(); - pdf.writeClip(imclip); - - applyColorAndPaint(tpi, pdf); - - textUtil.beginTextObject(); - textUtil.setFonts(fonts); - boolean stroke = (tpi.strokePaint != null) - && (tpi.strokeStroke != null); - textUtil.setTextRenderingMode(tpi.fillPaint != null, stroke, false); - - AffineTransform localTransform = new AffineTransform(); - Point2D prevPos = null; - double prevVisibleCharWidth = 0.0; - GVTGlyphVector gv = layout.getGlyphVector(); - for (int index = 0, c = gv.getNumGlyphs(); index < c; index++) { - char ch = chars.charAt(index); - boolean visibleChar = gv.isGlyphVisible(index) - || (CharUtilities.isAnySpace(ch) && !CharUtilities.isZeroWidthSpace(ch)); - if (DEBUG) { - System.out.println("glyph " + index - + " -> " + layout.getGlyphIndex(index) + " => " + ch); - if (CharUtilities.isAnySpace(ch) && ch != 32) { - System.out.println("Space found: " + Integer.toHexString(ch)); - } - if (ch == CharUtilities.ZERO_WIDTH_JOINER) { - System.out.println("ZWJ found: " + Integer.toHexString(ch)); - } - if (ch == CharUtilities.SOFT_HYPHEN) { - System.out.println("Soft hyphen found: " + Integer.toHexString(ch)); - } - if (!visibleChar) { - System.out.println("Invisible glyph found: " + Integer.toHexString(ch)); - } - } - if (!visibleChar) { - continue; - } - Point2D p = gv.getGlyphPosition(index); - - AffineTransform glyphTransform = gv.getGlyphTransform(index); - //TODO Glyph transforms could be refined so not every char has to be painted - //with its own TJ command (stretch/squeeze case could be optimized) - if (DEBUG) { - System.out.println("pos " + p + ", transform " + glyphTransform); - Shape sh; - sh = gv.getGlyphLogicalBounds(index); - if (sh == null) { - sh = new Ellipse2D.Double(p.getX(), p.getY(), 2, 2); - } - debugShapes.append(sh, false); + AffineTransform glyphTransform = gv.getGlyphTransform(index); + //TODO Glyph transforms could be refined so not every char has to be painted + //with its own TJ command (stretch/squeeze case could be optimized) + if (log.isTraceEnabled()) { + log.trace("pos " + glyphPos + ", transform " + glyphTransform); + } + if (DEBUG) { + Shape sh = gv.getGlyphLogicalBounds(index); + if (sh == null) { + sh = new Ellipse2D.Double(glyphPos.getX(), glyphPos.getY(), 2, 2); } + debugShapes.append(sh, false); + } - //Exact position of the glyph - localTransform.setToIdentity(); - localTransform.translate(p.getX(), p.getY()); - if (glyphTransform != null) { - localTransform.concatenate(glyphTransform); - } - localTransform.scale(1, -1); + //Exact position of the glyph + localTransform.setToIdentity(); + localTransform.translate(glyphPos.getX(), glyphPos.getY()); + if (glyphTransform != null) { + localTransform.concatenate(glyphTransform); + } + localTransform.scale(1, -1); - boolean yPosChanged = (prevPos == null - || prevPos.getY() != p.getY() - || glyphTransform != null); - if (yPosChanged) { - if (index > 0) { - textUtil.writeTJ(); - textUtil.writeTextMatrix(localTransform); - } - } else { - double xdiff = p.getX() - prevPos.getX(); - //Width of previous character - Font font = textUtil.getCurrentFont(); - double cw = prevVisibleCharWidth; - double effxdiff = (1000 * xdiff) - cw; - if (effxdiff != 0) { - double adjust = (-effxdiff / font.getFontSize()); - textUtil.adjustGlyphTJ(adjust * 1000); - } - if (DEBUG) { - System.out.println("==> x diff: " + xdiff + ", " + effxdiff - + ", charWidth: " + cw); - } - } - Font f = textUtil.selectFontForChar(ch); - if (f != textUtil.getCurrentFont()) { + boolean yPosChanged = (prevPos == null + || prevPos.getY() != glyphPos.getY() + || glyphTransform != null); + if (yPosChanged) { + if (index > 0) { textUtil.writeTJ(); - textUtil.setCurrentFont(f); - textUtil.writeTf(f); textUtil.writeTextMatrix(localTransform); } - char paintChar = (CharUtilities.isAnySpace(ch) ? ' ' : ch); - textUtil.writeTJChar(paintChar); - - //Update last position - prevPos = p; - prevVisibleCharWidth = textUtil.getCurrentFont().getCharWidth(chars.charAt(index)); + } else { + double xdiff = glyphPos.getX() - prevPos.getX(); + //Width of previous character + Font font = textUtil.getCurrentFont(); + double cw = prevVisibleCharWidth; + double effxdiff = (1000 * xdiff) - cw; + if (effxdiff != 0) { + double adjust = (-effxdiff / font.getFontSize()); + textUtil.adjustGlyphTJ(adjust * 1000); + } + if (log.isTraceEnabled()) { + log.trace("==> x diff: " + xdiff + ", " + effxdiff + + ", charWidth: " + cw); + } } - textUtil.writeTJ(); - textUtil.endTextObject(); - textUtil.restoreGraphicsState(); - if (DEBUG) { - g2d.setStroke(new BasicStroke(0)); - g2d.setColor(Color.LIGHT_GRAY); - g2d.draw(debugShapes); + Font f = textUtil.selectFontForChar(ch); + if (f != textUtil.getCurrentFont()) { + textUtil.writeTJ(); + textUtil.setCurrentFont(f); + textUtil.writeTf(f); + textUtil.writeTextMatrix(localTransform); } + char paintChar = (CharUtilities.isAnySpace(ch) ? ' ' : ch); + textUtil.writeTJChar(paintChar); + + //Update last position + prevPos = glyphPos; + prevVisibleCharWidth = textUtil.getCurrentFont().getCharWidth(chars.charAt(index)); + } + textUtil.writeTJ(); + textUtil.endTextObject(); + textUtil.restoreGraphicsState(); + if (DEBUG) { + g2d.setStroke(new BasicStroke(0)); + g2d.setColor(Color.LIGHT_GRAY); + g2d.draw(debugShapes); } } @@ -271,85 +227,4 @@ public class PDFTextPainter extends StrokingTextPainter { pdf.applyAlpha(fillAlpha, PDFGraphics2D.OPAQUE); } - private Font[] findFonts(AttributedCharacterIterator aci) { - List fonts = new java.util.ArrayList(); - List gvtFonts = (List) aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES); - Float posture = (Float) aci.getAttribute(TextAttribute.POSTURE); - Float taWeight = (Float) aci.getAttribute(TextAttribute.WEIGHT); - Float fontSize = (Float) aci.getAttribute(TextAttribute.SIZE); - - String style = ((posture != null) && (posture.floatValue() > 0.0)) - ? Font.STYLE_ITALIC : Font.STYLE_NORMAL; - int weight = ((taWeight != null) - && (taWeight.floatValue() > 1.0)) ? Font.WEIGHT_BOLD - : Font.WEIGHT_NORMAL; - - String firstFontFamily = null; - - //GVT_FONT can sometimes be different from the fonts in GVT_FONT_FAMILIES - //or GVT_FONT_FAMILIES can even be empty and only GVT_FONT is set - /* The following code section is not available until Batik 1.7 is released. */ - GVTFont gvtFont = (GVTFont)aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.GVT_FONT); - if (gvtFont != null) { - try { - Method method = gvtFont.getClass().getMethod("getFamilyName", null); - String gvtFontFamily = (String)method.invoke(gvtFont, null); - //TODO Uncomment the following line when Batik 1.7 is shipped with FOP - //String gvtFontFamily = gvtFont.getFamilyName(); //Not available in Batik 1.6 - if (DEBUG) { - System.out.print(gvtFontFamily + ", "); - } - if (fontInfo.hasFont(gvtFontFamily, style, weight)) { - FontTriplet triplet = fontInfo.fontLookup(gvtFontFamily, style, - weight); - int fsize = (int)(fontSize.floatValue() * 1000); - fonts.add(fontInfo.getFontInstance(triplet, fsize)); - } - firstFontFamily = gvtFontFamily; - } catch (Exception e) { - //Most likely NoSuchMethodError here when using Batik 1.6 - //Just skip this section in this case - } - } - - if (gvtFonts != null) { - Iterator i = gvtFonts.iterator(); - while (i.hasNext()) { - GVTFontFamily fam = (GVTFontFamily) i.next(); - if (fam instanceof SVGFontFamily) { - return null; //Let Batik paint this text! - } - String fontFamily = fam.getFamilyName(); - if (DEBUG) { - System.out.print(fontFamily + ", "); - } - if (fontInfo.hasFont(fontFamily, style, weight)) { - FontTriplet triplet = fontInfo.fontLookup(fontFamily, style, - weight); - int fsize = (int)(fontSize.floatValue() * 1000); - fonts.add(fontInfo.getFontInstance(triplet, fsize)); - } - if (firstFontFamily == null) { - firstFontFamily = fontFamily; - } - } - } - if (fonts.size() == 0) { - if (firstFontFamily == null) { - //This will probably never happen. Just to be on the safe side. - firstFontFamily = "any"; - } - //lookup with fallback possibility (incl. substitution notification) - FontTriplet triplet = fontInfo.fontLookup(firstFontFamily, style, weight); - int fsize = (int)(fontSize.floatValue() * 1000); - fonts.add(fontInfo.getFontInstance(triplet, fsize)); - } - if (DEBUG) { - System.out.println(); - } - return (Font[])fonts.toArray(new Font[fonts.size()]); - } - }
\ No newline at end of file |