protected Point2D adjustChunkOffsets()

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);
    }