Source Code Cross Referenced for TextLine.java in  » 6.0-JDK-Core » AWT » java » awt » font » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Home
Java Source Code / Java Documentation
1.6.0 JDK Core
2.6.0 JDK Modules
3.6.0 JDK Modules com.sun
4.6.0 JDK Modules com.sun.java
5.6.0 JDK Modules sun
6.6.0 JDK Platform
7.Ajax
8.Apache Harmony Java SE
9.Aspect oriented
10.Authentication Authorization
11.Blogger System
12.Build
13.Byte Code
14.Cache
15.Chart
16.Chat
17.Code Analyzer
18.Collaboration
19.Content Management System
20.Database Client
21.Database DBMS
22.Database JDBC Connection Pool
23.Database ORM
24.Development
25.EJB Server
26.ERP CRM Financial
27.ESB
28.Forum
29.Game
30.GIS
31.Graphic 3D
32.Graphic Library
33.Groupware
34.HTML Parser
35.IDE
36.IDE Eclipse
37.IDE Netbeans
38.Installer
39.Internationalization Localization
40.Inversion of Control
41.Issue Tracking
42.J2EE
43.J2ME
44.JBoss
45.JMS
46.JMX
47.Library
48.Mail Clients
49.Music
50.Net
51.Parser
52.PDF
53.Portal
54.Profiler
55.Project Management
56.Report
57.RSS RDF
58.Rule Engine
59.Science
60.Scripting
61.Search Engine
62.Security
63.Sevlet Container
64.Source Control
65.Swing Library
66.Template Engine
67.Test Coverage
68.Testing
69.UML
70.Web Crawler
71.Web Framework
72.Web Mail
73.Web Server
74.Web Services
75.Web Services apache cxf 2.2.6
76.Web Services AXIS2
77.Wiki Engine
78.Workflow Engines
79.XML
80.XML UI
Java Source Code / Java Documentation » 6.0 JDK Core » AWT » java.awt.font 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001        /*
0002         * Copyright 1998-2006 Sun Microsystems, Inc.  All Rights Reserved.
0003         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004         *
0005         * This code is free software; you can redistribute it and/or modify it
0006         * under the terms of the GNU General Public License version 2 only, as
0007         * published by the Free Software Foundation.  Sun designates this
0008         * particular file as subject to the "Classpath" exception as provided
0009         * by Sun in the LICENSE file that accompanied this code.
0010         *
0011         * This code is distributed in the hope that it will be useful, but WITHOUT
0012         * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013         * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
0014         * version 2 for more details (a copy is included in the LICENSE file that
0015         * accompanied this code).
0016         *
0017         * You should have received a copy of the GNU General Public License version
0018         * 2 along with this work; if not, write to the Free Software Foundation,
0019         * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020         *
0021         * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022         * CA 95054 USA or visit www.sun.com if you need additional information or
0023         * have any questions.
0024         */
0025
0026        /*
0027         * (C) Copyright IBM Corp. 1998-2003, All Rights Reserved
0028         *
0029         */
0030
0031        package java.awt.font;
0032
0033        import java.awt.Color;
0034        import java.awt.Font;
0035        import java.awt.Graphics2D;
0036        import java.awt.Rectangle;
0037        import java.awt.Shape;
0038        import java.awt.geom.AffineTransform;
0039        import java.awt.geom.GeneralPath;
0040        import java.awt.geom.Point2D;
0041        import java.awt.geom.Rectangle2D;
0042        import java.awt.im.InputMethodHighlight;
0043        import java.awt.image.BufferedImage;
0044        import java.text.Annotation;
0045        import java.text.AttributedCharacterIterator;
0046        import java.text.Bidi;
0047        import java.text.CharacterIterator;
0048        import java.util.Hashtable;
0049        import java.util.Map;
0050        import sun.font.AttributeValues;
0051        import sun.font.BidiUtils;
0052        import sun.font.CoreMetrics;
0053        import sun.font.Decoration;
0054        import sun.font.FontLineMetrics;
0055        import sun.font.FontResolver;
0056        import sun.font.GraphicComponent;
0057        import sun.font.LayoutPathImpl;
0058        import sun.font.LayoutPathImpl.EmptyPath;
0059        import sun.font.LayoutPathImpl.SegmentPathBuilder;
0060        import sun.font.TextLabelFactory;
0061        import sun.font.TextLineComponent;
0062        import sun.text.CodePointIterator;
0063
0064        import java.awt.geom.Line2D;
0065
0066        final class TextLine {
0067
0068            static final class TextLineMetrics {
0069                public final float ascent;
0070                public final float descent;
0071                public final float leading;
0072                public final float advance;
0073
0074                public TextLineMetrics(float ascent, float descent,
0075                        float leading, float advance) {
0076                    this .ascent = ascent;
0077                    this .descent = descent;
0078                    this .leading = leading;
0079                    this .advance = advance;
0080                }
0081            }
0082
0083            private TextLineComponent[] fComponents;
0084            private float[] fBaselineOffsets;
0085            private int[] fComponentVisualOrder; // if null, ltr
0086            private float[] locs; // x,y pairs for components in visual order
0087            private char[] fChars;
0088            private int fCharsStart;
0089            private int fCharsLimit;
0090            private int[] fCharVisualOrder; // if null, ltr
0091            private int[] fCharLogicalOrder; // if null, ltr
0092            private byte[] fCharLevels; // if null, 0
0093            private boolean fIsDirectionLTR;
0094            private LayoutPathImpl lp;
0095            private boolean isSimple;
0096            private Rectangle pixelBounds;
0097            private FontRenderContext frc;
0098
0099            private TextLineMetrics fMetrics = null; // built on demand in getMetrics
0100
0101            public TextLine(FontRenderContext frc,
0102                    TextLineComponent[] components, float[] baselineOffsets,
0103                    char[] chars, int charsStart, int charsLimit,
0104                    int[] charLogicalOrder, byte[] charLevels,
0105                    boolean isDirectionLTR) {
0106
0107                int[] componentVisualOrder = computeComponentOrder(components,
0108                        charLogicalOrder);
0109
0110                this .frc = frc;
0111                fComponents = components;
0112                fBaselineOffsets = baselineOffsets;
0113                fComponentVisualOrder = componentVisualOrder;
0114                fChars = chars;
0115                fCharsStart = charsStart;
0116                fCharsLimit = charsLimit;
0117                fCharLogicalOrder = charLogicalOrder;
0118                fCharLevels = charLevels;
0119                fIsDirectionLTR = isDirectionLTR;
0120                checkCtorArgs();
0121
0122                init();
0123            }
0124
0125            private void checkCtorArgs() {
0126
0127                int checkCharCount = 0;
0128                for (int i = 0; i < fComponents.length; i++) {
0129                    checkCharCount += fComponents[i].getNumCharacters();
0130                }
0131
0132                if (checkCharCount != this .characterCount()) {
0133                    throw new IllegalArgumentException("Invalid TextLine!  "
0134                            + "char count is different from "
0135                            + "sum of char counts of components.");
0136                }
0137            }
0138
0139            private void init() {
0140
0141                // first, we need to check for graphic components on the TOP or BOTTOM baselines.  So
0142                // we perform the work that used to be in getMetrics here.
0143
0144                float ascent = 0;
0145                float descent = 0;
0146                float leading = 0;
0147                float advance = 0;
0148
0149                // ascent + descent must not be less than this value
0150                float maxGraphicHeight = 0;
0151                float maxGraphicHeightWithLeading = 0;
0152
0153                // walk through EGA's
0154                TextLineComponent tlc;
0155                boolean fitTopAndBottomGraphics = false;
0156
0157                isSimple = true;
0158
0159                for (int i = 0; i < fComponents.length; i++) {
0160                    tlc = fComponents[i];
0161
0162                    isSimple &= tlc.isSimple();
0163
0164                    CoreMetrics cm = tlc.getCoreMetrics();
0165
0166                    byte baseline = (byte) cm.baselineIndex;
0167
0168                    if (baseline >= 0) {
0169                        float baselineOffset = fBaselineOffsets[baseline];
0170
0171                        ascent = Math.max(ascent, -baselineOffset + cm.ascent);
0172
0173                        float gd = baselineOffset + cm.descent;
0174                        descent = Math.max(descent, gd);
0175
0176                        leading = Math.max(leading, gd + cm.leading);
0177                    } else {
0178                        fitTopAndBottomGraphics = true;
0179                        float graphicHeight = cm.ascent + cm.descent;
0180                        float graphicHeightWithLeading = graphicHeight
0181                                + cm.leading;
0182                        maxGraphicHeight = Math.max(maxGraphicHeight,
0183                                graphicHeight);
0184                        maxGraphicHeightWithLeading = Math.max(
0185                                maxGraphicHeightWithLeading,
0186                                graphicHeightWithLeading);
0187                    }
0188                }
0189
0190                if (fitTopAndBottomGraphics) {
0191                    if (maxGraphicHeight > ascent + descent) {
0192                        descent = maxGraphicHeight - ascent;
0193                    }
0194                    if (maxGraphicHeightWithLeading > ascent + leading) {
0195                        leading = maxGraphicHeightWithLeading - ascent;
0196                    }
0197                }
0198
0199                leading -= descent;
0200
0201                // we now know enough to compute the locs, but we need the final loc
0202                // for the advance before we can create the metrics object
0203
0204                if (fitTopAndBottomGraphics) {
0205                    // we have top or bottom baselines, so expand the baselines array
0206                    // full offsets are needed by CoreMetrics.effectiveBaselineOffset
0207                    fBaselineOffsets = new float[] { fBaselineOffsets[0],
0208                            fBaselineOffsets[1], fBaselineOffsets[2], descent,
0209                            -ascent };
0210                }
0211
0212                float x = 0;
0213                float y = 0;
0214                CoreMetrics pcm = null;
0215
0216                boolean needPath = false;
0217                locs = new float[fComponents.length * 2 + 2];
0218
0219                for (int i = 0, n = 0; i < fComponents.length; ++i, n += 2) {
0220                    int vi = fComponentVisualOrder == null ? i
0221                            : fComponentVisualOrder[i];
0222
0223                    tlc = fComponents[vi];
0224                    CoreMetrics cm = tlc.getCoreMetrics();
0225
0226                    if ((pcm != null)
0227                            && (pcm.italicAngle != 0 || cm.italicAngle != 0) && // adjust because of italics
0228                            (pcm.italicAngle != cm.italicAngle
0229                                    || pcm.baselineIndex != cm.baselineIndex || pcm.ssOffset != cm.ssOffset)) {
0230
0231                        // 1) compute the area of overlap - min effective ascent and min effective descent
0232                        // 2) compute the x positions along italic angle of ascent and descent for left and right
0233                        // 3) compute maximum left - right, adjust right position by this value
0234                        // this is a crude form of kerning between textcomponents
0235
0236                        // note glyphvectors preposition glyphs based on offset, 
0237                        // so tl doesn't need to adjust glyphvector position
0238                        // 1)
0239                        float pb = pcm
0240                                .effectiveBaselineOffset(fBaselineOffsets);
0241                        float pa = pb - pcm.ascent;
0242                        float pd = pb + pcm.descent;
0243                        // pb += pcm.ssOffset;
0244
0245                        float cb = cm.effectiveBaselineOffset(fBaselineOffsets);
0246                        float ca = cb - cm.ascent;
0247                        float cd = cb + cm.descent;
0248                        // cb += cm.ssOffset;
0249
0250                        float a = Math.max(pa, ca);
0251                        float d = Math.min(pd, cd);
0252
0253                        // 2)
0254                        float pax = pcm.italicAngle * (pb - a);
0255                        float pdx = pcm.italicAngle * (pb - d);
0256
0257                        float cax = cm.italicAngle * (cb - a);
0258                        float cdx = cm.italicAngle * (cb - d);
0259
0260                        // 3)
0261                        float dax = pax - cax;
0262                        float ddx = pdx - cdx;
0263                        float dx = Math.max(dax, ddx);
0264
0265                        x += dx;
0266                        y = cb;
0267                    } else {
0268                        // no italic adjustment for x, but still need to compute y
0269                        y = cm.effectiveBaselineOffset(fBaselineOffsets); // + cm.ssOffset;
0270                    }
0271
0272                    locs[n] = x;
0273                    locs[n + 1] = y;
0274
0275                    x += tlc.getAdvance();
0276                    pcm = cm;
0277
0278                    needPath |= tlc.getBaselineTransform() != null;
0279                }
0280
0281                // do we want italic padding at the right of the line?
0282                if (pcm.italicAngle != 0) {
0283                    float pb = pcm.effectiveBaselineOffset(fBaselineOffsets);
0284                    float pa = pb - pcm.ascent;
0285                    float pd = pb + pcm.descent;
0286                    pb += pcm.ssOffset;
0287
0288                    float d;
0289                    if (pcm.italicAngle > 0) {
0290                        d = pb + pcm.ascent;
0291                    } else {
0292                        d = pb - pcm.descent;
0293                    }
0294                    d *= pcm.italicAngle;
0295
0296                    x += d;
0297                }
0298                locs[locs.length - 2] = x;
0299                // locs[locs.length - 1] = 0; // final offset is always back on baseline
0300
0301                // ok, build fMetrics since we have the final advance
0302                advance = x;
0303                fMetrics = new TextLineMetrics(ascent, descent, leading,
0304                        advance);
0305
0306                // build path if we need it
0307                if (needPath) {
0308                    isSimple = false;
0309
0310                    Point2D.Double pt = new Point2D.Double();
0311                    double tx = 0, ty = 0;
0312                    SegmentPathBuilder builder = new SegmentPathBuilder();
0313                    builder.moveTo(locs[0], 0);
0314                    for (int i = 0, n = 0; i < fComponents.length; ++i, n += 2) {
0315                        int vi = fComponentVisualOrder == null ? i
0316                                : fComponentVisualOrder[i];
0317                        tlc = fComponents[vi];
0318                        AffineTransform at = tlc.getBaselineTransform();
0319                        if (at != null
0320                                && ((at.getType() & at.TYPE_TRANSLATION) != 0)) {
0321                            double dx = at.getTranslateX();
0322                            double dy = at.getTranslateY();
0323                            builder.moveTo(tx += dx, ty += dy);
0324                        }
0325                        pt.x = locs[n + 2] - locs[n];
0326                        pt.y = 0;
0327                        if (at != null) {
0328                            at.deltaTransform(pt, pt);
0329                        }
0330                        builder.lineTo(tx += pt.x, ty += pt.y);
0331                    }
0332                    lp = builder.complete();
0333
0334                    if (lp == null) { // empty path
0335                        int vi = fComponentVisualOrder == null ? 0
0336                                : fComponentVisualOrder[0];
0337                        tlc = fComponents[vi];
0338                        AffineTransform at = tlc.getBaselineTransform();
0339                        if (at != null) {
0340                            lp = new EmptyPath(at);
0341                        }
0342                    }
0343                }
0344            }
0345
0346            public Rectangle getPixelBounds(FontRenderContext frc, float x,
0347                    float y) {
0348                Rectangle result = null;
0349
0350                // if we have a matching frc, set it to null so we don't have to test it
0351                // for each component
0352                if (frc != null && frc.equals(this .frc)) {
0353                    frc = null;
0354                }
0355
0356                // only cache integral locations with the default frc, this is a bit strict
0357                int ix = (int) Math.floor(x);
0358                int iy = (int) Math.floor(y);
0359                float rx = x - ix;
0360                float ry = y - iy;
0361                boolean canCache = frc == null && rx == 0 && ry == 0;
0362
0363                if (canCache && pixelBounds != null) {
0364                    result = new Rectangle(pixelBounds);
0365                    result.x += ix;
0366                    result.y += iy;
0367                    return result;
0368                }
0369
0370                // couldn't use cache, or didn't have it, so compute
0371
0372                if (isSimple) { // all glyphvectors with no decorations, no layout path
0373                    for (int i = 0, n = 0; i < fComponents.length; i++, n += 2) {
0374                        int vi = fComponentVisualOrder == null ? i
0375                                : fComponentVisualOrder[i];
0376                        TextLineComponent tlc = fComponents[vi];
0377                        Rectangle pb = tlc.getPixelBounds(frc, locs[n] + rx,
0378                                locs[n + 1] + ry);
0379                        if (!pb.isEmpty()) {
0380                            if (result == null) {
0381                                result = pb;
0382                            } else {
0383                                result.add(pb);
0384                            }
0385                        }
0386                    }
0387                    if (result == null) {
0388                        result = new Rectangle(0, 0, 0, 0);
0389                    }
0390                } else { // draw and test
0391                    final int MARGIN = 3;
0392                    Rectangle2D r2d = getVisualBounds();
0393                    if (lp != null) {
0394                        r2d = lp.mapShape(r2d).getBounds();
0395                    }
0396                    Rectangle bounds = r2d.getBounds();
0397                    BufferedImage im = new BufferedImage(bounds.width + MARGIN
0398                            * 2, bounds.height + MARGIN * 2,
0399                            BufferedImage.TYPE_INT_ARGB);
0400
0401                    Graphics2D g2d = im.createGraphics();
0402                    g2d.setColor(Color.WHITE);
0403                    g2d.fillRect(0, 0, im.getWidth(), im.getHeight());
0404
0405                    g2d.setColor(Color.BLACK);
0406                    draw(g2d, rx + MARGIN - bounds.x, ry + MARGIN - bounds.y);
0407
0408                    result = computePixelBounds(im);
0409                    result.x -= MARGIN - bounds.x;
0410                    result.y -= MARGIN - bounds.y;
0411                }
0412
0413                if (canCache) {
0414                    pixelBounds = new Rectangle(result);
0415                }
0416
0417                result.x += ix;
0418                result.y += iy;
0419                return result;
0420            }
0421
0422            static Rectangle computePixelBounds(BufferedImage im) {
0423                int w = im.getWidth();
0424                int h = im.getHeight();
0425
0426                int l = -1, t = -1, r = w, b = h;
0427
0428                {
0429                    // get top
0430                    int[] buf = new int[w];
0431                    loop: while (++t < h) {
0432                        im.getRGB(0, t, buf.length, 1, buf, 0, w); // w ignored
0433                        for (int i = 0; i < buf.length; i++) {
0434                            if (buf[i] != -1) {
0435                                break loop;
0436                            }
0437                        }
0438                    }
0439                }
0440
0441                // get bottom
0442                {
0443                    int[] buf = new int[w];
0444                    loop: while (--b > t) {
0445                        im.getRGB(0, b, buf.length, 1, buf, 0, w); // w ignored
0446                        for (int i = 0; i < buf.length; ++i) {
0447                            if (buf[i] != -1) {
0448                                break loop;
0449                            }
0450                        }
0451                    }
0452                    ++b;
0453                }
0454
0455                // get left
0456                {
0457                    loop: while (++l < r) {
0458                        for (int i = t; i < b; ++i) {
0459                            int v = im.getRGB(l, i);
0460                            if (v != -1) {
0461                                break loop;
0462                            }
0463                        }
0464                    }
0465                }
0466
0467                // get right
0468                {
0469                    loop: while (--r > l) {
0470                        for (int i = t; i < b; ++i) {
0471                            int v = im.getRGB(r, i);
0472                            if (v != -1) {
0473                                break loop;
0474                            }
0475                        }
0476                    }
0477                    ++r;
0478                }
0479
0480                return new Rectangle(l, t, r - l, b - t);
0481            }
0482
0483            private abstract static class Function {
0484
0485                abstract float computeFunction(TextLine line,
0486                        int componentIndex, int indexInArray);
0487            }
0488
0489            private static Function fgPosAdvF = new Function() {
0490                float computeFunction(TextLine line, int componentIndex,
0491                        int indexInArray) {
0492
0493                    TextLineComponent tlc = line.fComponents[componentIndex];
0494                    int vi = line.fComponentVisualOrder == null ? componentIndex
0495                            : line.fComponentVisualOrder[componentIndex];
0496                    return line.locs[vi * 2] + tlc.getCharX(indexInArray)
0497                            + tlc.getCharAdvance(indexInArray);
0498                }
0499            };
0500
0501            private static Function fgAdvanceF = new Function() {
0502
0503                float computeFunction(TextLine line, int componentIndex,
0504                        int indexInArray) {
0505
0506                    TextLineComponent tlc = line.fComponents[componentIndex];
0507                    return tlc.getCharAdvance(indexInArray);
0508                }
0509            };
0510
0511            private static Function fgXPositionF = new Function() {
0512
0513                float computeFunction(TextLine line, int componentIndex,
0514                        int indexInArray) {
0515
0516                    int vi = line.fComponentVisualOrder == null ? componentIndex
0517                            : line.fComponentVisualOrder[componentIndex];
0518                    TextLineComponent tlc = line.fComponents[componentIndex];
0519                    return line.locs[vi * 2] + tlc.getCharX(indexInArray);
0520                }
0521            };
0522
0523            private static Function fgYPositionF = new Function() {
0524
0525                float computeFunction(TextLine line, int componentIndex,
0526                        int indexInArray) {
0527
0528                    TextLineComponent tlc = line.fComponents[componentIndex];
0529                    float charPos = tlc.getCharY(indexInArray);
0530
0531                    // charPos is relative to the component - adjust for
0532                    // baseline
0533
0534                    return charPos + line.getComponentShift(componentIndex);
0535                }
0536            };
0537
0538            public int characterCount() {
0539
0540                return fCharsLimit - fCharsStart;
0541            }
0542
0543            public boolean isDirectionLTR() {
0544
0545                return fIsDirectionLTR;
0546            }
0547
0548            public TextLineMetrics getMetrics() {
0549                return fMetrics;
0550            }
0551
0552            public int visualToLogical(int visualIndex) {
0553
0554                if (fCharLogicalOrder == null) {
0555                    return visualIndex;
0556                }
0557
0558                if (fCharVisualOrder == null) {
0559                    fCharVisualOrder = BidiUtils
0560                            .createInverseMap(fCharLogicalOrder);
0561                }
0562
0563                return fCharVisualOrder[visualIndex];
0564            }
0565
0566            public int logicalToVisual(int logicalIndex) {
0567
0568                return (fCharLogicalOrder == null) ? logicalIndex
0569                        : fCharLogicalOrder[logicalIndex];
0570            }
0571
0572            public byte getCharLevel(int logicalIndex) {
0573
0574                return fCharLevels == null ? 0 : fCharLevels[logicalIndex];
0575            }
0576
0577            public boolean isCharLTR(int logicalIndex) {
0578
0579                return (getCharLevel(logicalIndex) & 0x1) == 0;
0580            }
0581
0582            public int getCharType(int logicalIndex) {
0583
0584                return Character.getType(fChars[logicalIndex + fCharsStart]);
0585            }
0586
0587            public boolean isCharSpace(int logicalIndex) {
0588
0589                return Character
0590                        .isSpaceChar(fChars[logicalIndex + fCharsStart]);
0591            }
0592
0593            public boolean isCharWhitespace(int logicalIndex) {
0594
0595                return Character
0596                        .isWhitespace(fChars[logicalIndex + fCharsStart]);
0597            }
0598
0599            public float getCharAngle(int logicalIndex) {
0600
0601                return getCoreMetricsAt(logicalIndex).italicAngle;
0602            }
0603
0604            public CoreMetrics getCoreMetricsAt(int logicalIndex) {
0605
0606                if (logicalIndex < 0) {
0607                    throw new IllegalArgumentException("Negative logicalIndex.");
0608                }
0609
0610                if (logicalIndex > fCharsLimit - fCharsStart) {
0611                    throw new IllegalArgumentException(
0612                            "logicalIndex too large.");
0613                }
0614
0615                int currentTlc = 0;
0616                int tlcStart = 0;
0617                int tlcLimit = 0;
0618
0619                do {
0620                    tlcLimit += fComponents[currentTlc].getNumCharacters();
0621                    if (tlcLimit > logicalIndex) {
0622                        break;
0623                    }
0624                    ++currentTlc;
0625                    tlcStart = tlcLimit;
0626                } while (currentTlc < fComponents.length);
0627
0628                return fComponents[currentTlc].getCoreMetrics();
0629            }
0630
0631            public float getCharAscent(int logicalIndex) {
0632
0633                return getCoreMetricsAt(logicalIndex).ascent;
0634            }
0635
0636            public float getCharDescent(int logicalIndex) {
0637
0638                return getCoreMetricsAt(logicalIndex).descent;
0639            }
0640
0641            public float getCharShift(int logicalIndex) {
0642
0643                return getCoreMetricsAt(logicalIndex).ssOffset;
0644            }
0645
0646            private float applyFunctionAtIndex(int logicalIndex, Function f) {
0647
0648                if (logicalIndex < 0) {
0649                    throw new IllegalArgumentException("Negative logicalIndex.");
0650                }
0651
0652                int tlcStart = 0;
0653
0654                for (int i = 0; i < fComponents.length; i++) {
0655
0656                    int tlcLimit = tlcStart + fComponents[i].getNumCharacters();
0657                    if (tlcLimit > logicalIndex) {
0658                        return f.computeFunction(this , i, logicalIndex
0659                                - tlcStart);
0660                    } else {
0661                        tlcStart = tlcLimit;
0662                    }
0663                }
0664
0665                throw new IllegalArgumentException("logicalIndex too large.");
0666            }
0667
0668            public float getCharAdvance(int logicalIndex) {
0669
0670                return applyFunctionAtIndex(logicalIndex, fgAdvanceF);
0671            }
0672
0673            public float getCharXPosition(int logicalIndex) {
0674
0675                return applyFunctionAtIndex(logicalIndex, fgXPositionF);
0676            }
0677
0678            public float getCharYPosition(int logicalIndex) {
0679
0680                return applyFunctionAtIndex(logicalIndex, fgYPositionF);
0681            }
0682
0683            public float getCharLinePosition(int logicalIndex) {
0684
0685                return getCharXPosition(logicalIndex);
0686            }
0687
0688            public float getCharLinePosition(int logicalIndex, boolean leading) {
0689                Function f = isCharLTR(logicalIndex) == leading ? fgXPositionF
0690                        : fgPosAdvF;
0691                return applyFunctionAtIndex(logicalIndex, f);
0692            }
0693
0694            public boolean caretAtOffsetIsValid(int offset) {
0695
0696                if (offset < 0) {
0697                    throw new IllegalArgumentException("Negative offset.");
0698                }
0699
0700                int tlcStart = 0;
0701
0702                for (int i = 0; i < fComponents.length; i++) {
0703
0704                    int tlcLimit = tlcStart + fComponents[i].getNumCharacters();
0705                    if (tlcLimit > offset) {
0706                        return fComponents[i].caretAtOffsetIsValid(offset
0707                                - tlcStart);
0708                    } else {
0709                        tlcStart = tlcLimit;
0710                    }
0711                }
0712
0713                throw new IllegalArgumentException("logicalIndex too large.");
0714            }
0715
0716            public Rectangle2D getCharBounds(int logicalIndex) {
0717
0718                if (logicalIndex < 0) {
0719                    throw new IllegalArgumentException("Negative logicalIndex.");
0720                }
0721
0722                int tlcStart = 0;
0723
0724                for (int i = 0; i < fComponents.length; i++) {
0725
0726                    int tlcLimit = tlcStart + fComponents[i].getNumCharacters();
0727                    if (tlcLimit > logicalIndex) {
0728
0729                        TextLineComponent tlc = fComponents[i];
0730                        int indexInTlc = logicalIndex - tlcStart;
0731                        Rectangle2D chBounds = tlc
0732                                .getCharVisualBounds(indexInTlc);
0733
0734                        int vi = fComponentVisualOrder == null ? i
0735                                : fComponentVisualOrder[i];
0736                        chBounds.setRect(chBounds.getX() + locs[vi * 2],
0737                                chBounds.getY() + locs[vi * 2 + 1], chBounds
0738                                        .getWidth(), chBounds.getHeight());
0739                        return chBounds;
0740                    } else {
0741                        tlcStart = tlcLimit;
0742                    }
0743                }
0744
0745                throw new IllegalArgumentException("logicalIndex too large.");
0746            }
0747
0748            private float getComponentShift(int index) {
0749                CoreMetrics cm = fComponents[index].getCoreMetrics();
0750                return cm.effectiveBaselineOffset(fBaselineOffsets);
0751            }
0752
0753            public void draw(Graphics2D g2, float x, float y) {
0754                if (lp == null) {
0755                    for (int i = 0, n = 0; i < fComponents.length; i++, n += 2) {
0756                        int vi = fComponentVisualOrder == null ? i
0757                                : fComponentVisualOrder[i];
0758                        TextLineComponent tlc = fComponents[vi];
0759                        tlc.draw(g2, locs[n] + x, locs[n + 1] + y);
0760                    }
0761                } else {
0762                    AffineTransform oldTx = g2.getTransform();
0763                    Point2D.Float pt = new Point2D.Float();
0764                    for (int i = 0, n = 0; i < fComponents.length; i++, n += 2) {
0765                        int vi = fComponentVisualOrder == null ? i
0766                                : fComponentVisualOrder[i];
0767                        TextLineComponent tlc = fComponents[vi];
0768                        lp.pathToPoint(locs[n], locs[n + 1], false, pt);
0769                        pt.x += x;
0770                        pt.y += y;
0771                        AffineTransform at = tlc.getBaselineTransform();
0772
0773                        if (at != null) {
0774                            g2.translate(pt.x - at.getTranslateX(), pt.y
0775                                    - at.getTranslateY());
0776                            g2.transform(at);
0777                            tlc.draw(g2, 0, 0);
0778                            g2.setTransform(oldTx);
0779                        } else {
0780                            tlc.draw(g2, pt.x, pt.y);
0781                        }
0782                    }
0783                }
0784            }
0785
0786            /** 
0787             * Return the union of the visual bounds of all the components.
0788             * This incorporates the path.  It does not include logical
0789             * bounds (used by carets).
0790             */
0791            public Rectangle2D getVisualBounds() {
0792                Rectangle2D result = null;
0793
0794                for (int i = 0, n = 0; i < fComponents.length; i++, n += 2) {
0795                    int vi = fComponentVisualOrder == null ? i
0796                            : fComponentVisualOrder[i];
0797                    TextLineComponent tlc = fComponents[vi];
0798                    Rectangle2D r = tlc.getVisualBounds();
0799
0800                    Point2D.Float pt = new Point2D.Float(locs[n], locs[n + 1]);
0801                    if (lp == null) {
0802                        r.setRect(r.getMinX() + pt.x, r.getMinY() + pt.y, r
0803                                .getWidth(), r.getHeight());
0804                    } else {
0805                        lp.pathToPoint(pt, false, pt);
0806
0807                        AffineTransform at = tlc.getBaselineTransform();
0808                        if (at != null) {
0809                            AffineTransform tx = AffineTransform
0810                                    .getTranslateInstance(pt.x
0811                                            - at.getTranslateX(), pt.y
0812                                            - at.getTranslateY());
0813                            tx.concatenate(at);
0814                            r = tx.createTransformedShape(r).getBounds2D();
0815                        } else {
0816                            r.setRect(r.getMinX() + pt.x, r.getMinY() + pt.y, r
0817                                    .getWidth(), r.getHeight());
0818                        }
0819                    }
0820
0821                    if (result == null) {
0822                        result = r;
0823                    } else {
0824                        result.add(r);
0825                    }
0826                }
0827
0828                if (result == null) {
0829                    result = new Rectangle2D.Float(Float.MAX_VALUE,
0830                            Float.MAX_VALUE, Float.MIN_VALUE, Float.MIN_VALUE);
0831                }
0832
0833                return result;
0834            }
0835
0836            public Rectangle2D getItalicBounds() {
0837
0838                float left = Float.MAX_VALUE, right = -Float.MAX_VALUE;
0839                float top = Float.MAX_VALUE, bottom = -Float.MAX_VALUE;
0840
0841                for (int i = 0, n = 0; i < fComponents.length; i++, n += 2) {
0842                    int vi = fComponentVisualOrder == null ? i
0843                            : fComponentVisualOrder[i];
0844                    TextLineComponent tlc = fComponents[vi];
0845
0846                    Rectangle2D tlcBounds = tlc.getItalicBounds();
0847                    float x = locs[n];
0848                    float y = locs[n + 1];
0849
0850                    left = Math.min(left, x + (float) tlcBounds.getX());
0851                    right = Math.max(right, x + (float) tlcBounds.getMaxX());
0852
0853                    top = Math.min(top, y + (float) tlcBounds.getY());
0854                    bottom = Math.max(bottom, y + (float) tlcBounds.getMaxY());
0855                }
0856
0857                return new Rectangle2D.Float(left, top, right - left, bottom
0858                        - top);
0859            }
0860
0861            public Shape getOutline(AffineTransform tx) {
0862
0863                GeneralPath dstShape = new GeneralPath(
0864                        GeneralPath.WIND_NON_ZERO);
0865
0866                for (int i = 0, n = 0; i < fComponents.length; i++, n += 2) {
0867                    int vi = fComponentVisualOrder == null ? i
0868                            : fComponentVisualOrder[i];
0869                    TextLineComponent tlc = fComponents[vi];
0870
0871                    dstShape
0872                            .append(tlc.getOutline(locs[n], locs[n + 1]), false);
0873                }
0874
0875                if (tx != null) {
0876                    dstShape.transform(tx);
0877                }
0878                return dstShape;
0879            }
0880
0881            public int hashCode() {
0882                return (fComponents.length << 16)
0883                        ^ (fComponents[0].hashCode() << 3)
0884                        ^ (fCharsLimit - fCharsStart);
0885            }
0886
0887            public String toString() {
0888                StringBuilder buf = new StringBuilder();
0889
0890                for (int i = 0; i < fComponents.length; i++) {
0891                    buf.append(fComponents[i]);
0892                }
0893
0894                return buf.toString();
0895            }
0896
0897            /**
0898             * Create a TextLine from the text.  The Font must be able to
0899             * display all of the text.
0900             * attributes==null is equivalent to using an empty Map for
0901             * attributes
0902             */
0903            public static TextLine fastCreateTextLine(FontRenderContext frc,
0904                    char[] chars, Font font, CoreMetrics lm, Map attributes) {
0905
0906                boolean isDirectionLTR = true;
0907                byte[] levels = null;
0908                int[] charsLtoV = null;
0909                Bidi bidi = null;
0910                int characterCount = chars.length;
0911
0912                boolean requiresBidi = false;
0913                byte[] embs = null;
0914
0915                AttributeValues values = null;
0916                if (attributes != null) {
0917                    values = AttributeValues.fromMap(attributes);
0918                    if (values.getRunDirection() >= 0) {
0919                        isDirectionLTR = values.getRunDirection() == 0;
0920                        requiresBidi = !isDirectionLTR;
0921                    }
0922                    if (values.getBidiEmbedding() != 0) {
0923                        requiresBidi = true;
0924                        byte level = (byte) values.getBidiEmbedding();
0925                        embs = new byte[characterCount];
0926                        for (int i = 0; i < embs.length; ++i) {
0927                            embs[i] = level;
0928                        }
0929                    }
0930                }
0931
0932                // dlf: get baseRot from font for now???
0933
0934                if (!requiresBidi) {
0935                    requiresBidi = Bidi.requiresBidi(chars, 0, chars.length);
0936                }
0937
0938                if (requiresBidi) {
0939                    int bidiflags = values == null ? Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT
0940                            : values.getRunDirection();
0941
0942                    bidi = new Bidi(chars, 0, embs, 0, chars.length, bidiflags);
0943                    if (!bidi.isLeftToRight()) {
0944                        levels = BidiUtils.getLevels(bidi);
0945                        int[] charsVtoL = BidiUtils
0946                                .createVisualToLogicalMap(levels);
0947                        charsLtoV = BidiUtils.createInverseMap(charsVtoL);
0948                        isDirectionLTR = bidi.baseIsLeftToRight();
0949                    }
0950                }
0951
0952                Decoration decorator = Decoration.getDecoration(values);
0953
0954                int layoutFlags = 0; // no extra info yet, bidi determines run and line direction
0955                TextLabelFactory factory = new TextLabelFactory(frc, chars,
0956                        bidi, layoutFlags);
0957
0958                TextLineComponent[] components = new TextLineComponent[1];
0959
0960                components = createComponentsOnRun(0, chars.length, chars,
0961                        charsLtoV, levels, factory, font, lm, frc, decorator,
0962                        components, 0);
0963
0964                int numComponents = components.length;
0965                while (components[numComponents - 1] == null) {
0966                    numComponents -= 1;
0967                }
0968
0969                if (numComponents != components.length) {
0970                    TextLineComponent[] temp = new TextLineComponent[numComponents];
0971                    System.arraycopy(components, 0, temp, 0, numComponents);
0972                    components = temp;
0973                }
0974
0975                return new TextLine(frc, components, lm.baselineOffsets, chars,
0976                        0, chars.length, charsLtoV, levels, isDirectionLTR);
0977            }
0978
0979            private static TextLineComponent[] expandArray(
0980                    TextLineComponent[] orig) {
0981
0982                TextLineComponent[] newComponents = new TextLineComponent[orig.length + 8];
0983                System.arraycopy(orig, 0, newComponents, 0, orig.length);
0984
0985                return newComponents;
0986            }
0987
0988            /**
0989             * Returns an array in logical order of the TextLineComponents on
0990             * the text in the given range, with the given attributes.
0991             */
0992            public static TextLineComponent[] createComponentsOnRun(
0993                    int runStart, int runLimit, char[] chars, int[] charsLtoV,
0994                    byte[] levels, TextLabelFactory factory, Font font,
0995                    CoreMetrics cm, FontRenderContext frc,
0996                    Decoration decorator, TextLineComponent[] components,
0997                    int numComponents) {
0998
0999                int pos = runStart;
1000                do {
1001                    int chunkLimit = firstVisualChunk(charsLtoV, levels, pos,
1002                            runLimit); // <= displayLimit
1003
1004                    do {
1005                        int startPos = pos;
1006                        int lmCount;
1007
1008                        if (cm == null) {
1009                            LineMetrics lineMetrics = font.getLineMetrics(
1010                                    chars, startPos, chunkLimit, frc);
1011                            cm = CoreMetrics.get(lineMetrics);
1012                            lmCount = lineMetrics.getNumChars();
1013                        } else {
1014                            lmCount = (chunkLimit - startPos);
1015                        }
1016
1017                        TextLineComponent nextComponent = factory
1018                                .createExtended(font, cm, decorator, startPos,
1019                                        startPos + lmCount);
1020
1021                        ++numComponents;
1022                        if (numComponents >= components.length) {
1023                            components = expandArray(components);
1024                        }
1025
1026                        components[numComponents - 1] = nextComponent;
1027
1028                        pos += lmCount;
1029                    } while (pos < chunkLimit);
1030
1031                } while (pos < runLimit);
1032
1033                return components;
1034            }
1035
1036            /**
1037             * Returns an array (in logical order) of the TextLineComponents representing
1038             * the text.  The components are both logically and visually contiguous.
1039             */
1040            public static TextLineComponent[] getComponents(
1041                    StyledParagraph styledParagraph, char[] chars,
1042                    int textStart, int textLimit, int[] charsLtoV,
1043                    byte[] levels, TextLabelFactory factory) {
1044
1045                FontRenderContext frc = factory.getFontRenderContext();
1046
1047                int numComponents = 0;
1048                TextLineComponent[] tempComponents = new TextLineComponent[1];
1049
1050                int pos = textStart;
1051                do {
1052                    int runLimit = Math.min(styledParagraph.getRunLimit(pos),
1053                            textLimit);
1054
1055                    Decoration decorator = styledParagraph.getDecorationAt(pos);
1056
1057                    Object graphicOrFont = styledParagraph
1058                            .getFontOrGraphicAt(pos);
1059
1060                    if (graphicOrFont instanceof  GraphicAttribute) {
1061                        // AffineTransform baseRot = styledParagraph.getBaselineRotationAt(pos);
1062                        // !!! For now, let's assign runs of text with both fonts and graphic attributes
1063                        // a null rotation (e.g. the baseline rotation goes away when a graphic
1064                        // is applied.
1065                        AffineTransform baseRot = null;
1066                        GraphicAttribute graphicAttribute = (GraphicAttribute) graphicOrFont;
1067                        do {
1068                            int chunkLimit = firstVisualChunk(charsLtoV,
1069                                    levels, pos, runLimit);
1070
1071                            GraphicComponent nextGraphic = new GraphicComponent(
1072                                    graphicAttribute, decorator, charsLtoV,
1073                                    levels, pos, chunkLimit, baseRot);
1074                            pos = chunkLimit;
1075
1076                            ++numComponents;
1077                            if (numComponents >= tempComponents.length) {
1078                                tempComponents = expandArray(tempComponents);
1079                            }
1080
1081                            tempComponents[numComponents - 1] = nextGraphic;
1082
1083                        } while (pos < runLimit);
1084                    } else {
1085                        Font font = (Font) graphicOrFont;
1086
1087                        tempComponents = createComponentsOnRun(pos, runLimit,
1088                                chars, charsLtoV, levels, factory, font, null,
1089                                frc, decorator, tempComponents, numComponents);
1090                        pos = runLimit;
1091                        numComponents = tempComponents.length;
1092                        while (tempComponents[numComponents - 1] == null) {
1093                            numComponents -= 1;
1094                        }
1095                    }
1096
1097                } while (pos < textLimit);
1098
1099                TextLineComponent[] components;
1100                if (tempComponents.length == numComponents) {
1101                    components = tempComponents;
1102                } else {
1103                    components = new TextLineComponent[numComponents];
1104                    System.arraycopy(tempComponents, 0, components, 0,
1105                            numComponents);
1106                }
1107
1108                return components;
1109            }
1110
1111            /**
1112             * Create a TextLine from the Font and character data over the
1113             * range.  The range is relative to both the StyledParagraph and the
1114             * character array.
1115             */
1116            public static TextLine createLineFromText(char[] chars,
1117                    StyledParagraph styledParagraph, TextLabelFactory factory,
1118                    boolean isDirectionLTR, float[] baselineOffsets) {
1119
1120                factory.setLineContext(0, chars.length);
1121
1122                Bidi lineBidi = factory.getLineBidi();
1123                int[] charsLtoV = null;
1124                byte[] levels = null;
1125
1126                if (lineBidi != null) {
1127                    levels = BidiUtils.getLevels(lineBidi);
1128                    int[] charsVtoL = BidiUtils
1129                            .createVisualToLogicalMap(levels);
1130                    charsLtoV = BidiUtils.createInverseMap(charsVtoL);
1131                }
1132
1133                TextLineComponent[] components = getComponents(styledParagraph,
1134                        chars, 0, chars.length, charsLtoV, levels, factory);
1135
1136                return new TextLine(factory.getFontRenderContext(), components,
1137                        baselineOffsets, chars, 0, chars.length, charsLtoV,
1138                        levels, isDirectionLTR);
1139            }
1140
1141            /**
1142             * Compute the components order from the given components array and
1143             * logical-to-visual character mapping.  May return null if canonical.
1144             */
1145            private static int[] computeComponentOrder(
1146                    TextLineComponent[] components, int[] charsLtoV) {
1147
1148                /*
1149                 * Create a visual ordering for the glyph sets.  The important thing
1150                 * here is that the values have the proper rank with respect to
1151                 * each other, not the exact values.  For example, the first glyph
1152                 * set that appears visually should have the lowest value.  The last
1153                 * should have the highest value.  The values are then normalized
1154                 * to map 1-1 with positions in glyphs.
1155                 *
1156                 */
1157                int[] componentOrder = null;
1158                if (charsLtoV != null && components.length > 1) {
1159                    componentOrder = new int[components.length];
1160                    int gStart = 0;
1161                    for (int i = 0; i < components.length; i++) {
1162                        componentOrder[i] = charsLtoV[gStart];
1163                        gStart += components[i].getNumCharacters();
1164                    }
1165
1166                    componentOrder = BidiUtils
1167                            .createContiguousOrder(componentOrder);
1168                    componentOrder = BidiUtils.createInverseMap(componentOrder);
1169                }
1170                return componentOrder;
1171            }
1172
1173            /**
1174             * Create a TextLine from the text.  chars is just the text in the iterator.
1175             */
1176            public static TextLine standardCreateTextLine(
1177                    FontRenderContext frc, AttributedCharacterIterator text,
1178                    char[] chars, float[] baselineOffsets) {
1179
1180                StyledParagraph styledParagraph = new StyledParagraph(text,
1181                        chars);
1182                Bidi bidi = new Bidi(text);
1183                if (bidi.isLeftToRight()) {
1184                    bidi = null;
1185                }
1186                int layoutFlags = 0; // no extra info yet, bidi determines run and line direction
1187                TextLabelFactory factory = new TextLabelFactory(frc, chars,
1188                        bidi, layoutFlags);
1189
1190                boolean isDirectionLTR = true;
1191                if (bidi != null) {
1192                    isDirectionLTR = bidi.baseIsLeftToRight();
1193                }
1194                return createLineFromText(chars, styledParagraph, factory,
1195                        isDirectionLTR, baselineOffsets);
1196            }
1197
1198            /*
1199             * A utility to get a range of text that is both logically and visually
1200             * contiguous.
1201             * If the entire range is ok, return limit, otherwise return the first
1202             * directional change after start.  We could do better than this, but
1203             * it doesn't seem worth it at the moment.
1204            private static int firstVisualChunk(int order[], byte direction[],
1205                                                int start, int limit)
1206            {
1207                if (order != null) {
1208                    int min = order[start];
1209                    int max = order[start];
1210                    int count = limit - start;
1211                    for (int i = start + 1; i < limit; i++) {
1212                        min = Math.min(min, order[i]);
1213                        max = Math.max(max, order[i]);
1214                        if (max - min >= count) {
1215                            if (direction != null) {
1216                                byte baseLevel = direction[start];
1217                                for (int j = start + 1; j < i; j++) {
1218                                    if (direction[j] != baseLevel) {
1219                                        return j;
1220                                    }
1221                                }
1222                            }
1223                            return i;
1224                        }
1225                    }
1226                }
1227                return limit;
1228            }
1229             */
1230
1231            /**
1232             * When this returns, the ACI's current position will be at the start of the
1233             * first run which does NOT contain a GraphicAttribute.  If no such run exists
1234             * the ACI's position will be at the end, and this method will return false.
1235             */
1236            static boolean advanceToFirstFont(AttributedCharacterIterator aci) {
1237
1238                for (char ch = aci.first(); ch != aci.DONE; ch = aci
1239                        .setIndex(aci.getRunLimit())) {
1240
1241                    if (aci.getAttribute(TextAttribute.CHAR_REPLACEMENT) == null) {
1242                        return true;
1243                    }
1244                }
1245
1246                return false;
1247            }
1248
1249            static float[] getNormalizedOffsets(float[] baselineOffsets,
1250                    byte baseline) {
1251
1252                if (baselineOffsets[baseline] != 0) {
1253                    float base = baselineOffsets[baseline];
1254                    float[] temp = new float[baselineOffsets.length];
1255                    for (int i = 0; i < temp.length; i++)
1256                        temp[i] = baselineOffsets[i] - base;
1257                    baselineOffsets = temp;
1258                }
1259                return baselineOffsets;
1260            }
1261
1262            static Font getFontAtCurrentPos(AttributedCharacterIterator aci) {
1263
1264                Object value = aci.getAttribute(TextAttribute.FONT);
1265                if (value != null) {
1266                    return (Font) value;
1267                }
1268                if (aci.getAttribute(TextAttribute.FAMILY) != null) {
1269                    return Font.getFont(aci.getAttributes());
1270                }
1271
1272                int ch = CodePointIterator.create(aci).next();
1273                if (ch != CodePointIterator.DONE) {
1274                    FontResolver resolver = FontResolver.getInstance();
1275                    return resolver.getFont(resolver.getFontIndex(ch), aci
1276                            .getAttributes());
1277                }
1278                return null;
1279            }
1280
1281            /*
1282             * The new version requires that chunks be at the same level.
1283             */
1284            private static int firstVisualChunk(int order[], byte direction[],
1285                    int start, int limit) {
1286                if (order != null && direction != null) {
1287                    byte dir = direction[start];
1288                    while (++start < limit && direction[start] == dir) {
1289                    }
1290                    return start;
1291                }
1292                return limit;
1293            }
1294
1295            /*
1296             * create a new line with characters between charStart and charLimit
1297             * justified using the provided width and ratio.
1298             */
1299            public TextLine getJustifiedLine(float justificationWidth,
1300                    float justifyRatio, int justStart, int justLimit) {
1301
1302                TextLineComponent[] newComponents = new TextLineComponent[fComponents.length];
1303                System.arraycopy(fComponents, 0, newComponents, 0,
1304                        fComponents.length);
1305
1306                float leftHang = 0;
1307                float adv = 0;
1308                float justifyDelta = 0;
1309                boolean rejustify = false;
1310                do {
1311                    adv = getAdvanceBetween(newComponents, 0, characterCount());
1312
1313                    // all characters outside the justification range must be in the base direction
1314                    // of the layout, otherwise justification makes no sense.
1315
1316                    float justifyAdvance = getAdvanceBetween(newComponents,
1317                            justStart, justLimit);
1318
1319                    // get the actual justification delta
1320                    justifyDelta = (justificationWidth - justifyAdvance)
1321                            * justifyRatio;
1322
1323                    // generate an array of GlyphJustificationInfo records to pass to
1324                    // the justifier.  Array is visually ordered.
1325
1326                    // get positions that each component will be using
1327                    int[] infoPositions = new int[newComponents.length];
1328                    int infoCount = 0;
1329                    for (int visIndex = 0; visIndex < newComponents.length; visIndex++) {
1330                        int logIndex = fComponentVisualOrder == null ? visIndex
1331                                : fComponentVisualOrder[visIndex];
1332                        infoPositions[logIndex] = infoCount;
1333                        infoCount += newComponents[logIndex]
1334                                .getNumJustificationInfos();
1335                    }
1336                    GlyphJustificationInfo[] infos = new GlyphJustificationInfo[infoCount];
1337
1338                    // get justification infos
1339                    int compStart = 0;
1340                    for (int i = 0; i < newComponents.length; i++) {
1341                        TextLineComponent comp = newComponents[i];
1342                        int compLength = comp.getNumCharacters();
1343                        int compLimit = compStart + compLength;
1344                        if (compLimit > justStart) {
1345                            int rangeMin = Math.max(0, justStart - compStart);
1346                            int rangeMax = Math.min(compLength, justLimit
1347                                    - compStart);
1348                            comp.getJustificationInfos(infos, infoPositions[i],
1349                                    rangeMin, rangeMax);
1350
1351                            if (compLimit >= justLimit) {
1352                                break;
1353                            }
1354                        }
1355                    }
1356
1357                    // records are visually ordered, and contiguous, so start and end are
1358                    // simply the places where we didn't fetch records
1359                    int infoStart = 0;
1360                    int infoLimit = infoCount;
1361                    while (infoStart < infoLimit && infos[infoStart] == null) {
1362                        ++infoStart;
1363                    }
1364
1365                    while (infoLimit > infoStart
1366                            && infos[infoLimit - 1] == null) {
1367                        --infoLimit;
1368                    }
1369
1370                    // invoke justifier on the records
1371                    TextJustifier justifier = new TextJustifier(infos,
1372                            infoStart, infoLimit);
1373
1374                    float[] deltas = justifier.justify(justifyDelta);
1375
1376                    boolean canRejustify = rejustify == false;
1377                    boolean wantRejustify = false;
1378                    boolean[] flags = new boolean[1];
1379
1380                    // apply justification deltas
1381                    compStart = 0;
1382                    for (int i = 0; i < newComponents.length; i++) {
1383                        TextLineComponent comp = newComponents[i];
1384                        int compLength = comp.getNumCharacters();
1385                        int compLimit = compStart + compLength;
1386                        if (compLimit > justStart) {
1387                            int rangeMin = Math.max(0, justStart - compStart);
1388                            int rangeMax = Math.min(compLength, justLimit
1389                                    - compStart);
1390                            newComponents[i] = comp.applyJustificationDeltas(
1391                                    deltas, infoPositions[i] * 2, flags);
1392
1393                            wantRejustify |= flags[0];
1394
1395                            if (compLimit >= justLimit) {
1396                                break;
1397                            }
1398                        }
1399                    }
1400
1401                    rejustify = wantRejustify && !rejustify; // only make two passes
1402                } while (rejustify);
1403
1404                return new TextLine(frc, newComponents, fBaselineOffsets,
1405                        fChars, fCharsStart, fCharsLimit, fCharLogicalOrder,
1406                        fCharLevels, fIsDirectionLTR);
1407            }
1408
1409            // return the sum of the advances of text between the logical start and limit
1410            public static float getAdvanceBetween(
1411                    TextLineComponent[] components, int start, int limit) {
1412                float advance = 0;
1413
1414                int tlcStart = 0;
1415                for (int i = 0; i < components.length; i++) {
1416                    TextLineComponent comp = components[i];
1417
1418                    int tlcLength = comp.getNumCharacters();
1419                    int tlcLimit = tlcStart + tlcLength;
1420                    if (tlcLimit > start) {
1421                        int measureStart = Math.max(0, start - tlcStart);
1422                        int measureLimit = Math
1423                                .min(tlcLength, limit - tlcStart);
1424                        advance += comp.getAdvanceBetween(measureStart,
1425                                measureLimit);
1426                        if (tlcLimit >= limit) {
1427                            break;
1428                        }
1429                    }
1430
1431                    tlcStart = tlcLimit;
1432                }
1433
1434                return advance;
1435            }
1436
1437            LayoutPathImpl getLayoutPath() {
1438                return lp;
1439            }
1440        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.