in batik-bridge/src/main/java/org/apache/batik/bridge/StrokingTextPainter.java [671:851]
protected Point2D adjustChunkOffsets(Point2D location,
List textRuns,
TextChunk chunk) {
int numRuns = chunk.end - chunk.begin;
TextRun r = (TextRun) textRuns.get(0);
int anchorType = r.getAnchorType();
Float length = r.getLength();
Integer lengthAdj = r.getLengthAdjust();
boolean doAdjust = true;
if ((length == null) || length.isNaN())
doAdjust = false;
int numChars = 0;
for (int i = 0; i < numRuns; ++i) {
r = (TextRun) textRuns.get(i);
AttributedCharacterIterator aci = r.getACI();
numChars += aci.getEndIndex() - aci.getBeginIndex();
}
if ((lengthAdj ==
GVTAttributedCharacterIterator.TextAttribute.ADJUST_SPACING) &&
(numChars == 1))
doAdjust = false;
float xScale = 1;
float yScale = 1;
r = (TextRun)textRuns.get(numRuns - 1);
TextSpanLayout layout = r.getLayout();
GVTGlyphMetrics lastMetrics =
layout.getGlyphMetrics(layout.getGlyphCount()-1);
GVTLineMetrics lastLineMetrics = layout.getLineMetrics();
Rectangle2D lastBounds = lastMetrics.getBounds2D();
float halfLeading = (lastMetrics.getVerticalAdvance()-
(lastLineMetrics.getAscent() +
lastLineMetrics.getDescent()))/2;
float lastW = (float)(lastBounds.getWidth() + lastBounds.getX());
float lastH = (float)(halfLeading + lastLineMetrics.getAscent() +
(lastBounds.getHeight() + lastBounds.getY()));
Point2D visualAdvance;
if (!doAdjust) {
visualAdvance = new Point2D.Float(
(float) (chunk.advance.getX()),
(float) (chunk.advance.getY() + lastH - lastMetrics.getVerticalAdvance()));
} else {
Point2D advance = chunk.advance;
// We have to do this here since textLength needs to be
// handled at the text chunk level. Otherwise tspans get
// messed up.
if (layout.isVertical()) {
if (lengthAdj == ADJUST_SPACING) {
yScale = (float)
((length - lastH) /
(advance.getY() - lastMetrics.getVerticalAdvance()));
} else {
double adv =(advance.getY() + lastH -
lastMetrics.getVerticalAdvance());
yScale = (float)(length /adv);
}
visualAdvance = new Point2D.Float(0, length);
} else {
if (lengthAdj == ADJUST_SPACING) {
xScale = (float)
((length - lastW) /
(advance.getX() - lastMetrics.getHorizontalAdvance()));
} else {
double adv = (advance.getX() + lastW -
lastMetrics.getHorizontalAdvance());
xScale = (float)(length /adv);
}
visualAdvance = new Point2D.Float(length, 0);
}
Point2D.Float adv = new Point2D.Float(0,0);
for (int i = 0; i < numRuns; ++i) {
r = (TextRun) textRuns.get(i);
layout = r.getLayout();
layout.setScale(xScale, yScale, lengthAdj==ADJUST_SPACING);
Point2D lAdv = layout.getAdvance2D();
adv.x += (float)lAdv.getX();
adv.y += (float)lAdv.getY();
}
chunk.advance = adv;
}
float dx = 0f;
float dy = 0f;
switch(anchorType){
case TextNode.Anchor.ANCHOR_MIDDLE:
dx = (float) (-visualAdvance.getX()/2d);
dy = (float) (-visualAdvance.getY()/2d);
break;
case TextNode.Anchor.ANCHOR_END:
dx = (float) (-visualAdvance.getX());
dy = (float) (-visualAdvance.getY());
break;
default:
break; // leave untouched
}
r = (TextRun) textRuns.get(0);
layout = r.getLayout();
AttributedCharacterIterator runaci = r.getACI();
runaci.first();
boolean vertical = layout.isVertical();
Float runX = (Float) runaci.getAttribute(XPOS);
Float runY = (Float) runaci.getAttribute(YPOS);
TextPath textPath = (TextPath) runaci.getAttribute(TEXTPATH);
// The point that the next peice of normal text should be
// layed out from, only used for normal text not text on a path.
float absX = (float)location.getX();
float absY = (float)location.getY();
// TextPath Shift used to account for startOffset.
float tpShiftX = 0;
float tpShiftY = 0;
// Of course X and Y override that, but they don't apply for
// text on a path.
if ((runX != null) && (!runX.isNaN())) {
absX = runX;
tpShiftX = absX;
}
if ((runY != null) && (!runY.isNaN())) {
absY = runY;
tpShiftY = absY;
}
// Factor in text-anchor in writing direction.
// Ignore tpShift in non-writing direction.
if (vertical) {
absY += dy;
tpShiftY += dy;
tpShiftX = 0;
} else {
absX += dx;
tpShiftX += dx;
tpShiftY = 0;
}
for (int i = 0; i < numRuns; ++i) {
r = (TextRun) textRuns.get(i);
layout = r.getLayout();
runaci = r.getACI();
runaci.first();
textPath = (TextPath) runaci.getAttribute(TEXTPATH);
if (vertical) {
runX = (Float) runaci.getAttribute(XPOS);
if ((runX != null) && (!runX.isNaN())) {
absX = runX;
}
} else {
runY = (Float) runaci.getAttribute(YPOS);
if ((runY != null) && (!runY.isNaN())) {
absY = runY;
}
}
if (textPath == null) {
layout.setOffset(new Point2D.Float(absX, absY));
Point2D ladv = layout.getAdvance2D();
absX += ladv.getX();
absY += ladv.getY();
} else {
layout.setOffset(new Point2D.Float(tpShiftX, tpShiftY));
Point2D ladv = layout.getAdvance2D();
tpShiftX += (float)ladv.getX();
tpShiftY += (float)ladv.getY();
ladv = layout.getTextPathAdvance();
absX = (float)ladv.getX();
absY = (float)ladv.getY();
}
}
return new Point2D.Float(absX, absY);
}