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
* @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();
+
}
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;
/** 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;
* @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;
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;
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);
writeGlyph(glyph, localTransform);
}
- endTextObject();
- restoreGraphicsState();
- if (DEBUG) {
- //Paint debug shapes
- g2d.setStroke(new BasicStroke(0));
- g2d.setColor(Color.LIGHT_GRAY);
- g2d.draw(debugShapes);
- }
}
@Override
* @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("================================================");
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}.
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);
if (associations != null) {
Collections.reverse(associations);
}
+ if (gposAdjustments != null) {
+ reverse(gposAdjustments);
+ }
if (positions != null) {
reverse(positions);
}
}
}
+ 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;
}
}
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 ];
+ }
}
}
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 {
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();
}
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;
}
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;
protected List associations;
+ protected int[][] gposAdjustments;
+
protected float[] positions;
protected Rectangle2D[] boundingBoxes;
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);
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]);