From: Glenn Adams Date: Sat, 20 Sep 2014 19:02:18 +0000 (+0000) Subject: FOP-2391: fix NSM reordering issue and ensure same PDF output as XSL-FO code path... X-Git-Tag: fop-2_0~52 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=ec1da9e25ca22853d9bd9be9d4bb6a39dea58ec3;p=xmlgraphics-fop.git FOP-2391: fix NSM reordering issue and ensure same PDF output as XSL-FO code path (using Td/Tj operators) git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1626491 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/lib/batik-all-trunk.jar b/lib/batik-all-trunk.jar index cdf423164..5aac032d7 100644 Binary files a/lib/batik-all-trunk.jar and b/lib/batik-all-trunk.jar differ diff --git a/src/java/org/apache/fop/fonts/Font.java b/src/java/org/apache/fop/fonts/Font.java index 305e8f78a..399be056a 100644 --- a/src/java/org/apache/fop/fonts/Font.java +++ b/src/java/org/apache/fop/fonts/Font.java @@ -102,6 +102,14 @@ public class Font implements Substitutable, Positionable { return this.metric; } + /** + * Determines whether the font is a multibyte font. + * @return True if it is multibyte + */ + public boolean isMultiByte() { + return getFontMetrics().isMultiByte(); + } + /** * Returns the font's ascender. * @return the ascender diff --git a/src/java/org/apache/fop/fonts/FontMetrics.java b/src/java/org/apache/fop/fonts/FontMetrics.java index 4b1fb1f1f..ce00e34b9 100644 --- a/src/java/org/apache/fop/fonts/FontMetrics.java +++ b/src/java/org/apache/fop/fonts/FontMetrics.java @@ -190,4 +190,11 @@ public interface FontMetrics { * @return true if feature supported (and has at least one lookup) */ boolean hasFeature(int tableType, String script, String language, String feature); + + /** + * Determines whether the font is a multibyte font. + * @return True if it is multibyte + */ + boolean isMultiByte(); + } diff --git a/src/java/org/apache/fop/svg/NativeTextPainter.java b/src/java/org/apache/fop/svg/NativeTextPainter.java index 18c140163..cb96368c8 100644 --- a/src/java/org/apache/fop/svg/NativeTextPainter.java +++ b/src/java/org/apache/fop/svg/NativeTextPainter.java @@ -51,6 +51,7 @@ import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontInfo; import org.apache.fop.svg.font.FOPFontFamilyResolverImpl; import org.apache.fop.svg.font.FOPGVTFont; +import org.apache.fop.svg.font.FOPGVTGlyphVector; import org.apache.fop.svg.text.BidiAttributedCharacterIterator; import org.apache.fop.svg.text.ComplexGlyphLayout; import org.apache.fop.util.CharUtilities; @@ -64,8 +65,6 @@ public abstract class NativeTextPainter extends StrokingTextPainter { /** the logger for this class */ protected static final Log log = LogFactory.getLog(NativeTextPainter.class); - private static final boolean DEBUG = false; - /** the font collection */ protected final FontInfo fontInfo; @@ -99,9 +98,9 @@ public abstract class NativeTextPainter extends StrokingTextPainter { * @throws IOException if an I/O error occurs while rendering the text */ protected final void paintTextRun(TextRun textRun, Graphics2D g2d) throws IOException { + logTextRun(textRun); AttributedCharacterIterator runaci = textRun.getACI(); runaci.first(); - tpi = (TextPaintInfo) runaci.getAttribute(PAINT_INFO); if (tpi == null || !tpi.visible) { return; @@ -109,36 +108,36 @@ public abstract class NativeTextPainter extends StrokingTextPainter { if (tpi.composite != null) { g2d.setComposite(tpi.composite); } - - //------------------------------------ TextSpanLayout layout = textRun.getLayout(); - logTextRun(runaci, layout); - runaci.first(); //Reset ACI - - GeneralPath debugShapes = null; - if (DEBUG) { - debugShapes = new GeneralPath(); - } - - preparePainting(g2d); - GVTGlyphVector gv = layout.getGlyphVector(); if (!(gv.getFont() instanceof FOPGVTFont)) { assert gv.getFont() == null || gv.getFont() instanceof SVGGVTFont; //Draw using Java2D when no native fonts are available textRun.getLayout().draw(g2d); return; + } else { + GeneralPath debugShapes = log.isDebugEnabled() ? new GeneralPath() : null; + preparePainting(g2d); + saveGraphicsState(); + setInitialTransform(g2d.getTransform()); + clip(g2d.getClip()); + beginTextObject(); + writeGlyphs((FOPGVTGlyphVector) gv, debugShapes); + endTextObject(); + restoreGraphicsState(); + if (debugShapes != null) { + g2d.setStroke(new BasicStroke(0)); + g2d.setColor(Color.LIGHT_GRAY); + g2d.draw(debugShapes); + } } - font = ((FOPGVTFont) gv.getFont()).getFont(); - - saveGraphicsState(); - setInitialTransform(g2d.getTransform()); - clip(g2d.getClip()); - beginTextObject(); + } + protected void writeGlyphs(FOPGVTGlyphVector gv, GeneralPath debugShapes) throws IOException { AffineTransform localTransform = new AffineTransform(); Point2D prevPos = null; AffineTransform prevGlyphTransform = null; + font = ((FOPGVTFont) gv.getFont()).getFont(); for (int index = 0, c = gv.getNumGlyphs(); index < c; index++) { if (!gv.isGlyphVisible(index)) { continue; @@ -149,7 +148,7 @@ public abstract class NativeTextPainter extends StrokingTextPainter { if (log.isTraceEnabled()) { log.trace("pos " + glyphPos + ", transform " + glyphTransform); } - if (DEBUG) { + if (debugShapes != null) { Shape sh = gv.getGlyphLogicalBounds(index); if (sh == null) { sh = new Ellipse2D.Double(glyphPos.getX(), glyphPos.getY(), 2, 2); @@ -173,14 +172,6 @@ public abstract class NativeTextPainter extends StrokingTextPainter { writeGlyph(glyph, localTransform); } - endTextObject(); - restoreGraphicsState(); - if (DEBUG) { - //Paint debug shapes - g2d.setStroke(new BasicStroke(0)); - g2d.setColor(Color.LIGHT_GRAY); - g2d.draw(debugShapes); - } } @Override @@ -348,7 +339,10 @@ public abstract class NativeTextPainter extends StrokingTextPainter { * @param runaci an attributed character iterator * @param layout a text span layout */ - protected final void logTextRun(AttributedCharacterIterator runaci, TextSpanLayout layout) { + protected final void logTextRun(TextRun textRun) { + AttributedCharacterIterator runaci = textRun.getACI(); + TextSpanLayout layout = textRun.getLayout(); + runaci.first(); if (log.isTraceEnabled()) { int charCount = runaci.getEndIndex() - runaci.getBeginIndex(); log.trace("================================================"); diff --git a/src/java/org/apache/fop/svg/PDFTextPainter.java b/src/java/org/apache/fop/svg/PDFTextPainter.java index c5fa9f04e..0320438f8 100644 --- a/src/java/org/apache/fop/svg/PDFTextPainter.java +++ b/src/java/org/apache/fop/svg/PDFTextPainter.java @@ -25,11 +25,16 @@ import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; import java.awt.geom.Point2D; +import java.io.IOException; import org.apache.batik.gvt.text.TextPaintInfo; +import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontInfo; +import org.apache.fop.svg.font.FOPGVTFont; +import org.apache.fop.svg.font.FOPGVTGlyphVector; /** * Renders the attributed character iterator of a {@link org.apache.batik.gvt.TextNode}. @@ -104,6 +109,46 @@ class PDFTextPainter extends NativeTextPainter { pdf.writeClip(clip); } + private static int[] paZero = new int[4]; + + protected void writeGlyphs(FOPGVTGlyphVector gv, GeneralPath debugShapes) throws IOException { + if (gv.getGlyphPositionAdjustments() == null) { + super.writeGlyphs(gv, debugShapes); + } else { + FOPGVTFont gvtFont = (FOPGVTFont) gv.getFont(); + String fk = gvtFont.getFontKey(); + Font f = gvtFont.getFont(); + Point2D initialPos = gv.getGlyphPosition(0); + if (f.isMultiByte()) { + int fs = f.getFontSize(); + float fsPoints = fs / 1000f; + double xc = 0f; + double yc = 0f; + double xoLast = 0f; + double yoLast = 0f; + textUtil.writeTextMatrix(new AffineTransform(1, 0, 0, -1, initialPos.getX(), initialPos.getY())); + textUtil.updateTf(fk, fsPoints, true); + int[][] dp = gv.getGlyphPositionAdjustments(); + for (int i = 0, n = gv.getNumGlyphs(); i < n; i++) { + int gc = gv.getGlyphCode(i); + int[] pa = ((i > dp.length) || (dp[i] == null)) ? paZero : dp[i]; + double xo = xc + pa[0]; + double yo = yc + pa[1]; + double xa = f.getWidth(gc); + double ya = 0; + double xd = (xo - xoLast) / 1000f; + double yd = (yo - yoLast) / 1000f; + textUtil.writeTd(xd, yd); + textUtil.writeTj((char) gc); + xc += xa + pa[2]; + yc += ya + pa[3]; + xoLast = xo; + yoLast = yo; + } + } + } + } + @Override protected void beginTextObject() { applyColorAndPaint(tpi); diff --git a/src/java/org/apache/fop/svg/font/ComplexGlyphVector.java b/src/java/org/apache/fop/svg/font/ComplexGlyphVector.java index 8fa705e4c..567f9726a 100644 --- a/src/java/org/apache/fop/svg/font/ComplexGlyphVector.java +++ b/src/java/org/apache/fop/svg/font/ComplexGlyphVector.java @@ -64,6 +64,9 @@ class ComplexGlyphVector extends FOPGVTGlyphVector { if (associations != null) { Collections.reverse(associations); } + if (gposAdjustments != null) { + reverse(gposAdjustments); + } if (positions != null) { reverse(positions); } @@ -139,6 +142,15 @@ class ComplexGlyphVector extends FOPGVTGlyphVector { } } + private static void reverse(int[][] iaa) { + for (int i = 0, n = iaa.length, m = n / 2; i < m; i++) { + int k = n - i - 1; + int[] t = iaa [ k ]; + iaa [ k ] = iaa [ i ]; + iaa [ i ] = t; + } + } + private static void reverse(float[] fa) { int skip = 2; int numPositions = fa.length / skip; @@ -153,8 +165,12 @@ class ComplexGlyphVector extends FOPGVTGlyphVector { } } float runAdvanceX = fa [ 0 ]; - for (int i = 0, n = fa.length; i < n; i += 2) { - fa [ i ] = runAdvanceX - fa [ i ]; + for (int i = 0, n = numPositions; i < n; ++i) { + int k = i * 2; + fa [ k + 0 ] = runAdvanceX - fa [ k + 0 ]; + if (i > 0) { + fa [ k - 1 ] = fa [ k + 1 ]; + } } } diff --git a/src/java/org/apache/fop/svg/font/FOPGVTFont.java b/src/java/org/apache/fop/svg/font/FOPGVTFont.java index 76f77d367..fd92e36bc 100644 --- a/src/java/org/apache/fop/svg/font/FOPGVTFont.java +++ b/src/java/org/apache/fop/svg/font/FOPGVTFont.java @@ -31,7 +31,9 @@ import org.apache.batik.gvt.font.GVTLineMetrics; import org.apache.batik.gvt.text.GVTAttributedCharacterIterator; import org.apache.fop.fonts.Font; +import org.apache.fop.fonts.FontInfo; import org.apache.fop.fonts.FontMetrics; +import org.apache.fop.fonts.FontTriplet; public class FOPGVTFont implements GVTFont { @@ -122,6 +124,18 @@ public class FOPGVTFont implements GVTFont { throw new UnsupportedOperationException("Not implemented"); } + public FontInfo getFontInfo() { + return ((FOPGVTFontFamily) fontFamily).getFontInfo(); + } + + public String getFontKey() { + return ((FOPGVTFontFamily) fontFamily).getFontKey(); + } + + public FontTriplet getFontTriplet() { + return ((FOPGVTFontFamily) fontFamily).getFontTriplet(); + } + public String getFamilyName() { return fontFamily.getFamilyName(); } diff --git a/src/java/org/apache/fop/svg/font/FOPGVTFontFamily.java b/src/java/org/apache/fop/svg/font/FOPGVTFontFamily.java index 5be9419d3..b9351af44 100644 --- a/src/java/org/apache/fop/svg/font/FOPGVTFontFamily.java +++ b/src/java/org/apache/fop/svg/font/FOPGVTFontFamily.java @@ -47,6 +47,18 @@ public class FOPGVTFontFamily implements GVTFontFamily { this.fontFace = fontFace; } + public FontInfo getFontInfo() { + return fontInfo; + } + + public FontTriplet getFontTriplet() { + return fontTriplet; + } + + public String getFontKey() { + return fontInfo.getInternalFontKey(fontTriplet); + } + public String getFamilyName() { return familyName; } diff --git a/src/java/org/apache/fop/svg/font/FOPGVTGlyphVector.java b/src/java/org/apache/fop/svg/font/FOPGVTGlyphVector.java index 2b2115935..77bdec48d 100644 --- a/src/java/org/apache/fop/svg/font/FOPGVTGlyphVector.java +++ b/src/java/org/apache/fop/svg/font/FOPGVTGlyphVector.java @@ -47,7 +47,7 @@ import org.apache.fop.fonts.GlyphMapping; import org.apache.fop.fonts.TextFragment; import org.apache.fop.traits.MinOptMax; -class FOPGVTGlyphVector implements GVTGlyphVector { +public class FOPGVTGlyphVector implements GVTGlyphVector { protected final TextFragment text; @@ -63,6 +63,8 @@ class FOPGVTGlyphVector implements GVTGlyphVector { protected List associations; + protected int[][] gposAdjustments; + protected float[] positions; protected Rectangle2D[] boundingBoxes; @@ -94,6 +96,7 @@ class FOPGVTGlyphVector implements GVTGlyphVector { mapping.mapping != null ? new StringCharacterIterator(mapping.mapping) : text.getIterator(); this.glyphs = buildGlyphs(f, glyphAsCharIter); this.associations = mapping.associations; + this.gposAdjustments = mapping.gposAdjustments; this.positions = buildGlyphPositions(glyphAsCharIter, mapping.gposAdjustments, letterSpaceAdjustments); this.glyphVisibilities = new boolean[this.glyphs.length]; Arrays.fill(glyphVisibilities, true); @@ -304,6 +307,10 @@ class FOPGVTGlyphVector implements GVTGlyphVector { throw new UnsupportedOperationException(); } + public int[][] getGlyphPositionAdjustments() { + return gposAdjustments; + } + public Point2D getGlyphPosition(int glyphIndex) { int positionIndex = glyphIndex * 2; return new Point2D.Float(positions[positionIndex], positions[positionIndex + 1]);