Save This Page
Home » openjdk-7 » java » awt » font » [javadoc | source]
    1   /*
    2    * Copyright 1998-2006 Sun Microsystems, Inc.  All Rights Reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any questions.
   24    */
   25   
   26   /*
   27    * (C) Copyright IBM Corp. 1998-2003, All Rights Reserved
   28    *
   29    */
   30   
   31   package java.awt.font;
   32   
   33   import java.awt.Color;
   34   import java.awt.Font;
   35   import java.awt.Graphics2D;
   36   import java.awt.Rectangle;
   37   import java.awt.Shape;
   38   import java.awt.geom.AffineTransform;
   39   import java.awt.geom.GeneralPath;
   40   import java.awt.geom.Point2D;
   41   import java.awt.geom.Rectangle2D;
   42   import java.awt.im.InputMethodHighlight;
   43   import java.awt.image.BufferedImage;
   44   import java.text.Annotation;
   45   import java.text.AttributedCharacterIterator;
   46   import java.text.Bidi;
   47   import java.text.CharacterIterator;
   48   import java.util.Hashtable;
   49   import java.util.Map;
   50   import sun.font.AttributeValues;
   51   import sun.font.BidiUtils;
   52   import sun.font.CoreMetrics;
   53   import sun.font.Decoration;
   54   import sun.font.FontLineMetrics;
   55   import sun.font.FontResolver;
   56   import sun.font.GraphicComponent;
   57   import sun.font.LayoutPathImpl;
   58   import sun.font.LayoutPathImpl.EmptyPath;
   59   import sun.font.LayoutPathImpl.SegmentPathBuilder;
   60   import sun.font.TextLabelFactory;
   61   import sun.font.TextLineComponent;
   62   import sun.text.CodePointIterator;
   63   
   64   import java.awt.geom.Line2D;
   65   
   66   final class TextLine {
   67   
   68       static final class TextLineMetrics {
   69           public final float ascent;
   70           public final float descent;
   71           public final float leading;
   72           public final float advance;
   73   
   74           public TextLineMetrics(float ascent,
   75                              float descent,
   76                              float leading,
   77                              float advance) {
   78               this.ascent = ascent;
   79               this.descent = descent;
   80               this.leading = leading;
   81               this.advance = advance;
   82           }
   83       }
   84   
   85       private TextLineComponent[] fComponents;
   86       private float[] fBaselineOffsets;
   87       private int[] fComponentVisualOrder; // if null, ltr
   88       private float[] locs; // x,y pairs for components in visual order
   89       private char[] fChars;
   90       private int fCharsStart;
   91       private int fCharsLimit;
   92       private int[] fCharVisualOrder;  // if null, ltr
   93       private int[] fCharLogicalOrder; // if null, ltr
   94       private byte[] fCharLevels;     // if null, 0
   95       private boolean fIsDirectionLTR;
   96       private LayoutPathImpl lp;
   97       private boolean isSimple;
   98       private Rectangle pixelBounds;
   99       private FontRenderContext frc;
  100   
  101       private TextLineMetrics fMetrics = null; // built on demand in getMetrics
  102   
  103       public TextLine(FontRenderContext frc,
  104                       TextLineComponent[] components,
  105                       float[] baselineOffsets,
  106                       char[] chars,
  107                       int charsStart,
  108                       int charsLimit,
  109                       int[] charLogicalOrder,
  110                       byte[] charLevels,
  111                       boolean isDirectionLTR) {
  112   
  113           int[] componentVisualOrder = computeComponentOrder(components,
  114                                                              charLogicalOrder);
  115   
  116           this.frc = frc;
  117           fComponents = components;
  118           fBaselineOffsets = baselineOffsets;
  119           fComponentVisualOrder = componentVisualOrder;
  120           fChars = chars;
  121           fCharsStart = charsStart;
  122           fCharsLimit = charsLimit;
  123           fCharLogicalOrder = charLogicalOrder;
  124           fCharLevels = charLevels;
  125           fIsDirectionLTR = isDirectionLTR;
  126           checkCtorArgs();
  127   
  128           init();
  129       }
  130   
  131       private void checkCtorArgs() {
  132   
  133           int checkCharCount = 0;
  134           for (int i=0; i < fComponents.length; i++) {
  135               checkCharCount += fComponents[i].getNumCharacters();
  136           }
  137   
  138           if (checkCharCount != this.characterCount()) {
  139               throw new IllegalArgumentException("Invalid TextLine!  " +
  140                                   "char count is different from " +
  141                                   "sum of char counts of components.");
  142           }
  143       }
  144   
  145       private void init() {
  146   
  147           // first, we need to check for graphic components on the TOP or BOTTOM baselines.  So
  148           // we perform the work that used to be in getMetrics here.
  149   
  150           float ascent = 0;
  151           float descent = 0;
  152           float leading = 0;
  153           float advance = 0;
  154   
  155           // ascent + descent must not be less than this value
  156           float maxGraphicHeight = 0;
  157           float maxGraphicHeightWithLeading = 0;
  158   
  159           // walk through EGA's
  160           TextLineComponent tlc;
  161           boolean fitTopAndBottomGraphics = false;
  162   
  163           isSimple = true;
  164   
  165           for (int i = 0; i < fComponents.length; i++) {
  166               tlc = fComponents[i];
  167   
  168               isSimple &= tlc.isSimple();
  169   
  170               CoreMetrics cm = tlc.getCoreMetrics();
  171   
  172               byte baseline = (byte)cm.baselineIndex;
  173   
  174               if (baseline >= 0) {
  175                   float baselineOffset = fBaselineOffsets[baseline];
  176   
  177                   ascent = Math.max(ascent, -baselineOffset + cm.ascent);
  178   
  179                   float gd = baselineOffset + cm.descent;
  180                   descent = Math.max(descent, gd);
  181   
  182                   leading = Math.max(leading, gd + cm.leading);
  183               }
  184               else {
  185                   fitTopAndBottomGraphics = true;
  186                   float graphicHeight = cm.ascent + cm.descent;
  187                   float graphicHeightWithLeading = graphicHeight + cm.leading;
  188                   maxGraphicHeight = Math.max(maxGraphicHeight, graphicHeight);
  189                   maxGraphicHeightWithLeading = Math.max(maxGraphicHeightWithLeading,
  190                                                          graphicHeightWithLeading);
  191               }
  192           }
  193   
  194           if (fitTopAndBottomGraphics) {
  195               if (maxGraphicHeight > ascent + descent) {
  196                   descent = maxGraphicHeight - ascent;
  197               }
  198               if (maxGraphicHeightWithLeading > ascent + leading) {
  199                   leading = maxGraphicHeightWithLeading - ascent;
  200               }
  201           }
  202   
  203           leading -= descent;
  204   
  205           // we now know enough to compute the locs, but we need the final loc
  206           // for the advance before we can create the metrics object
  207   
  208           if (fitTopAndBottomGraphics) {
  209               // we have top or bottom baselines, so expand the baselines array
  210               // full offsets are needed by CoreMetrics.effectiveBaselineOffset
  211               fBaselineOffsets = new float[] {
  212                   fBaselineOffsets[0],
  213                   fBaselineOffsets[1],
  214                   fBaselineOffsets[2],
  215                   descent,
  216                   -ascent
  217               };
  218           }
  219   
  220           float x = 0;
  221           float y = 0;
  222           CoreMetrics pcm = null;
  223   
  224           boolean needPath = false;
  225           locs = new float[fComponents.length * 2 + 2];
  226   
  227           for (int i = 0, n = 0; i < fComponents.length; ++i, n += 2) {
  228               tlc = fComponents[getComponentLogicalIndex(i)];
  229               CoreMetrics cm = tlc.getCoreMetrics();
  230   
  231               if ((pcm != null) &&
  232                   (pcm.italicAngle != 0 || cm.italicAngle != 0) &&  // adjust because of italics
  233                   (pcm.italicAngle != cm.italicAngle ||
  234                    pcm.baselineIndex != cm.baselineIndex ||
  235                    pcm.ssOffset != cm.ssOffset)) {
  236   
  237                   // 1) compute the area of overlap - min effective ascent and min effective descent
  238                   // 2) compute the x positions along italic angle of ascent and descent for left and right
  239                   // 3) compute maximum left - right, adjust right position by this value
  240                   // this is a crude form of kerning between textcomponents
  241   
  242                   // note glyphvectors preposition glyphs based on offset,
  243                   // so tl doesn't need to adjust glyphvector position
  244                   // 1)
  245                   float pb = pcm.effectiveBaselineOffset(fBaselineOffsets);
  246                   float pa = pb - pcm.ascent;
  247                   float pd = pb + pcm.descent;
  248                   // pb += pcm.ssOffset;
  249   
  250                   float cb = cm.effectiveBaselineOffset(fBaselineOffsets);
  251                   float ca = cb - cm.ascent;
  252                   float cd = cb + cm.descent;
  253                   // cb += cm.ssOffset;
  254   
  255                   float a = Math.max(pa, ca);
  256                   float d = Math.min(pd, cd);
  257   
  258                   // 2)
  259                   float pax = pcm.italicAngle * (pb - a);
  260                   float pdx = pcm.italicAngle * (pb - d);
  261   
  262                   float cax = cm.italicAngle * (cb - a);
  263                   float cdx = cm.italicAngle * (cb - d);
  264   
  265                   // 3)
  266                   float dax = pax - cax;
  267                   float ddx = pdx - cdx;
  268                   float dx = Math.max(dax, ddx);
  269   
  270                   x += dx;
  271                   y = cb;
  272               } else {
  273                   // no italic adjustment for x, but still need to compute y
  274                   y = cm.effectiveBaselineOffset(fBaselineOffsets); // + cm.ssOffset;
  275               }
  276   
  277               locs[n] = x;
  278               locs[n+1] = y;
  279   
  280               x += tlc.getAdvance();
  281               pcm = cm;
  282   
  283               needPath |= tlc.getBaselineTransform() != null;
  284           }
  285   
  286           // do we want italic padding at the right of the line?
  287           if (pcm.italicAngle != 0) {
  288               float pb = pcm.effectiveBaselineOffset(fBaselineOffsets);
  289               float pa = pb - pcm.ascent;
  290               float pd = pb + pcm.descent;
  291               pb += pcm.ssOffset;
  292   
  293               float d;
  294               if (pcm.italicAngle > 0) {
  295                   d = pb + pcm.ascent;
  296               } else {
  297                   d = pb - pcm.descent;
  298               }
  299               d *= pcm.italicAngle;
  300   
  301               x += d;
  302           }
  303           locs[locs.length - 2] = x;
  304           // locs[locs.length - 1] = 0; // final offset is always back on baseline
  305   
  306           // ok, build fMetrics since we have the final advance
  307           advance = x;
  308           fMetrics = new TextLineMetrics(ascent, descent, leading, advance);
  309   
  310           // build path if we need it
  311           if (needPath) {
  312               isSimple = false;
  313   
  314               Point2D.Double pt = new Point2D.Double();
  315               double tx = 0, ty = 0;
  316               SegmentPathBuilder builder = new SegmentPathBuilder();
  317               builder.moveTo(locs[0], 0);
  318               for (int i = 0, n = 0; i < fComponents.length; ++i, n += 2) {
  319                   tlc = fComponents[getComponentLogicalIndex(i)];
  320                   AffineTransform at = tlc.getBaselineTransform();
  321                   if (at != null && ((at.getType() & at.TYPE_TRANSLATION) != 0)) {
  322                       double dx = at.getTranslateX();
  323                       double dy = at.getTranslateY();
  324                       builder.moveTo(tx += dx, ty += dy);
  325                   }
  326                   pt.x = locs[n+2] - locs[n];
  327                   pt.y = 0;
  328                   if (at != null) {
  329                       at.deltaTransform(pt, pt);
  330                   }
  331                   builder.lineTo(tx += pt.x, ty += pt.y);
  332               }
  333               lp = builder.complete();
  334   
  335               if (lp == null) { // empty path
  336                   tlc = fComponents[getComponentLogicalIndex(0)];
  337                   AffineTransform at = tlc.getBaselineTransform();
  338                   if (at != null) {
  339                       lp = new EmptyPath(at);
  340                   }
  341               }
  342           }
  343       }
  344   
  345       public Rectangle getPixelBounds(FontRenderContext frc, float x, float y) {
  346           Rectangle result = null;
  347   
  348           // if we have a matching frc, set it to null so we don't have to test it
  349           // for each component
  350           if (frc != null && frc.equals(this.frc)) {
  351               frc = null;
  352           }
  353   
  354           // only cache integral locations with the default frc, this is a bit strict
  355           int ix = (int)Math.floor(x);
  356           int iy = (int)Math.floor(y);
  357           float rx = x - ix;
  358           float ry = y - iy;
  359           boolean canCache = frc == null && rx == 0 && ry == 0;
  360   
  361           if (canCache && pixelBounds != null) {
  362               result = new Rectangle(pixelBounds);
  363               result.x += ix;
  364               result.y += iy;
  365               return result;
  366           }
  367   
  368           // couldn't use cache, or didn't have it, so compute
  369   
  370           if (isSimple) { // all glyphvectors with no decorations, no layout path
  371               for (int i = 0, n = 0; i < fComponents.length; i++, n += 2) {
  372                   TextLineComponent tlc = fComponents[getComponentLogicalIndex(i)];
  373                   Rectangle pb = tlc.getPixelBounds(frc, locs[n] + rx, locs[n+1] + ry);
  374                   if (!pb.isEmpty()) {
  375                       if (result == null) {
  376                           result = pb;
  377                       } else {
  378                           result.add(pb);
  379                       }
  380                   }
  381               }
  382               if (result == null) {
  383                   result = new Rectangle(0, 0, 0, 0);
  384               }
  385           } else { // draw and test
  386               final int MARGIN = 3;
  387               Rectangle2D r2d = getVisualBounds();
  388               if (lp != null) {
  389                   r2d = lp.mapShape(r2d).getBounds();
  390               }
  391               Rectangle bounds = r2d.getBounds();
  392               BufferedImage im = new BufferedImage(bounds.width + MARGIN * 2,
  393                                                    bounds.height + MARGIN * 2,
  394                                                    BufferedImage.TYPE_INT_ARGB);
  395   
  396               Graphics2D g2d = im.createGraphics();
  397               g2d.setColor(Color.WHITE);
  398               g2d.fillRect(0, 0, im.getWidth(), im.getHeight());
  399   
  400               g2d.setColor(Color.BLACK);
  401               draw(g2d, rx + MARGIN - bounds.x, ry + MARGIN - bounds.y);
  402   
  403               result = computePixelBounds(im);
  404               result.x -= MARGIN - bounds.x;
  405               result.y -= MARGIN - bounds.y;
  406           }
  407   
  408           if (canCache) {
  409               pixelBounds = new Rectangle(result);
  410           }
  411   
  412           result.x += ix;
  413           result.y += iy;
  414           return result;
  415       }
  416   
  417       static Rectangle computePixelBounds(BufferedImage im) {
  418           int w = im.getWidth();
  419           int h = im.getHeight();
  420   
  421           int l = -1, t = -1, r = w, b = h;
  422   
  423           {
  424               // get top
  425               int[] buf = new int[w];
  426               loop: while (++t < h) {
  427                   im.getRGB(0, t, buf.length, 1, buf, 0, w); // w ignored
  428                   for (int i = 0; i < buf.length; i++) {
  429                       if (buf[i] != -1) {
  430                           break loop;
  431                       }
  432                   }
  433               }
  434           }
  435   
  436           // get bottom
  437           {
  438               int[] buf = new int[w];
  439               loop: while (--b > t) {
  440                   im.getRGB(0, b, buf.length, 1, buf, 0, w); // w ignored
  441                   for (int i = 0; i < buf.length; ++i) {
  442                       if (buf[i] != -1) {
  443                           break loop;
  444                       }
  445                   }
  446               }
  447               ++b;
  448           }
  449   
  450           // get left
  451           {
  452               loop: while (++l < r) {
  453                   for (int i = t; i < b; ++i) {
  454                       int v = im.getRGB(l, i);
  455                       if (v != -1) {
  456                           break loop;
  457                       }
  458                   }
  459               }
  460           }
  461   
  462           // get right
  463           {
  464               loop: while (--r > l) {
  465                   for (int i = t; i < b; ++i) {
  466                       int v = im.getRGB(r, i);
  467                       if (v != -1) {
  468                           break loop;
  469                       }
  470                   }
  471               }
  472               ++r;
  473           }
  474   
  475           return new Rectangle(l, t, r-l, b-t);
  476       }
  477   
  478       private abstract static class Function {
  479   
  480           abstract float computeFunction(TextLine line,
  481                                          int componentIndex,
  482                                          int indexInArray);
  483       }
  484   
  485       private static Function fgPosAdvF = new Function() {
  486           float computeFunction(TextLine line,
  487                                 int componentIndex,
  488                                 int indexInArray) {
  489   
  490               TextLineComponent tlc = line.fComponents[componentIndex];
  491                   int vi = line.getComponentVisualIndex(componentIndex);
  492               return line.locs[vi * 2] + tlc.getCharX(indexInArray) + tlc.getCharAdvance(indexInArray);
  493           }
  494       };
  495   
  496       private static Function fgAdvanceF = new Function() {
  497   
  498           float computeFunction(TextLine line,
  499                                 int componentIndex,
  500                                 int indexInArray) {
  501   
  502               TextLineComponent tlc = line.fComponents[componentIndex];
  503               return tlc.getCharAdvance(indexInArray);
  504           }
  505       };
  506   
  507       private static Function fgXPositionF = new Function() {
  508   
  509           float computeFunction(TextLine line,
  510                                 int componentIndex,
  511                                 int indexInArray) {
  512   
  513                   int vi = line.getComponentVisualIndex(componentIndex);
  514               TextLineComponent tlc = line.fComponents[componentIndex];
  515               return line.locs[vi * 2] + tlc.getCharX(indexInArray);
  516           }
  517       };
  518   
  519       private static Function fgYPositionF = new Function() {
  520   
  521           float computeFunction(TextLine line,
  522                                 int componentIndex,
  523                                 int indexInArray) {
  524   
  525               TextLineComponent tlc = line.fComponents[componentIndex];
  526               float charPos = tlc.getCharY(indexInArray);
  527   
  528               // charPos is relative to the component - adjust for
  529               // baseline
  530   
  531               return charPos + line.getComponentShift(componentIndex);
  532           }
  533       };
  534   
  535       public int characterCount() {
  536   
  537           return fCharsLimit - fCharsStart;
  538       }
  539   
  540       public boolean isDirectionLTR() {
  541   
  542           return fIsDirectionLTR;
  543       }
  544   
  545       public TextLineMetrics getMetrics() {
  546           return fMetrics;
  547       }
  548   
  549       public int visualToLogical(int visualIndex) {
  550   
  551           if (fCharLogicalOrder == null) {
  552               return visualIndex;
  553           }
  554   
  555           if (fCharVisualOrder == null) {
  556               fCharVisualOrder = BidiUtils.createInverseMap(fCharLogicalOrder);
  557           }
  558   
  559           return fCharVisualOrder[visualIndex];
  560       }
  561   
  562       public int logicalToVisual(int logicalIndex) {
  563   
  564           return (fCharLogicalOrder == null)?
  565               logicalIndex : fCharLogicalOrder[logicalIndex];
  566       }
  567   
  568       public byte getCharLevel(int logicalIndex) {
  569   
  570           return fCharLevels==null? 0 : fCharLevels[logicalIndex];
  571       }
  572   
  573       public boolean isCharLTR(int logicalIndex) {
  574   
  575           return (getCharLevel(logicalIndex) & 0x1) == 0;
  576       }
  577   
  578       public int getCharType(int logicalIndex) {
  579   
  580           return Character.getType(fChars[logicalIndex + fCharsStart]);
  581       }
  582   
  583       public boolean isCharSpace(int logicalIndex) {
  584   
  585           return Character.isSpaceChar(fChars[logicalIndex + fCharsStart]);
  586       }
  587   
  588       public boolean isCharWhitespace(int logicalIndex) {
  589   
  590           return Character.isWhitespace(fChars[logicalIndex + fCharsStart]);
  591       }
  592   
  593       public float getCharAngle(int logicalIndex) {
  594   
  595           return getCoreMetricsAt(logicalIndex).italicAngle;
  596       }
  597   
  598       public CoreMetrics getCoreMetricsAt(int logicalIndex) {
  599   
  600           if (logicalIndex < 0) {
  601               throw new IllegalArgumentException("Negative logicalIndex.");
  602           }
  603   
  604           if (logicalIndex > fCharsLimit - fCharsStart) {
  605               throw new IllegalArgumentException("logicalIndex too large.");
  606           }
  607   
  608           int currentTlc = 0;
  609           int tlcStart = 0;
  610           int tlcLimit = 0;
  611   
  612           do {
  613               tlcLimit += fComponents[currentTlc].getNumCharacters();
  614               if (tlcLimit > logicalIndex) {
  615                   break;
  616               }
  617               ++currentTlc;
  618               tlcStart = tlcLimit;
  619           } while(currentTlc < fComponents.length);
  620   
  621           return fComponents[currentTlc].getCoreMetrics();
  622       }
  623   
  624       public float getCharAscent(int logicalIndex) {
  625   
  626           return getCoreMetricsAt(logicalIndex).ascent;
  627       }
  628   
  629       public float getCharDescent(int logicalIndex) {
  630   
  631           return getCoreMetricsAt(logicalIndex).descent;
  632       }
  633   
  634       public float getCharShift(int logicalIndex) {
  635   
  636           return getCoreMetricsAt(logicalIndex).ssOffset;
  637       }
  638   
  639       private float applyFunctionAtIndex(int logicalIndex, Function f) {
  640   
  641           if (logicalIndex < 0) {
  642               throw new IllegalArgumentException("Negative logicalIndex.");
  643           }
  644   
  645           int tlcStart = 0;
  646   
  647           for(int i=0; i < fComponents.length; i++) {
  648   
  649               int tlcLimit = tlcStart + fComponents[i].getNumCharacters();
  650               if (tlcLimit > logicalIndex) {
  651                   return f.computeFunction(this, i, logicalIndex - tlcStart);
  652               }
  653               else {
  654                   tlcStart = tlcLimit;
  655               }
  656           }
  657   
  658           throw new IllegalArgumentException("logicalIndex too large.");
  659       }
  660   
  661       public float getCharAdvance(int logicalIndex) {
  662   
  663           return applyFunctionAtIndex(logicalIndex, fgAdvanceF);
  664       }
  665   
  666       public float getCharXPosition(int logicalIndex) {
  667   
  668           return applyFunctionAtIndex(logicalIndex, fgXPositionF);
  669       }
  670   
  671       public float getCharYPosition(int logicalIndex) {
  672   
  673           return applyFunctionAtIndex(logicalIndex, fgYPositionF);
  674       }
  675   
  676       public float getCharLinePosition(int logicalIndex) {
  677   
  678           return getCharXPosition(logicalIndex);
  679       }
  680   
  681       public float getCharLinePosition(int logicalIndex, boolean leading) {
  682           Function f = isCharLTR(logicalIndex) == leading ? fgXPositionF : fgPosAdvF;
  683           return applyFunctionAtIndex(logicalIndex, f);
  684       }
  685   
  686       public boolean caretAtOffsetIsValid(int offset) {
  687   
  688           if (offset < 0) {
  689               throw new IllegalArgumentException("Negative offset.");
  690           }
  691   
  692           int tlcStart = 0;
  693   
  694           for(int i=0; i < fComponents.length; i++) {
  695   
  696               int tlcLimit = tlcStart + fComponents[i].getNumCharacters();
  697               if (tlcLimit > offset) {
  698                   return fComponents[i].caretAtOffsetIsValid(offset-tlcStart);
  699               }
  700               else {
  701                   tlcStart = tlcLimit;
  702               }
  703           }
  704   
  705           throw new IllegalArgumentException("logicalIndex too large.");
  706       }
  707   
  708       /**
  709        * map a component visual index to the logical index.
  710        */
  711       private int getComponentLogicalIndex(int vi) {
  712           if (fComponentVisualOrder == null) {
  713               return vi;
  714           }
  715           return fComponentVisualOrder[vi];
  716       }
  717   
  718       /**
  719        * map a component logical index to the visual index.
  720        */
  721       private int getComponentVisualIndex(int li) {
  722           if (fComponentVisualOrder == null) {
  723                   return li;
  724           }
  725           for (int i = 0; i < fComponentVisualOrder.length; ++i) {
  726                   if (fComponentVisualOrder[i] == li) {
  727                       return i;
  728                   }
  729           }
  730           throw new IndexOutOfBoundsException("bad component index: " + li);
  731       }
  732   
  733       public Rectangle2D getCharBounds(int logicalIndex) {
  734   
  735           if (logicalIndex < 0) {
  736               throw new IllegalArgumentException("Negative logicalIndex.");
  737           }
  738   
  739           int tlcStart = 0;
  740   
  741           for (int i=0; i < fComponents.length; i++) {
  742   
  743               int tlcLimit = tlcStart + fComponents[i].getNumCharacters();
  744               if (tlcLimit > logicalIndex) {
  745   
  746                   TextLineComponent tlc = fComponents[i];
  747                   int indexInTlc = logicalIndex - tlcStart;
  748                   Rectangle2D chBounds = tlc.getCharVisualBounds(indexInTlc);
  749   
  750                           int vi = getComponentVisualIndex(i);
  751                   chBounds.setRect(chBounds.getX() + locs[vi * 2],
  752                                    chBounds.getY() + locs[vi * 2 + 1],
  753                                    chBounds.getWidth(),
  754                                    chBounds.getHeight());
  755                   return chBounds;
  756               }
  757               else {
  758                   tlcStart = tlcLimit;
  759               }
  760           }
  761   
  762           throw new IllegalArgumentException("logicalIndex too large.");
  763       }
  764   
  765       private float getComponentShift(int index) {
  766           CoreMetrics cm = fComponents[index].getCoreMetrics();
  767           return cm.effectiveBaselineOffset(fBaselineOffsets);
  768       }
  769   
  770       public void draw(Graphics2D g2, float x, float y) {
  771           if (lp == null) {
  772               for (int i = 0, n = 0; i < fComponents.length; i++, n += 2) {
  773                   TextLineComponent tlc = fComponents[getComponentLogicalIndex(i)];
  774                   tlc.draw(g2, locs[n] + x, locs[n+1] + y);
  775               }
  776           } else {
  777               AffineTransform oldTx = g2.getTransform();
  778               Point2D.Float pt = new Point2D.Float();
  779               for (int i = 0, n = 0; i < fComponents.length; i++, n += 2) {
  780                   TextLineComponent tlc = fComponents[getComponentLogicalIndex(i)];
  781                   lp.pathToPoint(locs[n], locs[n+1], false, pt);
  782                   pt.x += x;
  783                   pt.y += y;
  784                   AffineTransform at = tlc.getBaselineTransform();
  785   
  786                   if (at != null) {
  787                       g2.translate(pt.x - at.getTranslateX(), pt.y - at.getTranslateY());
  788                       g2.transform(at);
  789                       tlc.draw(g2, 0, 0);
  790                       g2.setTransform(oldTx);
  791                   } else {
  792                       tlc.draw(g2, pt.x, pt.y);
  793                   }
  794               }
  795           }
  796       }
  797   
  798       /**
  799        * Return the union of the visual bounds of all the components.
  800        * This incorporates the path.  It does not include logical
  801        * bounds (used by carets).
  802        */
  803       public Rectangle2D getVisualBounds() {
  804           Rectangle2D result = null;
  805   
  806           for (int i = 0, n = 0; i < fComponents.length; i++, n += 2) {
  807               TextLineComponent tlc = fComponents[getComponentLogicalIndex(i)];
  808               Rectangle2D r = tlc.getVisualBounds();
  809   
  810               Point2D.Float pt = new Point2D.Float(locs[n], locs[n+1]);
  811               if (lp == null) {
  812                   r.setRect(r.getMinX() + pt.x, r.getMinY() + pt.y,
  813                             r.getWidth(), r.getHeight());
  814               } else {
  815                   lp.pathToPoint(pt, false, pt);
  816   
  817                   AffineTransform at = tlc.getBaselineTransform();
  818                   if (at != null) {
  819                       AffineTransform tx = AffineTransform.getTranslateInstance
  820                           (pt.x - at.getTranslateX(), pt.y - at.getTranslateY());
  821                       tx.concatenate(at);
  822                       r = tx.createTransformedShape(r).getBounds2D();
  823                   } else {
  824                       r.setRect(r.getMinX() + pt.x, r.getMinY() + pt.y,
  825                                 r.getWidth(), r.getHeight());
  826                   }
  827               }
  828   
  829               if (result == null) {
  830                   result = r;
  831               } else {
  832                   result.add(r);
  833               }
  834           }
  835   
  836           if (result == null) {
  837               result = new Rectangle2D.Float(Float.MAX_VALUE, Float.MAX_VALUE, Float.MIN_VALUE, Float.MIN_VALUE);
  838           }
  839   
  840           return result;
  841       }
  842   
  843       public Rectangle2D getItalicBounds() {
  844   
  845           float left = Float.MAX_VALUE, right = -Float.MAX_VALUE;
  846           float top = Float.MAX_VALUE, bottom = -Float.MAX_VALUE;
  847   
  848           for (int i=0, n = 0; i < fComponents.length; i++, n += 2) {
  849               TextLineComponent tlc = fComponents[getComponentLogicalIndex(i)];
  850   
  851               Rectangle2D tlcBounds = tlc.getItalicBounds();
  852               float x = locs[n];
  853               float y = locs[n+1];
  854   
  855               left = Math.min(left, x + (float)tlcBounds.getX());
  856               right = Math.max(right, x + (float)tlcBounds.getMaxX());
  857   
  858               top = Math.min(top, y + (float)tlcBounds.getY());
  859               bottom = Math.max(bottom, y + (float)tlcBounds.getMaxY());
  860           }
  861   
  862           return new Rectangle2D.Float(left, top, right-left, bottom-top);
  863       }
  864   
  865       public Shape getOutline(AffineTransform tx) {
  866   
  867           GeneralPath dstShape = new GeneralPath(GeneralPath.WIND_NON_ZERO);
  868   
  869           for (int i=0, n = 0; i < fComponents.length; i++, n += 2) {
  870               TextLineComponent tlc = fComponents[getComponentLogicalIndex(i)];
  871   
  872               dstShape.append(tlc.getOutline(locs[n], locs[n+1]), false);
  873           }
  874   
  875           if (tx != null) {
  876               dstShape.transform(tx);
  877           }
  878           return dstShape;
  879       }
  880   
  881       public int hashCode() {
  882           return (fComponents.length << 16) ^
  883                       (fComponents[0].hashCode() << 3) ^ (fCharsLimit-fCharsStart);
  884       }
  885   
  886       public String toString() {
  887           StringBuilder buf = new StringBuilder();
  888   
  889           for (int i = 0; i < fComponents.length; i++) {
  890               buf.append(fComponents[i]);
  891           }
  892   
  893           return buf.toString();
  894       }
  895   
  896       /**
  897        * Create a TextLine from the text.  The Font must be able to
  898        * display all of the text.
  899        * attributes==null is equivalent to using an empty Map for
  900        * attributes
  901        */
  902       public static TextLine fastCreateTextLine(FontRenderContext frc,
  903                                                 char[] chars,
  904                                                 Font font,
  905                                                 CoreMetrics lm,
  906                                                 Map attributes) {
  907   
  908           boolean isDirectionLTR = true;
  909           byte[] levels = null;
  910           int[] charsLtoV = null;
  911           Bidi bidi = null;
  912           int characterCount = chars.length;
  913   
  914           boolean requiresBidi = false;
  915           byte[] embs = null;
  916   
  917           AttributeValues values = null;
  918           if (attributes != null) {
  919               values = AttributeValues.fromMap(attributes);
  920               if (values.getRunDirection() >= 0) {
  921                   isDirectionLTR = values.getRunDirection() == 0;
  922                   requiresBidi = !isDirectionLTR;
  923               }
  924               if (values.getBidiEmbedding() != 0) {
  925                   requiresBidi = true;
  926                   byte level = (byte)values.getBidiEmbedding();
  927                   embs = new byte[characterCount];
  928                   for (int i = 0; i < embs.length; ++i) {
  929                       embs[i] = level;
  930                   }
  931               }
  932           }
  933   
  934           // dlf: get baseRot from font for now???
  935   
  936           if (!requiresBidi) {
  937               requiresBidi = Bidi.requiresBidi(chars, 0, chars.length);
  938           }
  939   
  940           if (requiresBidi) {
  941             int bidiflags = values == null
  942                 ? Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT
  943                 : values.getRunDirection();
  944   
  945             bidi = new Bidi(chars, 0, embs, 0, chars.length, bidiflags);
  946             if (!bidi.isLeftToRight()) {
  947                 levels = BidiUtils.getLevels(bidi);
  948                 int[] charsVtoL = BidiUtils.createVisualToLogicalMap(levels);
  949                 charsLtoV = BidiUtils.createInverseMap(charsVtoL);
  950                 isDirectionLTR = bidi.baseIsLeftToRight();
  951             }
  952           }
  953   
  954           Decoration decorator = Decoration.getDecoration(values);
  955   
  956           int layoutFlags = 0; // no extra info yet, bidi determines run and line direction
  957           TextLabelFactory factory = new TextLabelFactory(frc, chars, bidi, layoutFlags);
  958   
  959           TextLineComponent[] components = new TextLineComponent[1];
  960   
  961           components = createComponentsOnRun(0, chars.length,
  962                                              chars,
  963                                              charsLtoV, levels,
  964                                              factory, font, lm,
  965                                              frc,
  966                                              decorator,
  967                                              components,
  968                                              0);
  969   
  970           int numComponents = components.length;
  971           while (components[numComponents-1] == null) {
  972               numComponents -= 1;
  973           }
  974   
  975           if (numComponents != components.length) {
  976               TextLineComponent[] temp = new TextLineComponent[numComponents];
  977               System.arraycopy(components, 0, temp, 0, numComponents);
  978               components = temp;
  979           }
  980   
  981           return new TextLine(frc, components, lm.baselineOffsets,
  982                               chars, 0, chars.length, charsLtoV, levels, isDirectionLTR);
  983       }
  984   
  985       private static TextLineComponent[] expandArray(TextLineComponent[] orig) {
  986   
  987           TextLineComponent[] newComponents = new TextLineComponent[orig.length + 8];
  988           System.arraycopy(orig, 0, newComponents, 0, orig.length);
  989   
  990           return newComponents;
  991       }
  992   
  993       /**
  994        * Returns an array in logical order of the TextLineComponents on
  995        * the text in the given range, with the given attributes.
  996        */
  997       public static TextLineComponent[] createComponentsOnRun(int runStart,
  998                                                               int runLimit,
  999                                                               char[] chars,
 1000                                                               int[] charsLtoV,
 1001                                                               byte[] levels,
 1002                                                               TextLabelFactory factory,
 1003                                                               Font font,
 1004                                                               CoreMetrics cm,
 1005                                                               FontRenderContext frc,
 1006                                                               Decoration decorator,
 1007                                                               TextLineComponent[] components,
 1008                                                               int numComponents) {
 1009   
 1010           int pos = runStart;
 1011           do {
 1012               int chunkLimit = firstVisualChunk(charsLtoV, levels, pos, runLimit); // <= displayLimit
 1013   
 1014               do {
 1015                   int startPos = pos;
 1016                   int lmCount;
 1017   
 1018                   if (cm == null) {
 1019                       LineMetrics lineMetrics = font.getLineMetrics(chars, startPos, chunkLimit, frc);
 1020                       cm = CoreMetrics.get(lineMetrics);
 1021                       lmCount = lineMetrics.getNumChars();
 1022                   }
 1023                   else {
 1024                       lmCount = (chunkLimit-startPos);
 1025                   }
 1026   
 1027                   TextLineComponent nextComponent =
 1028                       factory.createExtended(font, cm, decorator, startPos, startPos + lmCount);
 1029   
 1030                   ++numComponents;
 1031                   if (numComponents >= components.length) {
 1032                       components = expandArray(components);
 1033                   }
 1034   
 1035                   components[numComponents-1] = nextComponent;
 1036   
 1037                   pos += lmCount;
 1038               } while (pos < chunkLimit);
 1039   
 1040           } while (pos < runLimit);
 1041   
 1042           return components;
 1043       }
 1044   
 1045       /**
 1046        * Returns an array (in logical order) of the TextLineComponents representing
 1047        * the text.  The components are both logically and visually contiguous.
 1048        */
 1049       public static TextLineComponent[] getComponents(StyledParagraph styledParagraph,
 1050                                                       char[] chars,
 1051                                                       int textStart,
 1052                                                       int textLimit,
 1053                                                       int[] charsLtoV,
 1054                                                       byte[] levels,
 1055                                                       TextLabelFactory factory) {
 1056   
 1057           FontRenderContext frc = factory.getFontRenderContext();
 1058   
 1059           int numComponents = 0;
 1060           TextLineComponent[] tempComponents = new TextLineComponent[1];
 1061   
 1062           int pos = textStart;
 1063           do {
 1064               int runLimit = Math.min(styledParagraph.getRunLimit(pos), textLimit);
 1065   
 1066               Decoration decorator = styledParagraph.getDecorationAt(pos);
 1067   
 1068               Object graphicOrFont = styledParagraph.getFontOrGraphicAt(pos);
 1069   
 1070               if (graphicOrFont instanceof GraphicAttribute) {
 1071                   // AffineTransform baseRot = styledParagraph.getBaselineRotationAt(pos);
 1072                   // !!! For now, let's assign runs of text with both fonts and graphic attributes
 1073                   // a null rotation (e.g. the baseline rotation goes away when a graphic
 1074                   // is applied.
 1075                   AffineTransform baseRot = null;
 1076                   GraphicAttribute graphicAttribute = (GraphicAttribute) graphicOrFont;
 1077                   do {
 1078                       int chunkLimit = firstVisualChunk(charsLtoV, levels,
 1079                                       pos, runLimit);
 1080   
 1081                       GraphicComponent nextGraphic =
 1082                           new GraphicComponent(graphicAttribute, decorator, charsLtoV, levels, pos, chunkLimit, baseRot);
 1083                       pos = chunkLimit;
 1084   
 1085                       ++numComponents;
 1086                       if (numComponents >= tempComponents.length) {
 1087                           tempComponents = expandArray(tempComponents);
 1088                       }
 1089   
 1090                       tempComponents[numComponents-1] = nextGraphic;
 1091   
 1092                   } while(pos < runLimit);
 1093               }
 1094               else {
 1095                   Font font = (Font) graphicOrFont;
 1096   
 1097                   tempComponents = createComponentsOnRun(pos, runLimit,
 1098                                                           chars,
 1099                                                           charsLtoV, levels,
 1100                                                           factory, font, null,
 1101                                                           frc,
 1102                                                           decorator,
 1103                                                           tempComponents,
 1104                                                           numComponents);
 1105                   pos = runLimit;
 1106                   numComponents = tempComponents.length;
 1107                   while (tempComponents[numComponents-1] == null) {
 1108                       numComponents -= 1;
 1109                   }
 1110               }
 1111   
 1112           } while (pos < textLimit);
 1113   
 1114           TextLineComponent[] components;
 1115           if (tempComponents.length == numComponents) {
 1116               components = tempComponents;
 1117           }
 1118           else {
 1119               components = new TextLineComponent[numComponents];
 1120               System.arraycopy(tempComponents, 0, components, 0, numComponents);
 1121           }
 1122   
 1123           return components;
 1124       }
 1125   
 1126       /**
 1127        * Create a TextLine from the Font and character data over the
 1128        * range.  The range is relative to both the StyledParagraph and the
 1129        * character array.
 1130        */
 1131       public static TextLine createLineFromText(char[] chars,
 1132                                                 StyledParagraph styledParagraph,
 1133                                                 TextLabelFactory factory,
 1134                                                 boolean isDirectionLTR,
 1135                                                 float[] baselineOffsets) {
 1136   
 1137           factory.setLineContext(0, chars.length);
 1138   
 1139           Bidi lineBidi = factory.getLineBidi();
 1140           int[] charsLtoV = null;
 1141           byte[] levels = null;
 1142   
 1143           if (lineBidi != null) {
 1144               levels = BidiUtils.getLevels(lineBidi);
 1145               int[] charsVtoL = BidiUtils.createVisualToLogicalMap(levels);
 1146               charsLtoV = BidiUtils.createInverseMap(charsVtoL);
 1147           }
 1148   
 1149           TextLineComponent[] components =
 1150               getComponents(styledParagraph, chars, 0, chars.length, charsLtoV, levels, factory);
 1151   
 1152           return new TextLine(factory.getFontRenderContext(), components, baselineOffsets,
 1153                               chars, 0, chars.length, charsLtoV, levels, isDirectionLTR);
 1154       }
 1155   
 1156       /**
 1157        * Compute the components order from the given components array and
 1158        * logical-to-visual character mapping.  May return null if canonical.
 1159        */
 1160       private static int[] computeComponentOrder(TextLineComponent[] components,
 1161                                                  int[] charsLtoV) {
 1162   
 1163           /*
 1164            * Create a visual ordering for the glyph sets.  The important thing
 1165            * here is that the values have the proper rank with respect to
 1166            * each other, not the exact values.  For example, the first glyph
 1167            * set that appears visually should have the lowest value.  The last
 1168            * should have the highest value.  The values are then normalized
 1169            * to map 1-1 with positions in glyphs.
 1170            *
 1171            */
 1172           int[] componentOrder = null;
 1173           if (charsLtoV != null && components.length > 1) {
 1174               componentOrder = new int[components.length];
 1175               int gStart = 0;
 1176               for (int i = 0; i < components.length; i++) {
 1177                   componentOrder[i] = charsLtoV[gStart];
 1178                   gStart += components[i].getNumCharacters();
 1179               }
 1180   
 1181               componentOrder = BidiUtils.createContiguousOrder(componentOrder);
 1182               componentOrder = BidiUtils.createInverseMap(componentOrder);
 1183           }
 1184           return componentOrder;
 1185       }
 1186   
 1187   
 1188       /**
 1189        * Create a TextLine from the text.  chars is just the text in the iterator.
 1190        */
 1191       public static TextLine standardCreateTextLine(FontRenderContext frc,
 1192                                                     AttributedCharacterIterator text,
 1193                                                     char[] chars,
 1194                                                     float[] baselineOffsets) {
 1195   
 1196           StyledParagraph styledParagraph = new StyledParagraph(text, chars);
 1197           Bidi bidi = new Bidi(text);
 1198           if (bidi.isLeftToRight()) {
 1199               bidi = null;
 1200           }
 1201           int layoutFlags = 0; // no extra info yet, bidi determines run and line direction
 1202           TextLabelFactory factory = new TextLabelFactory(frc, chars, bidi, layoutFlags);
 1203   
 1204           boolean isDirectionLTR = true;
 1205           if (bidi != null) {
 1206               isDirectionLTR = bidi.baseIsLeftToRight();
 1207           }
 1208           return createLineFromText(chars, styledParagraph, factory, isDirectionLTR, baselineOffsets);
 1209       }
 1210   
 1211   
 1212   
 1213       /*
 1214        * A utility to get a range of text that is both logically and visually
 1215        * contiguous.
 1216        * If the entire range is ok, return limit, otherwise return the first
 1217        * directional change after start.  We could do better than this, but
 1218        * it doesn't seem worth it at the moment.
 1219       private static int firstVisualChunk(int order[], byte direction[],
 1220                                           int start, int limit)
 1221       {
 1222           if (order != null) {
 1223               int min = order[start];
 1224               int max = order[start];
 1225               int count = limit - start;
 1226               for (int i = start + 1; i < limit; i++) {
 1227                   min = Math.min(min, order[i]);
 1228                   max = Math.max(max, order[i]);
 1229                   if (max - min >= count) {
 1230                       if (direction != null) {
 1231                           byte baseLevel = direction[start];
 1232                           for (int j = start + 1; j < i; j++) {
 1233                               if (direction[j] != baseLevel) {
 1234                                   return j;
 1235                               }
 1236                           }
 1237                       }
 1238                       return i;
 1239                   }
 1240               }
 1241           }
 1242           return limit;
 1243       }
 1244        */
 1245   
 1246       /**
 1247        * When this returns, the ACI's current position will be at the start of the
 1248        * first run which does NOT contain a GraphicAttribute.  If no such run exists
 1249        * the ACI's position will be at the end, and this method will return false.
 1250        */
 1251       static boolean advanceToFirstFont(AttributedCharacterIterator aci) {
 1252   
 1253           for (char ch = aci.first(); ch != aci.DONE; ch = aci.setIndex(aci.getRunLimit())) {
 1254   
 1255               if (aci.getAttribute(TextAttribute.CHAR_REPLACEMENT) == null) {
 1256                   return true;
 1257               }
 1258           }
 1259   
 1260           return false;
 1261       }
 1262   
 1263       static float[] getNormalizedOffsets(float[] baselineOffsets, byte baseline) {
 1264   
 1265           if (baselineOffsets[baseline] != 0) {
 1266               float base = baselineOffsets[baseline];
 1267               float[] temp = new float[baselineOffsets.length];
 1268               for (int i = 0; i < temp.length; i++)
 1269                   temp[i] = baselineOffsets[i] - base;
 1270               baselineOffsets = temp;
 1271           }
 1272           return baselineOffsets;
 1273       }
 1274   
 1275       static Font getFontAtCurrentPos(AttributedCharacterIterator aci) {
 1276   
 1277           Object value = aci.getAttribute(TextAttribute.FONT);
 1278           if (value != null) {
 1279               return (Font) value;
 1280           }
 1281           if (aci.getAttribute(TextAttribute.FAMILY) != null) {
 1282               return Font.getFont(aci.getAttributes());
 1283           }
 1284   
 1285           int ch = CodePointIterator.create(aci).next();
 1286           if (ch != CodePointIterator.DONE) {
 1287               FontResolver resolver = FontResolver.getInstance();
 1288               return resolver.getFont(resolver.getFontIndex(ch), aci.getAttributes());
 1289           }
 1290           return null;
 1291       }
 1292   
 1293     /*
 1294      * The new version requires that chunks be at the same level.
 1295      */
 1296       private static int firstVisualChunk(int order[], byte direction[],
 1297                                           int start, int limit)
 1298       {
 1299           if (order != null && direction != null) {
 1300             byte dir = direction[start];
 1301             while (++start < limit && direction[start] == dir) {}
 1302             return start;
 1303           }
 1304           return limit;
 1305       }
 1306   
 1307     /*
 1308      * create a new line with characters between charStart and charLimit
 1309      * justified using the provided width and ratio.
 1310      */
 1311       public TextLine getJustifiedLine(float justificationWidth, float justifyRatio, int justStart, int justLimit) {
 1312   
 1313           TextLineComponent[] newComponents = new TextLineComponent[fComponents.length];
 1314           System.arraycopy(fComponents, 0, newComponents, 0, fComponents.length);
 1315   
 1316           float leftHang = 0;
 1317           float adv = 0;
 1318           float justifyDelta = 0;
 1319           boolean rejustify = false;
 1320           do {
 1321               adv = getAdvanceBetween(newComponents, 0, characterCount());
 1322   
 1323               // all characters outside the justification range must be in the base direction
 1324               // of the layout, otherwise justification makes no sense.
 1325   
 1326               float justifyAdvance = getAdvanceBetween(newComponents, justStart, justLimit);
 1327   
 1328               // get the actual justification delta
 1329               justifyDelta = (justificationWidth - justifyAdvance) * justifyRatio;
 1330   
 1331               // generate an array of GlyphJustificationInfo records to pass to
 1332               // the justifier.  Array is visually ordered.
 1333   
 1334               // get positions that each component will be using
 1335               int[] infoPositions = new int[newComponents.length];
 1336               int infoCount = 0;
 1337               for (int visIndex = 0; visIndex < newComponents.length; visIndex++) {
 1338                       int logIndex = getComponentLogicalIndex(visIndex);
 1339                   infoPositions[logIndex] = infoCount;
 1340                   infoCount += newComponents[logIndex].getNumJustificationInfos();
 1341               }
 1342               GlyphJustificationInfo[] infos = new GlyphJustificationInfo[infoCount];
 1343   
 1344               // get justification infos
 1345               int compStart = 0;
 1346               for (int i = 0; i < newComponents.length; i++) {
 1347                   TextLineComponent comp = newComponents[i];
 1348                   int compLength = comp.getNumCharacters();
 1349                   int compLimit = compStart + compLength;
 1350                   if (compLimit > justStart) {
 1351                       int rangeMin = Math.max(0, justStart - compStart);
 1352                       int rangeMax = Math.min(compLength, justLimit - compStart);
 1353                       comp.getJustificationInfos(infos, infoPositions[i], rangeMin, rangeMax);
 1354   
 1355                       if (compLimit >= justLimit) {
 1356                           break;
 1357                       }
 1358                   }
 1359               }
 1360   
 1361               // records are visually ordered, and contiguous, so start and end are
 1362               // simply the places where we didn't fetch records
 1363               int infoStart = 0;
 1364               int infoLimit = infoCount;
 1365               while (infoStart < infoLimit && infos[infoStart] == null) {
 1366                   ++infoStart;
 1367               }
 1368   
 1369               while (infoLimit > infoStart && infos[infoLimit - 1] == null) {
 1370                   --infoLimit;
 1371               }
 1372   
 1373               // invoke justifier on the records
 1374               TextJustifier justifier = new TextJustifier(infos, infoStart, infoLimit);
 1375   
 1376               float[] deltas = justifier.justify(justifyDelta);
 1377   
 1378               boolean canRejustify = rejustify == false;
 1379               boolean wantRejustify = false;
 1380               boolean[] flags = new boolean[1];
 1381   
 1382               // apply justification deltas
 1383               compStart = 0;
 1384               for (int i = 0; i < newComponents.length; i++) {
 1385                   TextLineComponent comp = newComponents[i];
 1386                   int compLength = comp.getNumCharacters();
 1387                   int compLimit = compStart + compLength;
 1388                   if (compLimit > justStart) {
 1389                       int rangeMin = Math.max(0, justStart - compStart);
 1390                       int rangeMax = Math.min(compLength, justLimit - compStart);
 1391                       newComponents[i] = comp.applyJustificationDeltas(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, fChars, fCharsStart,
 1405                               fCharsLimit, fCharLogicalOrder, fCharLevels,
 1406                               fIsDirectionLTR);
 1407       }
 1408   
 1409       // return the sum of the advances of text between the logical start and limit
 1410       public static float getAdvanceBetween(TextLineComponent[] components, int start, int limit) {
 1411           float advance = 0;
 1412   
 1413           int tlcStart = 0;
 1414           for(int i = 0; i < components.length; i++) {
 1415               TextLineComponent comp = components[i];
 1416   
 1417               int tlcLength = comp.getNumCharacters();
 1418               int tlcLimit = tlcStart + tlcLength;
 1419               if (tlcLimit > start) {
 1420                   int measureStart = Math.max(0, start - tlcStart);
 1421                   int measureLimit = Math.min(tlcLength, limit - tlcStart);
 1422                   advance += comp.getAdvanceBetween(measureStart, measureLimit);
 1423                   if (tlcLimit >= limit) {
 1424                       break;
 1425                   }
 1426               }
 1427   
 1428               tlcStart = tlcLimit;
 1429           }
 1430   
 1431           return advance;
 1432       }
 1433   
 1434       LayoutPathImpl getLayoutPath() {
 1435           return lp;
 1436       }
 1437   }

Save This Page
Home » openjdk-7 » java » awt » font » [javadoc | source]