logTextRun(runaci, layout);
CharSequence chars = collectCharacters(runaci);
runaci.first(); //Reset ACI
final PSGraphics2D ps = (PSGraphics2D)g2d;
final PSGenerator gen = ps.getPSGenerator();
ps.preparePainting();
if (DEBUG) {
log.debug("Text: " + chars);
gen.commentln("%Text: " + chars);
}
GeneralPath debugShapes = null;
if (DEBUG) {
debugShapes = new GeneralPath();
}
TextUtil textUtil = new TextUtil(gen);
textUtil.setupFonts(runaci);
if (!textUtil.hasFonts()) {
//Draw using Java2D when no native fonts are available
textRun.getLayout().draw(g2d);
return;
}
gen.saveGraphicsState();
gen.concatMatrix(g2d.getTransform());
Shape imclip = g2d.getClip();
clip(ps, imclip);
gen.writeln("BT"); //beginTextObject()
AffineTransform localTransform = new AffineTransform();
Point2D prevPos = null;
GVTGlyphVector gv = layout.getGlyphVector();
PSTextRun psRun = new PSTextRun(); //Used to split a text run into smaller runs
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);
AffineTransform glyphTransform = gv.getGlyphTransform(index);
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(glyphPos.getX(), glyphPos.getY());
if (glyphTransform != null) {
localTransform.concatenate(glyphTransform);
}
localTransform.scale(1, -1);
boolean flushCurrentRun = false;
//Try to optimize by combining characters using the same font and on the same line.
if (glyphTransform != null) {
//Happens for text-on-a-path
flushCurrentRun = true;
}
if (psRun.getRunLength() >= 128) {
//Don't let a run get too long
flushCurrentRun = true;
}
//Note the position of the glyph relative to the previous one
Point2D relPos;
if (prevPos == null) {
relPos = new Point2D.Double(0, 0);
} else {
relPos = new Point2D.Double(
glyphPos.getX() - prevPos.getX(),
glyphPos.getY() - prevPos.getY());
}
if (psRun.vertChanges == 0
&& psRun.getHorizRunLength() > 2
&& relPos.getY() != 0) {
//new line
flushCurrentRun = true;
}
//Select the actual character to paint
char paintChar = (CharUtilities.isAnySpace(ch) ? ' ' : ch);
//Select (sub)font for character
Font f = textUtil.selectFontForChar(paintChar);
char mapped = f.mapChar(ch);
boolean fontChanging = textUtil.isFontChanging(f, mapped);
if (fontChanging) {
flushCurrentRun = true;
}
if (flushCurrentRun) {
//Paint the current run and reset for the next run
psRun.paint(ps, textUtil, tpi);
psRun.reset();
}
//Track current run
psRun.addCharacter(paintChar, relPos);
psRun.noteStartingTransformation(localTransform);
//Change font if necessary
if (fontChanging) {
textUtil.setCurrentFont(f, mapped);
}
//Update last position
prevPos = glyphPos;
}
psRun.paint(ps, textUtil, tpi);
gen.writeln("ET"); //endTextObject()
gen.restoreGraphicsState();
if (DEBUG) {
//Paint debug shapes
g2d.setStroke(new BasicStroke(0));
g2d.setColor(Color.LIGHT_GRAY);