< prev index next >

src/java.desktop/share/classes/java/awt/font/TextLayout.java

Print this page




  53 import java.awt.geom.Point2D;
  54 import java.awt.geom.Rectangle2D;
  55 import java.text.AttributedString;
  56 import java.text.AttributedCharacterIterator;
  57 import java.text.AttributedCharacterIterator.Attribute;
  58 import java.text.CharacterIterator;
  59 import java.util.Map;
  60 import java.util.HashMap;
  61 import java.util.Hashtable;
  62 import sun.font.AttributeValues;
  63 import sun.font.CodePointIterator;
  64 import sun.font.CoreMetrics;
  65 import sun.font.Decoration;
  66 import sun.font.FontLineMetrics;
  67 import sun.font.FontResolver;
  68 import sun.font.GraphicComponent;
  69 import sun.font.LayoutPathImpl;
  70 
  71 /**
  72  *
  73  * <code>TextLayout</code> is an immutable graphical representation of styled
  74  * character data.
  75  * <p>
  76  * It provides the following capabilities:
  77  * <ul>
  78  * <li>implicit bidirectional analysis and reordering,
  79  * <li>cursor positioning and movement, including split cursors for
  80  * mixed directional text,
  81  * <li>highlighting, including both logical and visual highlighting
  82  * for mixed directional text,
  83  * <li>multiple baselines (roman, hanging, and centered),
  84  * <li>hit testing,
  85  * <li>justification,
  86  * <li>default font substitution,
  87  * <li>metric information such as ascent, descent, and advance, and
  88  * <li>rendering
  89  * </ul>
  90  * <p>
  91  * A <code>TextLayout</code> object can be rendered using
  92  * its <code>draw</code> method.
  93  * <p>
  94  * <code>TextLayout</code> can be constructed either directly or through
  95  * the use of a {@link LineBreakMeasurer}.  When constructed directly, the
  96  * source text represents a single paragraph.  <code>LineBreakMeasurer</code>
  97  * allows styled text to be broken into lines that fit within a particular
  98  * width.  See the <code>LineBreakMeasurer</code> documentation for more
  99  * information.
 100  * <p>
 101  * <code>TextLayout</code> construction logically proceeds as follows:
 102  * <ul>
 103  * <li>paragraph attributes are extracted and examined,
 104  * <li>text is analyzed for bidirectional reordering, and reordering
 105  * information is computed if needed,
 106  * <li>text is segmented into style runs
 107  * <li>fonts are chosen for style runs, first by using a font if the
 108  * attribute {@link TextAttribute#FONT} is present, otherwise by computing
 109  * a default font using the attributes that have been defined
 110  * <li>if text is on multiple baselines, the runs or subruns are further
 111  * broken into subruns sharing a common baseline,
 112  * <li>glyphvectors are generated for each run using the chosen font,
 113  * <li>final bidirectional reordering is performed on the glyphvectors
 114  * </ul>
 115  * <p>
 116  * All graphical information returned from a <code>TextLayout</code>
 117  * object's methods is relative to the origin of the
 118  * <code>TextLayout</code>, which is the intersection of the
 119  * <code>TextLayout</code> object's baseline with its left edge.  Also,
 120  * coordinates passed into a <code>TextLayout</code> object's methods
 121  * are assumed to be relative to the <code>TextLayout</code> object's
 122  * origin.  Clients usually need to translate between a
 123  * <code>TextLayout</code> object's coordinate system and the coordinate
 124  * system in another object (such as a
 125  * {@link java.awt.Graphics Graphics} object).
 126  * <p>
 127  * <code>TextLayout</code> objects are constructed from styled text,
 128  * but they do not retain a reference to their source text.  Thus,
 129  * changes in the text previously used to generate a <code>TextLayout</code>
 130  * do not affect the <code>TextLayout</code>.
 131  * <p>
 132  * Three methods on a <code>TextLayout</code> object
 133  * (<code>getNextRightHit</code>, <code>getNextLeftHit</code>, and
 134  * <code>hitTestChar</code>) return instances of {@link TextHitInfo}.
 135  * The offsets contained in these <code>TextHitInfo</code> objects
 136  * are relative to the start of the <code>TextLayout</code>, <b>not</b>
 137  * to the text used to create the <code>TextLayout</code>.  Similarly,
 138  * <code>TextLayout</code> methods that accept <code>TextHitInfo</code>
 139  * instances as parameters expect the <code>TextHitInfo</code> object's
 140  * offsets to be relative to the <code>TextLayout</code>, not to any
 141  * underlying text storage model.
 142  * <p>
 143  * <strong>Examples</strong>:<p>
 144  * Constructing and drawing a <code>TextLayout</code> and its bounding
 145  * rectangle:
 146  * <blockquote><pre>
 147  *   Graphics2D g = ...;
 148  *   Point2D loc = ...;
 149  *   Font font = Font.getFont("Helvetica-bold-italic");
 150  *   FontRenderContext frc = g.getFontRenderContext();
 151  *   TextLayout layout = new TextLayout("This is a string", font, frc);
 152  *   layout.draw(g, (float)loc.getX(), (float)loc.getY());
 153  *
 154  *   Rectangle2D bounds = layout.getBounds();
 155  *   bounds.setRect(bounds.getX()+loc.getX(),
 156  *                  bounds.getY()+loc.getY(),
 157  *                  bounds.getWidth(),
 158  *                  bounds.getHeight());
 159  *   g.draw(bounds);
 160  * </pre>
 161  * </blockquote>
 162  * <p>
 163  * Hit-testing a <code>TextLayout</code> (determining which character is at
 164  * a particular graphical location):
 165  * <blockquote><pre>
 166  *   Point2D click = ...;
 167  *   TextHitInfo hit = layout.hitTestChar(
 168  *                         (float) (click.getX() - loc.getX()),
 169  *                         (float) (click.getY() - loc.getY()));
 170  * </pre>
 171  * </blockquote>
 172  * <p>
 173  * Responding to a right-arrow key press:
 174  * <blockquote><pre>
 175  *   int insertionIndex = ...;
 176  *   TextHitInfo next = layout.getNextRightHit(insertionIndex);
 177  *   if (next != null) {
 178  *       // translate graphics to origin of layout on screen
 179  *       g.translate(loc.getX(), loc.getY());
 180  *       Shape[] carets = layout.getCaretShapes(next.getInsertionIndex());
 181  *       g.draw(carets[0]);
 182  *       if (carets[1] != null) {
 183  *           g.draw(carets[1]);


 186  * </pre></blockquote>
 187  * <p>
 188  * Drawing a selection range corresponding to a substring in the source text.
 189  * The selected area may not be visually contiguous:
 190  * <blockquote><pre>
 191  *   // selStart, selLimit should be relative to the layout,
 192  *   // not to the source text
 193  *
 194  *   int selStart = ..., selLimit = ...;
 195  *   Color selectionColor = ...;
 196  *   Shape selection = layout.getLogicalHighlightShape(selStart, selLimit);
 197  *   // selection may consist of disjoint areas
 198  *   // graphics is assumed to be translated to origin of layout
 199  *   g.setColor(selectionColor);
 200  *   g.fill(selection);
 201  * </pre></blockquote>
 202  * <p>
 203  * Drawing a visually contiguous selection range.  The selection range may
 204  * correspond to more than one substring in the source text.  The ranges of
 205  * the corresponding source text substrings can be obtained with
 206  * <code>getLogicalRangesForVisualSelection()</code>:
 207  * <blockquote><pre>
 208  *   TextHitInfo selStart = ..., selLimit = ...;
 209  *   Shape selection = layout.getVisualHighlightShape(selStart, selLimit);
 210  *   g.setColor(selectionColor);
 211  *   g.fill(selection);
 212  *   int[] ranges = getLogicalRangesForVisualSelection(selStart, selLimit);
 213  *   // ranges[0], ranges[1] is the first selection range,
 214  *   // ranges[2], ranges[3] is the second selection range, etc.
 215  * </pre></blockquote>
 216  * <p>
 217  * Note: Font rotations can cause text baselines to be rotated, and
 218  * multiple runs with different rotations can cause the baseline to
 219  * bend or zig-zag.  In order to account for this (rare) possibility,
 220  * some APIs are specified to return metrics and take parameters 'in
 221  * baseline-relative coordinates' (e.g. ascent, advance), and others
 222  * are in 'in standard coordinates' (e.g. getBounds).  Values in
 223  * baseline-relative coordinates map the 'x' coordinate to the
 224  * distance along the baseline, (positive x is forward along the
 225  * baseline), and the 'y' coordinate to a distance along the
 226  * perpendicular to the baseline at 'x' (positive y is 90 degrees


 282     /*
 283      * Natural bounds is used internally.  It is built on demand in
 284      * getNaturalBounds.
 285      */
 286     private Rectangle2D naturalBounds = null;
 287 
 288     /*
 289      * boundsRect encloses all of the bits this TextLayout can draw.  It
 290      * is build on demand in getBounds.
 291      */
 292     private Rectangle2D boundsRect = null;
 293 
 294     /*
 295      * flag to suppress/allow carets inside of ligatures when hit testing or
 296      * arrow-keying
 297      */
 298     private boolean caretsInLigaturesAreAllowed = false;
 299 
 300     /**
 301      * Defines a policy for determining the strong caret location.
 302      * This class contains one method, <code>getStrongCaret</code>, which
 303      * is used to specify the policy that determines the strong caret in
 304      * dual-caret text.  The strong caret is used to move the caret to the
 305      * left or right. Instances of this class can be passed to
 306      * <code>getCaretShapes</code>, <code>getNextLeftHit</code> and
 307      * <code>getNextRightHit</code> to customize strong caret
 308      * selection.
 309      * <p>
 310      * To specify alternate caret policies, subclass <code>CaretPolicy</code>
 311      * and override <code>getStrongCaret</code>.  <code>getStrongCaret</code>
 312      * should inspect the two <code>TextHitInfo</code> arguments and choose
 313      * one of them as the strong caret.
 314      * <p>
 315      * Most clients do not need to use this class.
 316      */
 317     public static class CaretPolicy {
 318 
 319         /**
 320          * Constructs a <code>CaretPolicy</code>.
 321          */
 322          public CaretPolicy() {
 323          }
 324 
 325         /**
 326          * Chooses one of the specified <code>TextHitInfo</code> instances as
 327          * a strong caret in the specified <code>TextLayout</code>.
 328          * @param hit1 a valid hit in <code>layout</code>
 329          * @param hit2 a valid hit in <code>layout</code>
 330          * @param layout the <code>TextLayout</code> in which
 331          *        <code>hit1</code> and <code>hit2</code> are used
 332          * @return <code>hit1</code> or <code>hit2</code>
 333          *        (or an equivalent <code>TextHitInfo</code>), indicating the
 334          *        strong caret.
 335          */
 336         public TextHitInfo getStrongCaret(TextHitInfo hit1,
 337                                           TextHitInfo hit2,
 338                                           TextLayout layout) {
 339 
 340             // default implementation just calls private method on layout
 341             return layout.getStrongHit(hit1, hit2);
 342         }
 343     }
 344 
 345     /**
 346      * This <code>CaretPolicy</code> is used when a policy is not specified
 347      * by the client.  With this policy, a hit on a character whose direction
 348      * is the same as the line direction is stronger than a hit on a
 349      * counterdirectional character.  If the characters' directions are
 350      * the same, a hit on the leading edge of a character is stronger
 351      * than a hit on the trailing edge of a character.
 352      */
 353     public static final CaretPolicy DEFAULT_CARET_POLICY = new CaretPolicy();
 354 
 355     /**
 356      * Constructs a <code>TextLayout</code> from a <code>String</code>
 357      * and a {@link Font}.  All the text is styled using the specified
 358      * <code>Font</code>.
 359      * <p>
 360      * The <code>String</code> must specify a single paragraph of text,
 361      * because an entire paragraph is required for the bidirectional
 362      * algorithm.
 363      * @param string the text to display
 364      * @param font a <code>Font</code> used to style the text
 365      * @param frc contains information about a graphics device which is needed
 366      *       to measure the text correctly.
 367      *       Text measurements can vary slightly depending on the
 368      *       device resolution, and attributes such as antialiasing.  This
 369      *       parameter does not specify a translation between the
 370      *       <code>TextLayout</code> and user space.
 371      */
 372     public TextLayout(String string, Font font, FontRenderContext frc) {
 373 
 374         if (font == null) {
 375             throw new IllegalArgumentException("Null font passed to TextLayout constructor.");
 376         }
 377 
 378         if (string == null) {
 379             throw new IllegalArgumentException("Null string passed to TextLayout constructor.");
 380         }
 381 
 382         if (string.length() == 0) {
 383             throw new IllegalArgumentException("Zero length string passed to TextLayout constructor.");
 384         }
 385 
 386         Map<? extends Attribute, ?> attributes = null;
 387         if (font.hasLayoutAttributes()) {
 388             attributes = font.getAttributes();
 389         }
 390 
 391         char[] text = string.toCharArray();
 392         if (sameBaselineUpTo(font, text, 0, text.length) == text.length) {
 393             fastInit(text, font, attributes, frc);
 394         } else {
 395             AttributedString as = attributes == null
 396                 ? new AttributedString(string)
 397                 : new AttributedString(string, attributes);
 398             as.addAttribute(TextAttribute.FONT, font);
 399             standardInit(as.getIterator(), text, frc);
 400         }
 401     }
 402 
 403     /**
 404      * Constructs a <code>TextLayout</code> from a <code>String</code>
 405      * and an attribute set.
 406      * <p>
 407      * All the text is styled using the provided attributes.
 408      * <p>
 409      * <code>string</code> must specify a single paragraph of text because an
 410      * entire paragraph is required for the bidirectional algorithm.
 411      * @param string the text to display
 412      * @param attributes the attributes used to style the text
 413      * @param frc contains information about a graphics device which is needed
 414      *       to measure the text correctly.
 415      *       Text measurements can vary slightly depending on the
 416      *       device resolution, and attributes such as antialiasing.  This
 417      *       parameter does not specify a translation between the
 418      *       <code>TextLayout</code> and user space.
 419      */
 420     public TextLayout(String string, Map<? extends Attribute,?> attributes,
 421                       FontRenderContext frc)
 422     {
 423         if (string == null) {
 424             throw new IllegalArgumentException("Null string passed to TextLayout constructor.");
 425         }
 426 
 427         if (attributes == null) {
 428             throw new IllegalArgumentException("Null map passed to TextLayout constructor.");
 429         }
 430 
 431         if (string.length() == 0) {
 432             throw new IllegalArgumentException("Zero length string passed to TextLayout constructor.");
 433         }
 434 
 435         char[] text = string.toCharArray();
 436         Font font = singleFont(text, 0, text.length, attributes);
 437         if (font != null) {
 438             fastInit(text, font, attributes, frc);


 471                     return null;
 472                 }
 473             } else {
 474                 FontResolver resolver = FontResolver.getInstance();
 475                 CodePointIterator iter = CodePointIterator.create(text, start, limit);
 476                 int fontIndex = resolver.nextFontRunIndex(iter);
 477                 if (iter.charIndex() == limit) {
 478                     font = resolver.getFont(fontIndex, attributes);
 479                 }
 480             }
 481         }
 482 
 483         if (sameBaselineUpTo(font, text, start, limit) != limit) {
 484             return null;
 485         }
 486 
 487         return font;
 488     }
 489 
 490     /**
 491      * Constructs a <code>TextLayout</code> from an iterator over styled text.
 492      * <p>
 493      * The iterator must specify a single paragraph of text because an
 494      * entire paragraph is required for the bidirectional
 495      * algorithm.
 496      * @param text the styled text to display
 497      * @param frc contains information about a graphics device which is needed
 498      *       to measure the text correctly.
 499      *       Text measurements can vary slightly depending on the
 500      *       device resolution, and attributes such as antialiasing.  This
 501      *       parameter does not specify a translation between the
 502      *       <code>TextLayout</code> and user space.
 503      */
 504     public TextLayout(AttributedCharacterIterator text, FontRenderContext frc) {
 505 
 506         if (text == null) {
 507             throw new IllegalArgumentException("Null iterator passed to TextLayout constructor.");
 508         }
 509 
 510         int start = text.getBeginIndex();
 511         int limit = text.getEndIndex();
 512         if (start == limit) {
 513             throw new IllegalArgumentException("Zero length iterator passed to TextLayout constructor.");
 514         }
 515 
 516         int len = limit - start;
 517         text.first();
 518         char[] chars = new char[len];
 519         int n = 0;
 520         for (char c = text.first();
 521              c != CharacterIterator.DONE;
 522              c = text.next())
 523         {
 524             chars[n++] = c;
 525         }
 526 
 527         text.first();
 528         if (text.getRunLimit() == limit) {
 529 
 530             Map<? extends Attribute, ?> attributes = text.getAttributes();
 531             Font font = singleFont(chars, 0, len, attributes);
 532             if (font != null) {
 533                 fastInit(chars, font, attributes, frc);
 534                 return;
 535             }
 536         }
 537 
 538         standardInit(text, chars, frc);
 539     }
 540 
 541     /**
 542      * Creates a <code>TextLayout</code> from a {@link TextLine} and
 543      * some paragraph data.  This method is used by {@link TextMeasurer}.
 544      * @param textLine the line measurement attributes to apply to the
 545      *       the resulting <code>TextLayout</code>
 546      * @param baseline the baseline of the text
 547      * @param baselineOffsets the baseline offsets for this
 548      * <code>TextLayout</code>.  This should already be normalized to
 549      * <code>baseline</code>
 550      * @param justifyRatio <code>0</code> if the <code>TextLayout</code>
 551      *     cannot be justified; <code>1</code> otherwise.
 552      */
 553     TextLayout(TextLine textLine,
 554                byte baseline,
 555                float[] baselineOffsets,
 556                float justifyRatio) {
 557 
 558         this.characterCount = textLine.characterCount();
 559         this.baseline = baseline;
 560         this.baselineOffsets = baselineOffsets;
 561         this.textLine = textLine;
 562         this.justifyRatio = justifyRatio;
 563     }
 564 
 565     /**
 566      * Initialize the paragraph-specific data.
 567      */
 568     private void paragraphInit(byte aBaseline, CoreMetrics lm,
 569                                Map<? extends Attribute, ?> paragraphAttrs,
 570                                char[] text) {
 571 


 726         hashCodeCache = 0;
 727 
 728         cacheIsValid = true;
 729     }
 730 
 731     /**
 732      * The 'natural bounds' encloses all the carets the layout can draw.
 733      *
 734      */
 735     private Rectangle2D getNaturalBounds() {
 736         ensureCache();
 737 
 738         if (naturalBounds == null) {
 739             naturalBounds = textLine.getItalicBounds();
 740         }
 741 
 742         return naturalBounds;
 743     }
 744 
 745     /**
 746      * Creates a copy of this <code>TextLayout</code>.
 747      */
 748     protected Object clone() {
 749         /*
 750          * !!! I think this is safe.  Once created, nothing mutates the
 751          * glyphvectors or arrays.  But we need to make sure.
 752          * {jbr} actually, that's not quite true.  The justification code
 753          * mutates after cloning.  It doesn't actually change the glyphvectors
 754          * (that's impossible) but it replaces them with justified sets.  This
 755          * is a problem for GlyphIterator creation, since new GlyphIterators
 756          * are created by cloning a prototype.  If the prototype has outdated
 757          * glyphvectors, so will the new ones.  A partial solution is to set the
 758          * prototypical GlyphIterator to null when the glyphvectors change.  If
 759          * you forget this one time, you're hosed.
 760          */
 761         try {
 762             return super.clone();
 763         }
 764         catch (CloneNotSupportedException e) {
 765             throw new InternalError(e);
 766         }
 767     }
 768 
 769     /*
 770      * Utility to throw an exception if an invalid TextHitInfo is passed
 771      * as a parameter.  Avoids code duplication.
 772      */
 773     private void checkTextHit(TextHitInfo hit) {
 774         if (hit == null) {
 775             throw new IllegalArgumentException("TextHitInfo is null.");
 776         }
 777 
 778         if (hit.getInsertionIndex() < 0 ||
 779             hit.getInsertionIndex() > characterCount) {
 780             throw new IllegalArgumentException("TextHitInfo is out of range");
 781         }
 782     }
 783 
 784     /**
 785      * Creates a copy of this <code>TextLayout</code> justified to the
 786      * specified width.
 787      * <p>
 788      * If this <code>TextLayout</code> has already been justified, an
 789      * exception is thrown.  If this <code>TextLayout</code> object's
 790      * justification ratio is zero, a <code>TextLayout</code> identical
 791      * to this <code>TextLayout</code> is returned.
 792      * @param justificationWidth the width to use when justifying the line.
 793      * For best results, it should not be too different from the current
 794      * advance of the line.
 795      * @return a <code>TextLayout</code> justified to the specified width.
 796      * @exception Error if this layout has already been justified, an Error is
 797      * thrown.
 798      */
 799     public TextLayout getJustifiedLayout(float justificationWidth) {
 800 
 801         if (justificationWidth <= 0) {
 802             throw new IllegalArgumentException("justificationWidth <= 0 passed to TextLayout.getJustifiedLayout()");
 803         }
 804 
 805         if (justifyRatio == ALREADY_JUSTIFIED) {
 806             throw new Error("Can't justify again.");
 807         }
 808 
 809         ensureCache(); // make sure textLine is not null
 810 
 811         // default justification range to exclude trailing logical whitespace
 812         int limit = characterCount;
 813         while (limit > 0 && textLine.isCharWhitespace(limit-1)) {
 814             --limit;
 815         }


 832      * indicated width.  The current implementation also adjusts hanging
 833      * punctuation and trailing whitespace to overhang the justification width.
 834      * Once justified, the layout may not be rejustified.
 835      * <p>
 836      * Some code may rely on immutability of layouts.  Subclassers should not
 837      * call this directly, but instead should call getJustifiedLayout, which
 838      * will call this method on a clone of this layout, preserving
 839      * the original.
 840      *
 841      * @param justificationWidth the width to use when justifying the line.
 842      * For best results, it should not be too different from the current
 843      * advance of the line.
 844      * @see #getJustifiedLayout(float)
 845      */
 846     protected void handleJustify(float justificationWidth) {
 847       // never called
 848     }
 849 
 850 
 851     /**
 852      * Returns the baseline for this <code>TextLayout</code>.
 853      * The baseline is one of the values defined in <code>Font</code>,
 854      * which are roman, centered and hanging.  Ascent and descent are
 855      * relative to this baseline.  The <code>baselineOffsets</code>
 856      * are also relative to this baseline.
 857      * @return the baseline of this <code>TextLayout</code>.
 858      * @see #getBaselineOffsets()
 859      * @see Font
 860      */
 861     public byte getBaseline() {
 862         return baseline;
 863     }
 864 
 865     /**
 866      * Returns the offsets array for the baselines used for this
 867      * <code>TextLayout</code>.
 868      * <p>
 869      * The array is indexed by one of the values defined in
 870      * <code>Font</code>, which are roman, centered and hanging.  The
 871      * values are relative to this <code>TextLayout</code> object's
 872      * baseline, so that <code>getBaselineOffsets[getBaseline()] == 0</code>.
 873      * Offsets are added to the position of the <code>TextLayout</code>
 874      * object's baseline to get the position for the new baseline.
 875      * @return the offsets array containing the baselines used for this
 876      *    <code>TextLayout</code>.
 877      * @see #getBaseline()
 878      * @see Font
 879      */
 880     public float[] getBaselineOffsets() {
 881         float[] offsets = new float[baselineOffsets.length];
 882         System.arraycopy(baselineOffsets, 0, offsets, 0, offsets.length);
 883         return offsets;
 884     }
 885 
 886     /**
 887      * Returns the advance of this <code>TextLayout</code>.
 888      * The advance is the distance from the origin to the advance of the
 889      * rightmost (bottommost) character.  This is in baseline-relative
 890      * coordinates.
 891      * @return the advance of this <code>TextLayout</code>.
 892      */
 893     public float getAdvance() {
 894         ensureCache();
 895         return lineMetrics.advance;
 896     }
 897 
 898     /**
 899      * Returns the advance of this <code>TextLayout</code>, minus trailing
 900      * whitespace.  This is in baseline-relative coordinates.
 901      * @return the advance of this <code>TextLayout</code> without the
 902      *      trailing whitespace.
 903      * @see #getAdvance()
 904      */
 905     public float getVisibleAdvance() {
 906         ensureCache();
 907         return visibleAdvance;
 908     }
 909 
 910     /**
 911      * Returns the ascent of this <code>TextLayout</code>.
 912      * The ascent is the distance from the top (right) of the
 913      * <code>TextLayout</code> to the baseline.  It is always either
 914      * positive or zero.  The ascent is sufficient to
 915      * accommodate superscripted text and is the maximum of the sum of the
 916      * ascent, offset, and baseline of each glyph.  The ascent is
 917      * the maximum ascent from the baseline of all the text in the
 918      * TextLayout.  It is in baseline-relative coordinates.
 919      * @return the ascent of this <code>TextLayout</code>.
 920      */
 921     public float getAscent() {
 922         ensureCache();
 923         return lineMetrics.ascent;
 924     }
 925 
 926     /**
 927      * Returns the descent of this <code>TextLayout</code>.
 928      * The descent is the distance from the baseline to the bottom (left) of
 929      * the <code>TextLayout</code>.  It is always either positive or zero.
 930      * The descent is sufficient to accommodate subscripted text and is the
 931      * maximum of the sum of the descent, offset, and baseline of each glyph.
 932      * This is the maximum descent from the baseline of all the text in
 933      * the TextLayout.  It is in baseline-relative coordinates.
 934      * @return the descent of this <code>TextLayout</code>.
 935      */
 936     public float getDescent() {
 937         ensureCache();
 938         return lineMetrics.descent;
 939     }
 940 
 941     /**
 942      * Returns the leading of the <code>TextLayout</code>.
 943      * The leading is the suggested interline spacing for this
 944      * <code>TextLayout</code>.  This is in baseline-relative
 945      * coordinates.
 946      * <p>
 947      * The leading is computed from the leading, descent, and baseline
 948      * of all glyphvectors in the <code>TextLayout</code>.  The algorithm
 949      * is roughly as follows:
 950      * <blockquote><pre>
 951      * maxD = 0;
 952      * maxDL = 0;
 953      * for (GlyphVector g in all glyphvectors) {
 954      *    maxD = max(maxD, g.getDescent() + offsets[g.getBaseline()]);
 955      *    maxDL = max(maxDL, g.getDescent() + g.getLeading() +
 956      *                       offsets[g.getBaseline()]);
 957      * }
 958      * return maxDL - maxD;
 959      * </pre></blockquote>
 960      * @return the leading of this <code>TextLayout</code>.
 961      */
 962     public float getLeading() {
 963         ensureCache();
 964         return lineMetrics.leading;
 965     }
 966 
 967     /**
 968      * Returns the bounds of this <code>TextLayout</code>.
 969      * The bounds are in standard coordinates.
 970      * <p>Due to rasterization effects, this bounds might not enclose all of the
 971      * pixels rendered by the TextLayout.</p>
 972      * It might not coincide exactly with the ascent, descent,
 973      * origin or advance of the <code>TextLayout</code>.
 974      * @return a {@link Rectangle2D} that is the bounds of this
 975      *        <code>TextLayout</code>.
 976      */
 977     public Rectangle2D getBounds() {
 978         ensureCache();
 979 
 980         if (boundsRect == null) {
 981             Rectangle2D vb = textLine.getVisualBounds();
 982             if (dx != 0 || dy != 0) {
 983                 vb.setRect(vb.getX() - dx,
 984                            vb.getY() - dy,
 985                            vb.getWidth(),
 986                            vb.getHeight());
 987             }
 988             boundsRect = vb;
 989         }
 990 
 991         Rectangle2D bounds = new Rectangle2D.Float();
 992         bounds.setRect(boundsRect);
 993 
 994         return bounds;
 995     }
 996 
 997     /**
 998      * Returns the pixel bounds of this <code>TextLayout</code> when
 999      * rendered in a graphics with the given
1000      * <code>FontRenderContext</code> at the given location.  The
1001      * graphics render context need not be the same as the
1002      * <code>FontRenderContext</code> used to create this
1003      * <code>TextLayout</code>, and can be null.  If it is null, the
1004      * <code>FontRenderContext</code> of this <code>TextLayout</code>
1005      * is used.
1006      * @param frc the <code>FontRenderContext</code> of the <code>Graphics</code>.
1007      * @param x the x-coordinate at which to render this <code>TextLayout</code>.
1008      * @param y the y-coordinate at which to render this <code>TextLayout</code>.
1009      * @return a <code>Rectangle</code> bounding the pixels that would be affected.
1010      * @see GlyphVector#getPixelBounds
1011      * @since 1.6
1012      */
1013     public Rectangle getPixelBounds(FontRenderContext frc, float x, float y) {
1014         return textLine.getPixelBounds(frc, x, y);
1015     }
1016 
1017     /**
1018      * Returns <code>true</code> if this <code>TextLayout</code> has
1019      * a left-to-right base direction or <code>false</code> if it has
1020      * a right-to-left base direction.  The <code>TextLayout</code>
1021      * has a base direction of either left-to-right (LTR) or
1022      * right-to-left (RTL).  The base direction is independent of the
1023      * actual direction of text on the line, which may be either LTR,
1024      * RTL, or mixed. Left-to-right layouts by default should position
1025      * flush left.  If the layout is on a tabbed line, the
1026      * tabs run left to right, so that logically successive layouts position
1027      * left to right.  The opposite is true for RTL layouts. By default they
1028      * should position flush left, and tabs run right-to-left.
1029      * @return <code>true</code> if the base direction of this
1030      *         <code>TextLayout</code> is left-to-right; <code>false</code>
1031      *         otherwise.
1032      */
1033     public boolean isLeftToRight() {
1034         return textLine.isDirectionLTR();
1035     }
1036 
1037     /**
1038      * Returns <code>true</code> if this <code>TextLayout</code> is vertical.
1039      * @return <code>true</code> if this <code>TextLayout</code> is vertical;
1040      *      <code>false</code> otherwise.
1041      */
1042     public boolean isVertical() {
1043         return isVerticalLine;
1044     }
1045 
1046     /**
1047      * Returns the number of characters represented by this
1048      * <code>TextLayout</code>.
1049      * @return the number of characters in this <code>TextLayout</code>.
1050      */
1051     public int getCharacterCount() {
1052         return characterCount;
1053     }
1054 
1055     /*
1056      * carets and hit testing
1057      *
1058      * Positions on a text line are represented by instances of TextHitInfo.
1059      * Any TextHitInfo with characterOffset between 0 and characterCount-1,
1060      * inclusive, represents a valid position on the line.  Additionally,
1061      * [-1, trailing] and [characterCount, leading] are valid positions, and
1062      * represent positions at the logical start and end of the line,
1063      * respectively.
1064      *
1065      * The characterOffsets in TextHitInfo's used and returned by TextLayout
1066      * are relative to the beginning of the text layout, not necessarily to
1067      * the beginning of the text storage the client is using.
1068      *
1069      *


1182         float topX = (top1X + top2X) / 2;
1183         float bottomX = (bottom1X + bottom2X) / 2;
1184 
1185         if (info == null) {
1186             info = new float[2];
1187         }
1188 
1189         if (isVerticalLine) {
1190             info[1] = (float) ((topX - bottomX) / bounds.getWidth());
1191             info[0] = (float) (topX + (info[1]*bounds.getX()));
1192         }
1193         else {
1194             info[1] = (float) ((topX - bottomX) / bounds.getHeight());
1195             info[0] = (float) (bottomX + (info[1]*bounds.getMaxY()));
1196         }
1197 
1198         return info;
1199     }
1200 
1201     /**
1202      * Returns information about the caret corresponding to <code>hit</code>.
1203      * The first element of the array is the intersection of the caret with
1204      * the baseline, as a distance along the baseline. The second element
1205      * of the array is the inverse slope (run/rise) of the caret, measured
1206      * with respect to the baseline at that point.
1207      * <p>
1208      * This method is meant for informational use.  To display carets, it
1209      * is better to use <code>getCaretShapes</code>.
1210      * @param hit a hit on a character in this <code>TextLayout</code>
1211      * @param bounds the bounds to which the caret info is constructed.
1212      *     The bounds is in baseline-relative coordinates.
1213      * @return a two-element array containing the position and slope of
1214      * the caret.  The returned caret info is in baseline-relative coordinates.
1215      * @see #getCaretShapes(int, Rectangle2D, TextLayout.CaretPolicy)
1216      * @see Font#getItalicAngle
1217      */
1218     public float[] getCaretInfo(TextHitInfo hit, Rectangle2D bounds) {
1219         ensureCache();
1220         checkTextHit(hit);
1221 
1222         return getCaretInfoTestInternal(hit, bounds);
1223     }
1224 
1225     // this version provides extra info in the float array
1226     // the first two values are as above
1227     // the next four values are the endpoints of the caret, as computed
1228     // using the hit character's offset (baseline + ssoffset) and
1229     // natural ascent and descent.
1230     // these  values are trimmed to the bounds where required to fit,


1293                     p2y = bo + thiscm.descent;
1294                 } else {
1295                     ixbase -= iangle * thiscm.ssOffset;
1296                     p1y = ixbase + iangle * thiscm.ascent;
1297                     p2y = ixbase - iangle * thiscm.descent;
1298                     p1x = bo + thiscm.ascent;
1299                     p2x = bo + thiscm.descent;
1300                 }
1301             }
1302         }
1303 
1304         info[2] = (float)p1x;
1305         info[3] = (float)p1y;
1306         info[4] = (float)p2x;
1307         info[5] = (float)p2y;
1308 
1309         return info;
1310     }
1311 
1312     /**
1313      * Returns information about the caret corresponding to <code>hit</code>.
1314      * This method is a convenience overload of <code>getCaretInfo</code> and
1315      * uses the natural bounds of this <code>TextLayout</code>.
1316      * @param hit a hit on a character in this <code>TextLayout</code>
1317      * @return the information about a caret corresponding to a hit.  The
1318      *     returned caret info is in baseline-relative coordinates.
1319      */
1320     public float[] getCaretInfo(TextHitInfo hit) {
1321 
1322         return getCaretInfo(hit, getNaturalBounds());
1323     }
1324 
1325     /**
1326      * Returns a caret index corresponding to <code>hit</code>.
1327      * Carets are numbered from left to right (top to bottom) starting from
1328      * zero. This always places carets next to the character hit, on the
1329      * indicated side of the character.
1330      * @param hit a hit on a character in this <code>TextLayout</code>
1331      * @return a caret index corresponding to the specified hit.
1332      */
1333     private int hitToCaret(TextHitInfo hit) {
1334 
1335         int hitIndex = hit.getCharIndex();
1336 
1337         if (hitIndex < 0) {
1338             return textLine.isDirectionLTR() ? 0 : characterCount;
1339         } else if (hitIndex >= characterCount) {
1340             return textLine.isDirectionLTR() ? characterCount : 0;
1341         }
1342 
1343         int visIndex = textLine.logicalToVisual(hitIndex);
1344 
1345         if (hit.isLeadingEdge() != textLine.isCharLTR(hitIndex)) {
1346             ++visIndex;
1347         }
1348 
1349         return visIndex;
1350     }


1384             return true;
1385         }
1386 
1387         int offset = textLine.visualToLogical(caret);
1388 
1389         if (!textLine.isCharLTR(offset)) {
1390             offset = textLine.visualToLogical(caret-1);
1391             if (textLine.isCharLTR(offset)) {
1392                 return true;
1393             }
1394         }
1395 
1396         // At this point, the leading edge of the character
1397         // at offset is at the given caret.
1398 
1399         return textLine.caretAtOffsetIsValid(offset);
1400     }
1401 
1402     /**
1403      * Returns the hit for the next caret to the right (bottom); if there
1404      * is no such hit, returns <code>null</code>.
1405      * If the hit character index is out of bounds, an
1406      * {@link IllegalArgumentException} is thrown.
1407      * @param hit a hit on a character in this layout
1408      * @return a hit whose caret appears at the next position to the
1409      * right (bottom) of the caret of the provided hit or <code>null</code>.
1410      */
1411     public TextHitInfo getNextRightHit(TextHitInfo hit) {
1412         ensureCache();
1413         checkTextHit(hit);
1414 
1415         int caret = hitToCaret(hit);
1416 
1417         if (caret == characterCount) {
1418             return null;
1419         }
1420 
1421         do {
1422             ++caret;
1423         } while (!caretIsValid(caret));
1424 
1425         return caretToHit(caret);
1426     }
1427 
1428     /**
1429      * Returns the hit for the next caret to the right (bottom); if no
1430      * such hit, returns <code>null</code>.  The hit is to the right of
1431      * the strong caret at the specified offset, as determined by the
1432      * specified policy.
1433      * The returned hit is the stronger of the two possible
1434      * hits, as determined by the specified policy.
1435      * @param offset an insertion offset in this <code>TextLayout</code>.
1436      * Cannot be less than 0 or greater than this <code>TextLayout</code>
1437      * object's character count.
1438      * @param policy the policy used to select the strong caret
1439      * @return a hit whose caret appears at the next position to the
1440      * right (bottom) of the caret of the provided hit, or <code>null</code>.
1441      */
1442     public TextHitInfo getNextRightHit(int offset, CaretPolicy policy) {
1443 
1444         if (offset < 0 || offset > characterCount) {
1445             throw new IllegalArgumentException("Offset out of bounds in TextLayout.getNextRightHit()");
1446         }
1447 
1448         if (policy == null) {
1449             throw new IllegalArgumentException("Null CaretPolicy passed to TextLayout.getNextRightHit()");
1450         }
1451 
1452         TextHitInfo hit1 = TextHitInfo.afterOffset(offset);
1453         TextHitInfo hit2 = hit1.getOtherHit();
1454 
1455         TextHitInfo nextHit = getNextRightHit(policy.getStrongCaret(hit1, hit2, this));
1456 
1457         if (nextHit != null) {
1458             TextHitInfo otherHit = getVisualOtherHit(nextHit);
1459             return policy.getStrongCaret(otherHit, nextHit, this);
1460         }
1461         else {
1462             return null;
1463         }
1464     }
1465 
1466     /**
1467      * Returns the hit for the next caret to the right (bottom); if no
1468      * such hit, returns <code>null</code>.  The hit is to the right of
1469      * the strong caret at the specified offset, as determined by the
1470      * default policy.
1471      * The returned hit is the stronger of the two possible
1472      * hits, as determined by the default policy.
1473      * @param offset an insertion offset in this <code>TextLayout</code>.
1474      * Cannot be less than 0 or greater than the <code>TextLayout</code>
1475      * object's character count.
1476      * @return a hit whose caret appears at the next position to the
1477      * right (bottom) of the caret of the provided hit, or <code>null</code>.
1478      */
1479     public TextHitInfo getNextRightHit(int offset) {
1480 
1481         return getNextRightHit(offset, DEFAULT_CARET_POLICY);
1482     }
1483 
1484     /**
1485      * Returns the hit for the next caret to the left (top); if no such
1486      * hit, returns <code>null</code>.
1487      * If the hit character index is out of bounds, an
1488      * <code>IllegalArgumentException</code> is thrown.
1489      * @param hit a hit on a character in this <code>TextLayout</code>.
1490      * @return a hit whose caret appears at the next position to the
1491      * left (top) of the caret of the provided hit, or <code>null</code>.
1492      */
1493     public TextHitInfo getNextLeftHit(TextHitInfo hit) {
1494         ensureCache();
1495         checkTextHit(hit);
1496 
1497         int caret = hitToCaret(hit);
1498 
1499         if (caret == 0) {
1500             return null;
1501         }
1502 
1503         do {
1504             --caret;
1505         } while(!caretIsValid(caret));
1506 
1507         return caretToHit(caret);
1508     }
1509 
1510     /**
1511      * Returns the hit for the next caret to the left (top); if no
1512      * such hit, returns <code>null</code>.  The hit is to the left of
1513      * the strong caret at the specified offset, as determined by the
1514      * specified policy.
1515      * The returned hit is the stronger of the two possible
1516      * hits, as determined by the specified policy.
1517      * @param offset an insertion offset in this <code>TextLayout</code>.
1518      * Cannot be less than 0 or greater than this <code>TextLayout</code>
1519      * object's character count.
1520      * @param policy the policy used to select the strong caret
1521      * @return a hit whose caret appears at the next position to the
1522      * left (top) of the caret of the provided hit, or <code>null</code>.
1523      */
1524     public TextHitInfo getNextLeftHit(int offset, CaretPolicy policy) {
1525 
1526         if (policy == null) {
1527             throw new IllegalArgumentException("Null CaretPolicy passed to TextLayout.getNextLeftHit()");
1528         }
1529 
1530         if (offset < 0 || offset > characterCount) {
1531             throw new IllegalArgumentException("Offset out of bounds in TextLayout.getNextLeftHit()");
1532         }
1533 
1534         TextHitInfo hit1 = TextHitInfo.afterOffset(offset);
1535         TextHitInfo hit2 = hit1.getOtherHit();
1536 
1537         TextHitInfo nextHit = getNextLeftHit(policy.getStrongCaret(hit1, hit2, this));
1538 
1539         if (nextHit != null) {
1540             TextHitInfo otherHit = getVisualOtherHit(nextHit);
1541             return policy.getStrongCaret(otherHit, nextHit, this);
1542         }
1543         else {
1544             return null;
1545         }
1546     }
1547 
1548     /**
1549      * Returns the hit for the next caret to the left (top); if no
1550      * such hit, returns <code>null</code>.  The hit is to the left of
1551      * the strong caret at the specified offset, as determined by the
1552      * default policy.
1553      * The returned hit is the stronger of the two possible
1554      * hits, as determined by the default policy.
1555      * @param offset an insertion offset in this <code>TextLayout</code>.
1556      * Cannot be less than 0 or greater than this <code>TextLayout</code>
1557      * object's character count.
1558      * @return a hit whose caret appears at the next position to the
1559      * left (top) of the caret of the provided hit, or <code>null</code>.
1560      */
1561     public TextHitInfo getNextLeftHit(int offset) {
1562 
1563         return getNextLeftHit(offset, DEFAULT_CARET_POLICY);
1564     }
1565 
1566     /**
1567      * Returns the hit on the opposite side of the specified hit's caret.
1568      * @param hit the specified hit
1569      * @return a hit that is on the opposite side of the specified hit's
1570      *    caret.
1571      */
1572     public TextHitInfo getVisualOtherHit(TextHitInfo hit) {
1573 
1574         ensureCache();
1575         checkTextHit(hit);
1576 
1577         int hitCharIndex = hit.getCharIndex();
1578 
1579         int charIndex;


1760     private static GeneralPath pathToShape(double[] path, boolean close, LayoutPathImpl lp) {
1761         GeneralPath result = new GeneralPath(GeneralPath.WIND_EVEN_ODD, path.length);
1762         result.moveTo((float)path[0], (float)path[1]);
1763         for (int i = 2; i < path.length; i += 2) {
1764             result.lineTo((float)path[i], (float)path[i+1]);
1765         }
1766         if (close) {
1767             result.closePath();
1768         }
1769 
1770         if (lp != null) {
1771             result = (GeneralPath)lp.mapShape(result);
1772         }
1773         return result;
1774     }
1775 
1776     /**
1777      * Returns a {@link Shape} representing the caret at the specified
1778      * hit inside the specified bounds.
1779      * @param hit the hit at which to generate the caret
1780      * @param bounds the bounds of the <code>TextLayout</code> to use
1781      *    in generating the caret.  The bounds is in baseline-relative
1782      *    coordinates.
1783      * @return a <code>Shape</code> representing the caret.  The returned
1784      *    shape is in standard coordinates.
1785      */
1786     public Shape getCaretShape(TextHitInfo hit, Rectangle2D bounds) {
1787         ensureCache();
1788         checkTextHit(hit);
1789 
1790         if (bounds == null) {
1791             throw new IllegalArgumentException("Null Rectangle2D passed to TextLayout.getCaret()");
1792         }
1793 
1794         return pathToShape(getCaretPath(hit, bounds), false, textLine.getLayoutPath());
1795     }
1796 
1797     /**
1798      * Returns a <code>Shape</code> representing the caret at the specified
1799      * hit inside the natural bounds of this <code>TextLayout</code>.
1800      * @param hit the hit at which to generate the caret
1801      * @return a <code>Shape</code> representing the caret.  The returned
1802      *     shape is in standard coordinates.
1803      */
1804     public Shape getCaretShape(TextHitInfo hit) {
1805 
1806         return getCaretShape(hit, getNaturalBounds());
1807     }
1808 
1809     /**
1810      * Return the "stronger" of the TextHitInfos.  The TextHitInfos
1811      * should be logical or visual counterparts.  They are not
1812      * checked for validity.
1813      */
1814     private TextHitInfo getStrongHit(TextHitInfo hit1, TextHitInfo hit2) {
1815 
1816         // right now we're using the following rule for strong hits:
1817         // A hit on a character with a lower level
1818         // is stronger than one on a character with a higher level.
1819         // If this rule ties, the hit on the leading edge of a character wins.
1820         // If THIS rule ties, hit1 wins.  Both rules shouldn't tie, unless the
1821         // infos aren't counterparts of some sort.
1822 
1823         byte hit1Level = getCharacterLevel(hit1.getCharIndex());
1824         byte hit2Level = getCharacterLevel(hit2.getCharIndex());
1825 
1826         if (hit1Level == hit2Level) {
1827             if (hit2.isLeadingEdge() && !hit1.isLeadingEdge()) {
1828                 return hit2;
1829             }
1830             else {
1831                 return hit1;
1832             }
1833         }
1834         else {
1835             return (hit1Level < hit2Level)? hit1 : hit2;
1836         }
1837     }
1838 
1839     /**
1840      * Returns the level of the character at <code>index</code>.
1841      * Indices -1 and <code>characterCount</code> are assigned the base
1842      * level of this <code>TextLayout</code>.
1843      * @param index the index of the character from which to get the level
1844      * @return the level of the character at the specified index.
1845      */
1846     public byte getCharacterLevel(int index) {
1847 
1848         // hmm, allow indices at endpoints?  For now, yes.
1849         if (index < -1 || index > characterCount) {
1850             throw new IllegalArgumentException("Index is out of range in getCharacterLevel.");
1851         }
1852 
1853         ensureCache();
1854         if (index == -1 || index == characterCount) {
1855              return (byte) (textLine.isDirectionLTR()? 0 : 1);
1856         }
1857 
1858         return textLine.getCharLevel(index);
1859     }
1860 
1861     /**
1862      * Returns two paths corresponding to the strong and weak caret.
1863      * @param offset an offset in this <code>TextLayout</code>
1864      * @param bounds the bounds to which to extend the carets.  The
1865      * bounds is in baseline-relative coordinates.
1866      * @param policy the specified <code>CaretPolicy</code>
1867      * @return an array of two paths.  Element zero is the strong
1868      * caret.  If there are two carets, element one is the weak caret,
1869      * otherwise it is <code>null</code>. The returned shapes
1870      * are in standard coordinates.
1871      */
1872     public Shape[] getCaretShapes(int offset, Rectangle2D bounds, CaretPolicy policy) {
1873 
1874         ensureCache();
1875 
1876         if (offset < 0 || offset > characterCount) {
1877             throw new IllegalArgumentException("Offset out of bounds in TextLayout.getCaretShapes()");
1878         }
1879 
1880         if (bounds == null) {
1881             throw new IllegalArgumentException("Null Rectangle2D passed to TextLayout.getCaretShapes()");
1882         }
1883 
1884         if (policy == null) {
1885             throw new IllegalArgumentException("Null CaretPolicy passed to TextLayout.getCaretShapes()");
1886         }
1887 
1888         Shape[] result = new Shape[2];
1889 


1903             Shape otherShape = pathToShape(getCaretPath(otherHit, bounds), false, lp);
1904 
1905             TextHitInfo strongHit = policy.getStrongCaret(hit, otherHit, this);
1906             boolean hitIsStrong = strongHit.equals(hit);
1907 
1908             if (hitIsStrong) {// then other is weak
1909                 result[0] = hitShape;
1910                 result[1] = otherShape;
1911             }
1912             else {
1913                 result[0] = otherShape;
1914                 result[1] = hitShape;
1915             }
1916         }
1917 
1918         return result;
1919     }
1920 
1921     /**
1922      * Returns two paths corresponding to the strong and weak caret.
1923      * This method is a convenience overload of <code>getCaretShapes</code>
1924      * that uses the default caret policy.
1925      * @param offset an offset in this <code>TextLayout</code>
1926      * @param bounds the bounds to which to extend the carets.  This is
1927      *     in baseline-relative coordinates.
1928      * @return two paths corresponding to the strong and weak caret as
1929      *    defined by the <code>DEFAULT_CARET_POLICY</code>.  These are
1930      *    in standard coordinates.
1931      */
1932     public Shape[] getCaretShapes(int offset, Rectangle2D bounds) {
1933         // {sfb} parameter checking is done in overloaded version
1934         return getCaretShapes(offset, bounds, DEFAULT_CARET_POLICY);
1935     }
1936 
1937     /**
1938      * Returns two paths corresponding to the strong and weak caret.
1939      * This method is a convenience overload of <code>getCaretShapes</code>
1940      * that uses the default caret policy and this <code>TextLayout</code>
1941      * object's natural bounds.
1942      * @param offset an offset in this <code>TextLayout</code>
1943      * @return two paths corresponding to the strong and weak caret as
1944      *    defined by the <code>DEFAULT_CARET_POLICY</code>.  These are
1945      *    in standard coordinates.
1946      */
1947     public Shape[] getCaretShapes(int offset) {
1948         // {sfb} parameter checking is done in overloaded version
1949         return getCaretShapes(offset, getNaturalBounds(), DEFAULT_CARET_POLICY);
1950     }
1951 
1952     // A utility to return a path enclosing the given path
1953     // Path0 must be left or top of path1
1954     // {jbr} no assumptions about size of path0, path1 anymore.
1955     private GeneralPath boundingShape(double[] path0, double[] path1) {
1956 
1957         // Really, we want the path to be a convex hull around all of the
1958         // points in path0 and path1.  But we can get by with less than
1959         // that.  We do need to prevent the two segments which
1960         // join path0 to path1 from crossing each other.  So, if we
1961         // traverse path0 from top to bottom, we'll traverse path1 from
1962         // bottom to top (and vice versa).
1963 
1964         GeneralPath result = pathToShape(path0, false, null);


2051                 bounds.getY() + bounds.getHeight()
2052             };
2053         } else {
2054             path1 = new double[] {
2055                 bounds.getX() + bounds.getWidth(),
2056                 bounds.getY() + bounds.getHeight(),
2057                 bounds.getX() + bounds.getWidth(),
2058                 bounds.getY()
2059             };
2060         }
2061 
2062         double[] path0 = getCaretPath(characterCount, bounds, true);
2063 
2064         return boundingShape(path0, path1);
2065     }
2066 
2067     /**
2068      * Returns the logical ranges of text corresponding to a visual selection.
2069      * @param firstEndpoint an endpoint of the visual range
2070      * @param secondEndpoint the other endpoint of the visual range.
2071      * This endpoint can be less than <code>firstEndpoint</code>.
2072      * @return an array of integers representing start/limit pairs for the
2073      * selected ranges.
2074      * @see #getVisualHighlightShape(TextHitInfo, TextHitInfo, Rectangle2D)
2075      */
2076     public int[] getLogicalRangesForVisualSelection(TextHitInfo firstEndpoint,
2077                                                     TextHitInfo secondEndpoint) {
2078         ensureCache();
2079 
2080         checkTextHit(firstEndpoint);
2081         checkTextHit(secondEndpoint);
2082 
2083         // !!! probably want to optimize for all LTR text
2084 
2085         boolean[] included = new boolean[characterCount];
2086 
2087         int startIndex = hitToCaret(firstEndpoint);
2088         int limitIndex = hitToCaret(secondEndpoint);
2089 
2090         if (startIndex > limitIndex) {
2091             int t = startIndex;


2124         }
2125 
2126         int[] ranges = new int[count * 2];
2127         count = 0;
2128         inrun = false;
2129         for (int i = 0; i < characterCount; i++) {
2130             if (included[i] != inrun) {
2131                 ranges[count++] = i;
2132                 inrun = !inrun;
2133             }
2134         }
2135         if (inrun) {
2136             ranges[count++] = characterCount;
2137         }
2138 
2139         return ranges;
2140     }
2141 
2142     /**
2143      * Returns a path enclosing the visual selection in the specified range,
2144      * extended to <code>bounds</code>.
2145      * <p>
2146      * If the selection includes the leftmost (topmost) position, the selection
2147      * is extended to the left (top) of <code>bounds</code>.  If the
2148      * selection includes the rightmost (bottommost) position, the selection
2149      * is extended to the right (bottom) of the bounds.  The height
2150      * (width on vertical lines) of the selection is always extended to
2151      * <code>bounds</code>.
2152      * <p>
2153      * Although the selection is always contiguous, the logically selected
2154      * text can be discontiguous on lines with mixed-direction text.  The
2155      * logical ranges of text selected can be retrieved using
2156      * <code>getLogicalRangesForVisualSelection</code>.  For example,
2157      * consider the text 'ABCdef' where capital letters indicate
2158      * right-to-left text, rendered on a right-to-left line, with a visual
2159      * selection from 0L (the leading edge of 'A') to 3T (the trailing edge
2160      * of 'd').  The text appears as follows, with bold underlined areas
2161      * representing the selection:
2162      * <br><pre>
2163      *    d<u><b>efCBA  </b></u>
2164      * </pre>
2165      * The logical selection ranges are 0-3, 4-6 (ABC, ef) because the
2166      * visually contiguous text is logically discontiguous.  Also note that
2167      * since the rightmost position on the layout (to the right of 'A') is
2168      * selected, the selection is extended to the right of the bounds.
2169      * @param firstEndpoint one end of the visual selection
2170      * @param secondEndpoint the other end of the visual selection
2171      * @param bounds the bounding rectangle to which to extend the selection.
2172      *     This is in baseline-relative coordinates.
2173      * @return a <code>Shape</code> enclosing the selection.  This is in
2174      *     standard coordinates.
2175      * @see #getLogicalRangesForVisualSelection(TextHitInfo, TextHitInfo)
2176      * @see #getLogicalHighlightShape(int, int, Rectangle2D)
2177      */
2178     public Shape getVisualHighlightShape(TextHitInfo firstEndpoint,
2179                                         TextHitInfo secondEndpoint,
2180                                         Rectangle2D bounds)
2181     {
2182         ensureCache();
2183 
2184         checkTextHit(firstEndpoint);
2185         checkTextHit(secondEndpoint);
2186 
2187         if(bounds == null) {
2188                 throw new IllegalArgumentException("Null Rectangle2D passed to TextLayout.getVisualHighlightShape()");
2189         }
2190 
2191         GeneralPath result = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
2192 
2193         int firstCaret = hitToCaret(firstEndpoint);


2201             if (!ls.getBounds().isEmpty())
2202                 result.append(ls, false);
2203         }
2204 
2205         if (firstCaret == characterCount || secondCaret == characterCount) {
2206             GeneralPath rs = rightShape(bounds);
2207             if (!rs.getBounds().isEmpty()) {
2208                 result.append(rs, false);
2209             }
2210         }
2211 
2212         LayoutPathImpl lp = textLine.getLayoutPath();
2213         if (lp != null) {
2214             result = (GeneralPath)lp.mapShape(result); // dlf cast safe?
2215         }
2216 
2217         return  result;
2218     }
2219 
2220     /**
2221      * Returns a <code>Shape</code> enclosing the visual selection in the
2222      * specified range, extended to the bounds.  This method is a
2223      * convenience overload of <code>getVisualHighlightShape</code> that
2224      * uses the natural bounds of this <code>TextLayout</code>.
2225      * @param firstEndpoint one end of the visual selection
2226      * @param secondEndpoint the other end of the visual selection
2227      * @return a <code>Shape</code> enclosing the selection.  This is
2228      *     in standard coordinates.
2229      */
2230     public Shape getVisualHighlightShape(TextHitInfo firstEndpoint,
2231                                              TextHitInfo secondEndpoint) {
2232         return getVisualHighlightShape(firstEndpoint, secondEndpoint, getNaturalBounds());
2233     }
2234 
2235     /**
2236      * Returns a <code>Shape</code> enclosing the logical selection in the
2237      * specified range, extended to the specified <code>bounds</code>.
2238      * <p>
2239      * If the selection range includes the first logical character, the
2240      * selection is extended to the portion of <code>bounds</code> before
2241      * the start of this <code>TextLayout</code>.  If the range includes
2242      * the last logical character, the selection is extended to the portion
2243      * of <code>bounds</code> after the end of this <code>TextLayout</code>.
2244      * The height (width on vertical lines) of the selection is always
2245      * extended to <code>bounds</code>.
2246      * <p>
2247      * The selection can be discontiguous on lines with mixed-direction text.
2248      * Only those characters in the logical range between start and limit
2249      * appear selected.  For example, consider the text 'ABCdef' where capital
2250      * letters indicate right-to-left text, rendered on a right-to-left line,
2251      * with a logical selection from 0 to 4 ('ABCd').  The text appears as
2252      * follows, with bold standing in for the selection, and underlining for
2253      * the extension:
2254      * <br><pre>
2255      *    <u><b>d</b></u>ef<u><b>CBA  </b></u>
2256      * </pre>
2257      * The selection is discontiguous because the selected characters are
2258      * visually discontiguous. Also note that since the range includes the
2259      * first logical character (A), the selection is extended to the portion
2260      * of the <code>bounds</code> before the start of the layout, which in
2261      * this case (a right-to-left line) is the right portion of the
2262      * <code>bounds</code>.
2263      * @param firstEndpoint an endpoint in the range of characters to select
2264      * @param secondEndpoint the other endpoint of the range of characters
2265      * to select. Can be less than <code>firstEndpoint</code>.  The range
2266      * includes the character at min(firstEndpoint, secondEndpoint), but
2267      * excludes max(firstEndpoint, secondEndpoint).
2268      * @param bounds the bounding rectangle to which to extend the selection.
2269      *     This is in baseline-relative coordinates.
2270      * @return an area enclosing the selection.  This is in standard
2271      *     coordinates.
2272      * @see #getVisualHighlightShape(TextHitInfo, TextHitInfo, Rectangle2D)
2273      */
2274     public Shape getLogicalHighlightShape(int firstEndpoint,
2275                                          int secondEndpoint,
2276                                          Rectangle2D bounds) {
2277         if (bounds == null) {
2278             throw new IllegalArgumentException("Null Rectangle2D passed to TextLayout.getLogicalHighlightShape()");
2279         }
2280 
2281         ensureCache();
2282 
2283         if (firstEndpoint > secondEndpoint) {
2284             int t = firstEndpoint;
2285             firstEndpoint = secondEndpoint;


2337             }
2338 
2339             if ((textLine.isDirectionLTR() && secondEndpoint == characterCount) ||
2340                 (!textLine.isDirectionLTR() && firstEndpoint == 0)) {
2341 
2342                 GeneralPath rs = rightShape(bounds);
2343                 if (!rs.getBounds().isEmpty()) {
2344                     result.append(rs, false);
2345                 }
2346             }
2347         }
2348 
2349         LayoutPathImpl lp = textLine.getLayoutPath();
2350         if (lp != null) {
2351             result = (GeneralPath)lp.mapShape(result); // dlf cast safe?
2352         }
2353         return result;
2354     }
2355 
2356     /**
2357      * Returns a <code>Shape</code> enclosing the logical selection in the
2358      * specified range, extended to the natural bounds of this
2359      * <code>TextLayout</code>.  This method is a convenience overload of
2360      * <code>getLogicalHighlightShape</code> that uses the natural bounds of
2361      * this <code>TextLayout</code>.
2362      * @param firstEndpoint an endpoint in the range of characters to select
2363      * @param secondEndpoint the other endpoint of the range of characters
2364      * to select. Can be less than <code>firstEndpoint</code>.  The range
2365      * includes the character at min(firstEndpoint, secondEndpoint), but
2366      * excludes max(firstEndpoint, secondEndpoint).
2367      * @return a <code>Shape</code> enclosing the selection.  This is in
2368      *     standard coordinates.
2369      */
2370     public Shape getLogicalHighlightShape(int firstEndpoint, int secondEndpoint) {
2371 
2372         return getLogicalHighlightShape(firstEndpoint, secondEndpoint, getNaturalBounds());
2373     }
2374 
2375     /**
2376      * Returns the black box bounds of the characters in the specified range.
2377      * The black box bounds is an area consisting of the union of the bounding
2378      * boxes of all the glyphs corresponding to the characters between start
2379      * and limit.  This area can be disjoint.
2380      * @param firstEndpoint one end of the character range
2381      * @param secondEndpoint the other end of the character range.  Can be
2382      * less than <code>firstEndpoint</code>.
2383      * @return a <code>Shape</code> enclosing the black box bounds.  This is
2384      *     in standard coordinates.
2385      */
2386     public Shape getBlackBoxBounds(int firstEndpoint, int secondEndpoint) {
2387         ensureCache();
2388 
2389         if (firstEndpoint > secondEndpoint) {
2390             int t = firstEndpoint;
2391             firstEndpoint = secondEndpoint;
2392             secondEndpoint = t;
2393         }
2394 
2395         if (firstEndpoint < 0 || secondEndpoint > characterCount) {
2396             throw new IllegalArgumentException("Invalid range passed to TextLayout.getBlackBoxBounds()");
2397         }
2398 
2399         /*
2400          * return an area that consists of the bounding boxes of all the
2401          * characters from firstEndpoint to limit
2402          */
2403 


2413                     result.append(r, false);
2414                 }
2415             }
2416         }
2417 
2418         if (dx != 0 || dy != 0) {
2419             AffineTransform tx = AffineTransform.getTranslateInstance(dx, dy);
2420             result = (GeneralPath)tx.createTransformedShape(result);
2421         }
2422         LayoutPathImpl lp = textLine.getLayoutPath();
2423         if (lp != null) {
2424             result = (GeneralPath)lp.mapShape(result);
2425         }
2426 
2427         //return new Highlight(result, false);
2428         return result;
2429     }
2430 
2431     /**
2432      * Returns the distance from the point (x,&nbsp;y) to the caret along
2433      * the line direction defined in <code>caretInfo</code>.  Distance is
2434      * negative if the point is to the left of the caret on a horizontal
2435      * line, or above the caret on a vertical line.
2436      * Utility for use by hitTestChar.
2437      */
2438     private float caretToPointDistance(float[] caretInfo, float x, float y) {
2439         // distanceOffBaseline is negative if you're 'above' baseline
2440 
2441         float lineDistance = isVerticalLine? y : x;
2442         float distanceOffBaseline = isVerticalLine? -x : y;
2443 
2444         return lineDistance - caretInfo[0] +
2445             (distanceOffBaseline*caretInfo[1]);
2446     }
2447 
2448     /**
2449      * Returns a <code>TextHitInfo</code> corresponding to the
2450      * specified point.
2451      * Coordinates outside the bounds of the <code>TextLayout</code>
2452      * map to hits on the leading edge of the first logical character,
2453      * or the trailing edge of the last logical character, as appropriate,
2454      * regardless of the position of that character in the line.  Only the
2455      * direction along the baseline is used to make this evaluation.
2456      * @param x the x offset from the origin of this
2457      *     <code>TextLayout</code>.  This is in standard coordinates.
2458      * @param y the y offset from the origin of this
2459      *     <code>TextLayout</code>.  This is in standard coordinates.
2460      * @param bounds the bounds of the <code>TextLayout</code>.  This
2461      *     is in baseline-relative coordinates.
2462      * @return a hit describing the character and edge (leading or trailing)
2463      *     under the specified point.
2464      */
2465     public TextHitInfo hitTestChar(float x, float y, Rectangle2D bounds) {
2466         // check boundary conditions
2467 
2468         LayoutPathImpl lp = textLine.getLayoutPath();
2469         boolean prev = false;
2470         if (lp != null) {
2471             Point2D.Float pt = new Point2D.Float(x, y);
2472             prev = lp.pointToPath(pt, pt);
2473             x = pt.x;
2474             y = pt.y;
2475         }
2476 
2477         if (isVertical()) {
2478             if (y < bounds.getMinY()) {
2479                 return TextHitInfo.leading(0);
2480             } else if (y >= bounds.getMaxY()) {


2535             // proximity in x (along baseline) is two times as important as proximity in y
2536             double nd = Math.sqrt(4*(cx - x)*(cx - x) + ydsq);
2537             if (nd < distance) {
2538                 distance = nd;
2539                 index = i;
2540                 trail = -1;
2541                 icx = cx; icy = cy; ia = cm.italicAngle;
2542             }
2543         }
2544         boolean left = x < icx - (y - icy) * ia;
2545         boolean leading = textLine.isCharLTR(index) == left;
2546         if (trail == -1) {
2547             trail = characterCount;
2548         }
2549         TextHitInfo result = leading ? TextHitInfo.leading(index) :
2550             TextHitInfo.trailing(trail-1);
2551         return result;
2552     }
2553 
2554     /**
2555      * Returns a <code>TextHitInfo</code> corresponding to the
2556      * specified point.  This method is a convenience overload of
2557      * <code>hitTestChar</code> that uses the natural bounds of this
2558      * <code>TextLayout</code>.
2559      * @param x the x offset from the origin of this
2560      *     <code>TextLayout</code>.  This is in standard coordinates.
2561      * @param y the y offset from the origin of this
2562      *     <code>TextLayout</code>.  This is in standard coordinates.
2563      * @return a hit describing the character and edge (leading or trailing)
2564      * under the specified point.
2565      */
2566     public TextHitInfo hitTestChar(float x, float y) {
2567 
2568         return hitTestChar(x, y, getNaturalBounds());
2569     }
2570 
2571     /**
2572      * Returns the hash code of this <code>TextLayout</code>.
2573      * @return the hash code of this <code>TextLayout</code>.
2574      */
2575     public int hashCode() {
2576         if (hashCodeCache == 0) {
2577             ensureCache();
2578             hashCodeCache = textLine.hashCode();
2579         }
2580         return hashCodeCache;
2581     }
2582 
2583     /**
2584      * Returns <code>true</code> if the specified <code>Object</code> is a
2585      * <code>TextLayout</code> object and if the specified <code>Object</code>
2586      * equals this <code>TextLayout</code>.
2587      * @param obj an <code>Object</code> to test for equality
2588      * @return <code>true</code> if the specified <code>Object</code>
2589      *      equals this <code>TextLayout</code>; <code>false</code>
2590      *      otherwise.
2591      */
2592     public boolean equals(Object obj) {
2593         return (obj instanceof TextLayout) && equals((TextLayout)obj);
2594     }
2595 
2596     /**
2597      * Returns <code>true</code> if the two layouts are equal.
2598      * Two layouts are equal if they contain equal glyphvectors in the same order.
2599      * @param rhs the <code>TextLayout</code> to compare to this
2600      *       <code>TextLayout</code>
2601      * @return <code>true</code> if the specified <code>TextLayout</code>
2602      *      equals this <code>TextLayout</code>.
2603      *
2604      */
2605     public boolean equals(TextLayout rhs) {
2606 
2607         if (rhs == null) {
2608             return false;
2609         }
2610         if (rhs == this) {
2611             return true;
2612         }
2613 
2614         ensureCache();
2615         return textLine.equals(rhs.textLine);
2616     }
2617 
2618     /**
2619      * Returns debugging information for this <code>TextLayout</code>.
2620      * @return the <code>textLine</code> of this <code>TextLayout</code>
2621      *        as a <code>String</code>.
2622      */
2623     public String toString() {
2624         ensureCache();
2625         return textLine.toString();
2626      }
2627 
2628     /**
2629      * Renders this <code>TextLayout</code> at the specified location in
2630      * the specified {@link java.awt.Graphics2D Graphics2D} context.
2631      * The origin of the layout is placed at x,&nbsp;y.  Rendering may touch
2632      * any point within <code>getBounds()</code> of this position.  This
2633      * leaves the <code>g2</code> unchanged.  Text is rendered along the
2634      * baseline path.
2635      * @param g2 the <code>Graphics2D</code> context into which to render
2636      *         the layout
2637      * @param x the X coordinate of the origin of this <code>TextLayout</code>
2638      * @param y the Y coordinate of the origin of this <code>TextLayout</code>
2639      * @see #getBounds()
2640      */
2641     public void draw(Graphics2D g2, float x, float y) {
2642 
2643         if (g2 == null) {
2644             throw new IllegalArgumentException("Null Graphics2D passed to TextLayout.draw()");
2645         }
2646 
2647         textLine.draw(g2, x - dx, y - dy);
2648     }
2649 
2650     /**
2651      * Package-only method for testing ONLY.  Please don't abuse.
2652      */
2653     TextLine getTextLineForTesting() {
2654 
2655         return textLine;
2656     }
2657 
2658     /**


2672         }
2673         return start;
2674         */
2675     }
2676 
2677     static byte getBaselineFromGraphic(GraphicAttribute graphic) {
2678 
2679         byte alignment = (byte) graphic.getAlignment();
2680 
2681         if (alignment == GraphicAttribute.BOTTOM_ALIGNMENT ||
2682                 alignment == GraphicAttribute.TOP_ALIGNMENT) {
2683 
2684             return (byte)GraphicAttribute.ROMAN_BASELINE;
2685         }
2686         else {
2687             return alignment;
2688         }
2689     }
2690 
2691     /**
2692      * Returns a <code>Shape</code> representing the outline of this
2693      * <code>TextLayout</code>.
2694      * @param tx an optional {@link AffineTransform} to apply to the
2695      *     outline of this <code>TextLayout</code>.
2696      * @return a <code>Shape</code> that is the outline of this
2697      *     <code>TextLayout</code>.  This is in standard coordinates.
2698      */
2699     public Shape getOutline(AffineTransform tx) {
2700         ensureCache();
2701         Shape result = textLine.getOutline(tx);
2702         LayoutPathImpl lp = textLine.getLayoutPath();
2703         if (lp != null) {
2704             result = lp.mapShape(result);
2705         }
2706         return result;
2707     }
2708 
2709     /**
2710      * Return the LayoutPath, or null if the layout path is the
2711      * default path (x maps to advance, y maps to offset).
2712      * @return the layout path
2713      * @since 1.6
2714      */
2715     public LayoutPath getLayoutPath() {
2716         return textLine.getLayoutPath();
2717     }




  53 import java.awt.geom.Point2D;
  54 import java.awt.geom.Rectangle2D;
  55 import java.text.AttributedString;
  56 import java.text.AttributedCharacterIterator;
  57 import java.text.AttributedCharacterIterator.Attribute;
  58 import java.text.CharacterIterator;
  59 import java.util.Map;
  60 import java.util.HashMap;
  61 import java.util.Hashtable;
  62 import sun.font.AttributeValues;
  63 import sun.font.CodePointIterator;
  64 import sun.font.CoreMetrics;
  65 import sun.font.Decoration;
  66 import sun.font.FontLineMetrics;
  67 import sun.font.FontResolver;
  68 import sun.font.GraphicComponent;
  69 import sun.font.LayoutPathImpl;
  70 
  71 /**
  72  *
  73  * {@code TextLayout} is an immutable graphical representation of styled
  74  * character data.
  75  * <p>
  76  * It provides the following capabilities:
  77  * <ul>
  78  * <li>implicit bidirectional analysis and reordering,
  79  * <li>cursor positioning and movement, including split cursors for
  80  * mixed directional text,
  81  * <li>highlighting, including both logical and visual highlighting
  82  * for mixed directional text,
  83  * <li>multiple baselines (roman, hanging, and centered),
  84  * <li>hit testing,
  85  * <li>justification,
  86  * <li>default font substitution,
  87  * <li>metric information such as ascent, descent, and advance, and
  88  * <li>rendering
  89  * </ul>
  90  * <p>
  91  * A {@code TextLayout} object can be rendered using
  92  * its {@code draw} method.
  93  * <p>
  94  * {@code TextLayout} can be constructed either directly or through
  95  * the use of a {@link LineBreakMeasurer}.  When constructed directly, the
  96  * source text represents a single paragraph.  {@code LineBreakMeasurer}
  97  * allows styled text to be broken into lines that fit within a particular
  98  * width.  See the {@code LineBreakMeasurer} documentation for more
  99  * information.
 100  * <p>
 101  * {@code TextLayout} construction logically proceeds as follows:
 102  * <ul>
 103  * <li>paragraph attributes are extracted and examined,
 104  * <li>text is analyzed for bidirectional reordering, and reordering
 105  * information is computed if needed,
 106  * <li>text is segmented into style runs
 107  * <li>fonts are chosen for style runs, first by using a font if the
 108  * attribute {@link TextAttribute#FONT} is present, otherwise by computing
 109  * a default font using the attributes that have been defined
 110  * <li>if text is on multiple baselines, the runs or subruns are further
 111  * broken into subruns sharing a common baseline,
 112  * <li>glyphvectors are generated for each run using the chosen font,
 113  * <li>final bidirectional reordering is performed on the glyphvectors
 114  * </ul>
 115  * <p>
 116  * All graphical information returned from a {@code TextLayout}
 117  * object's methods is relative to the origin of the
 118  * {@code TextLayout}, which is the intersection of the
 119  * {@code TextLayout} object's baseline with its left edge.  Also,
 120  * coordinates passed into a {@code TextLayout} object's methods
 121  * are assumed to be relative to the {@code TextLayout} object's
 122  * origin.  Clients usually need to translate between a
 123  * {@code TextLayout} object's coordinate system and the coordinate
 124  * system in another object (such as a
 125  * {@link java.awt.Graphics Graphics} object).
 126  * <p>
 127  * {@code TextLayout} objects are constructed from styled text,
 128  * but they do not retain a reference to their source text.  Thus,
 129  * changes in the text previously used to generate a {@code TextLayout}
 130  * do not affect the {@code TextLayout}.
 131  * <p>
 132  * Three methods on a {@code TextLayout} object
 133  * ({@code getNextRightHit}, {@code getNextLeftHit}, and
 134  * {@code hitTestChar}) return instances of {@link TextHitInfo}.
 135  * The offsets contained in these {@code TextHitInfo} objects
 136  * are relative to the start of the {@code TextLayout}, <b>not</b>
 137  * to the text used to create the {@code TextLayout}.  Similarly,
 138  * {@code TextLayout} methods that accept {@code TextHitInfo}
 139  * instances as parameters expect the {@code TextHitInfo} object's
 140  * offsets to be relative to the {@code TextLayout}, not to any
 141  * underlying text storage model.
 142  * <p>
 143  * <strong>Examples</strong>:<p>
 144  * Constructing and drawing a {@code TextLayout} and its bounding
 145  * rectangle:
 146  * <blockquote><pre>
 147  *   Graphics2D g = ...;
 148  *   Point2D loc = ...;
 149  *   Font font = Font.getFont("Helvetica-bold-italic");
 150  *   FontRenderContext frc = g.getFontRenderContext();
 151  *   TextLayout layout = new TextLayout("This is a string", font, frc);
 152  *   layout.draw(g, (float)loc.getX(), (float)loc.getY());
 153  *
 154  *   Rectangle2D bounds = layout.getBounds();
 155  *   bounds.setRect(bounds.getX()+loc.getX(),
 156  *                  bounds.getY()+loc.getY(),
 157  *                  bounds.getWidth(),
 158  *                  bounds.getHeight());
 159  *   g.draw(bounds);
 160  * </pre>
 161  * </blockquote>
 162  * <p>
 163  * Hit-testing a {@code TextLayout} (determining which character is at
 164  * a particular graphical location):
 165  * <blockquote><pre>
 166  *   Point2D click = ...;
 167  *   TextHitInfo hit = layout.hitTestChar(
 168  *                         (float) (click.getX() - loc.getX()),
 169  *                         (float) (click.getY() - loc.getY()));
 170  * </pre>
 171  * </blockquote>
 172  * <p>
 173  * Responding to a right-arrow key press:
 174  * <blockquote><pre>
 175  *   int insertionIndex = ...;
 176  *   TextHitInfo next = layout.getNextRightHit(insertionIndex);
 177  *   if (next != null) {
 178  *       // translate graphics to origin of layout on screen
 179  *       g.translate(loc.getX(), loc.getY());
 180  *       Shape[] carets = layout.getCaretShapes(next.getInsertionIndex());
 181  *       g.draw(carets[0]);
 182  *       if (carets[1] != null) {
 183  *           g.draw(carets[1]);


 186  * </pre></blockquote>
 187  * <p>
 188  * Drawing a selection range corresponding to a substring in the source text.
 189  * The selected area may not be visually contiguous:
 190  * <blockquote><pre>
 191  *   // selStart, selLimit should be relative to the layout,
 192  *   // not to the source text
 193  *
 194  *   int selStart = ..., selLimit = ...;
 195  *   Color selectionColor = ...;
 196  *   Shape selection = layout.getLogicalHighlightShape(selStart, selLimit);
 197  *   // selection may consist of disjoint areas
 198  *   // graphics is assumed to be translated to origin of layout
 199  *   g.setColor(selectionColor);
 200  *   g.fill(selection);
 201  * </pre></blockquote>
 202  * <p>
 203  * Drawing a visually contiguous selection range.  The selection range may
 204  * correspond to more than one substring in the source text.  The ranges of
 205  * the corresponding source text substrings can be obtained with
 206  * {@code getLogicalRangesForVisualSelection()}:
 207  * <blockquote><pre>
 208  *   TextHitInfo selStart = ..., selLimit = ...;
 209  *   Shape selection = layout.getVisualHighlightShape(selStart, selLimit);
 210  *   g.setColor(selectionColor);
 211  *   g.fill(selection);
 212  *   int[] ranges = getLogicalRangesForVisualSelection(selStart, selLimit);
 213  *   // ranges[0], ranges[1] is the first selection range,
 214  *   // ranges[2], ranges[3] is the second selection range, etc.
 215  * </pre></blockquote>
 216  * <p>
 217  * Note: Font rotations can cause text baselines to be rotated, and
 218  * multiple runs with different rotations can cause the baseline to
 219  * bend or zig-zag.  In order to account for this (rare) possibility,
 220  * some APIs are specified to return metrics and take parameters 'in
 221  * baseline-relative coordinates' (e.g. ascent, advance), and others
 222  * are in 'in standard coordinates' (e.g. getBounds).  Values in
 223  * baseline-relative coordinates map the 'x' coordinate to the
 224  * distance along the baseline, (positive x is forward along the
 225  * baseline), and the 'y' coordinate to a distance along the
 226  * perpendicular to the baseline at 'x' (positive y is 90 degrees


 282     /*
 283      * Natural bounds is used internally.  It is built on demand in
 284      * getNaturalBounds.
 285      */
 286     private Rectangle2D naturalBounds = null;
 287 
 288     /*
 289      * boundsRect encloses all of the bits this TextLayout can draw.  It
 290      * is build on demand in getBounds.
 291      */
 292     private Rectangle2D boundsRect = null;
 293 
 294     /*
 295      * flag to suppress/allow carets inside of ligatures when hit testing or
 296      * arrow-keying
 297      */
 298     private boolean caretsInLigaturesAreAllowed = false;
 299 
 300     /**
 301      * Defines a policy for determining the strong caret location.
 302      * This class contains one method, {@code getStrongCaret}, which
 303      * is used to specify the policy that determines the strong caret in
 304      * dual-caret text.  The strong caret is used to move the caret to the
 305      * left or right. Instances of this class can be passed to
 306      * {@code getCaretShapes}, {@code getNextLeftHit} and
 307      * {@code getNextRightHit} to customize strong caret
 308      * selection.
 309      * <p>
 310      * To specify alternate caret policies, subclass {@code CaretPolicy}
 311      * and override {@code getStrongCaret}.  {@code getStrongCaret}
 312      * should inspect the two {@code TextHitInfo} arguments and choose
 313      * one of them as the strong caret.
 314      * <p>
 315      * Most clients do not need to use this class.
 316      */
 317     public static class CaretPolicy {
 318 
 319         /**
 320          * Constructs a {@code CaretPolicy}.
 321          */
 322          public CaretPolicy() {
 323          }
 324 
 325         /**
 326          * Chooses one of the specified {@code TextHitInfo} instances as
 327          * a strong caret in the specified {@code TextLayout}.
 328          * @param hit1 a valid hit in {@code layout}
 329          * @param hit2 a valid hit in {@code layout}
 330          * @param layout the {@code TextLayout} in which
 331          *        {@code hit1} and {@code hit2} are used
 332          * @return {@code hit1} or {@code hit2}
 333          *        (or an equivalent {@code TextHitInfo}), indicating the
 334          *        strong caret.
 335          */
 336         public TextHitInfo getStrongCaret(TextHitInfo hit1,
 337                                           TextHitInfo hit2,
 338                                           TextLayout layout) {
 339 
 340             // default implementation just calls private method on layout
 341             return layout.getStrongHit(hit1, hit2);
 342         }
 343     }
 344 
 345     /**
 346      * This {@code CaretPolicy} is used when a policy is not specified
 347      * by the client.  With this policy, a hit on a character whose direction
 348      * is the same as the line direction is stronger than a hit on a
 349      * counterdirectional character.  If the characters' directions are
 350      * the same, a hit on the leading edge of a character is stronger
 351      * than a hit on the trailing edge of a character.
 352      */
 353     public static final CaretPolicy DEFAULT_CARET_POLICY = new CaretPolicy();
 354 
 355     /**
 356      * Constructs a {@code TextLayout} from a {@code String}
 357      * and a {@link Font}.  All the text is styled using the specified
 358      * {@code Font}.
 359      * <p>
 360      * The {@code String} must specify a single paragraph of text,
 361      * because an entire paragraph is required for the bidirectional
 362      * algorithm.
 363      * @param string the text to display
 364      * @param font a {@code Font} used to style the text
 365      * @param frc contains information about a graphics device which is needed
 366      *       to measure the text correctly.
 367      *       Text measurements can vary slightly depending on the
 368      *       device resolution, and attributes such as antialiasing.  This
 369      *       parameter does not specify a translation between the
 370      *       {@code TextLayout} and user space.
 371      */
 372     public TextLayout(String string, Font font, FontRenderContext frc) {
 373 
 374         if (font == null) {
 375             throw new IllegalArgumentException("Null font passed to TextLayout constructor.");
 376         }
 377 
 378         if (string == null) {
 379             throw new IllegalArgumentException("Null string passed to TextLayout constructor.");
 380         }
 381 
 382         if (string.length() == 0) {
 383             throw new IllegalArgumentException("Zero length string passed to TextLayout constructor.");
 384         }
 385 
 386         Map<? extends Attribute, ?> attributes = null;
 387         if (font.hasLayoutAttributes()) {
 388             attributes = font.getAttributes();
 389         }
 390 
 391         char[] text = string.toCharArray();
 392         if (sameBaselineUpTo(font, text, 0, text.length) == text.length) {
 393             fastInit(text, font, attributes, frc);
 394         } else {
 395             AttributedString as = attributes == null
 396                 ? new AttributedString(string)
 397                 : new AttributedString(string, attributes);
 398             as.addAttribute(TextAttribute.FONT, font);
 399             standardInit(as.getIterator(), text, frc);
 400         }
 401     }
 402 
 403     /**
 404      * Constructs a {@code TextLayout} from a {@code String}
 405      * and an attribute set.
 406      * <p>
 407      * All the text is styled using the provided attributes.
 408      * <p>
 409      * {@code string} must specify a single paragraph of text because an
 410      * entire paragraph is required for the bidirectional algorithm.
 411      * @param string the text to display
 412      * @param attributes the attributes used to style the text
 413      * @param frc contains information about a graphics device which is needed
 414      *       to measure the text correctly.
 415      *       Text measurements can vary slightly depending on the
 416      *       device resolution, and attributes such as antialiasing.  This
 417      *       parameter does not specify a translation between the
 418      *       {@code TextLayout} and user space.
 419      */
 420     public TextLayout(String string, Map<? extends Attribute,?> attributes,
 421                       FontRenderContext frc)
 422     {
 423         if (string == null) {
 424             throw new IllegalArgumentException("Null string passed to TextLayout constructor.");
 425         }
 426 
 427         if (attributes == null) {
 428             throw new IllegalArgumentException("Null map passed to TextLayout constructor.");
 429         }
 430 
 431         if (string.length() == 0) {
 432             throw new IllegalArgumentException("Zero length string passed to TextLayout constructor.");
 433         }
 434 
 435         char[] text = string.toCharArray();
 436         Font font = singleFont(text, 0, text.length, attributes);
 437         if (font != null) {
 438             fastInit(text, font, attributes, frc);


 471                     return null;
 472                 }
 473             } else {
 474                 FontResolver resolver = FontResolver.getInstance();
 475                 CodePointIterator iter = CodePointIterator.create(text, start, limit);
 476                 int fontIndex = resolver.nextFontRunIndex(iter);
 477                 if (iter.charIndex() == limit) {
 478                     font = resolver.getFont(fontIndex, attributes);
 479                 }
 480             }
 481         }
 482 
 483         if (sameBaselineUpTo(font, text, start, limit) != limit) {
 484             return null;
 485         }
 486 
 487         return font;
 488     }
 489 
 490     /**
 491      * Constructs a {@code TextLayout} from an iterator over styled text.
 492      * <p>
 493      * The iterator must specify a single paragraph of text because an
 494      * entire paragraph is required for the bidirectional
 495      * algorithm.
 496      * @param text the styled text to display
 497      * @param frc contains information about a graphics device which is needed
 498      *       to measure the text correctly.
 499      *       Text measurements can vary slightly depending on the
 500      *       device resolution, and attributes such as antialiasing.  This
 501      *       parameter does not specify a translation between the
 502      *       {@code TextLayout} and user space.
 503      */
 504     public TextLayout(AttributedCharacterIterator text, FontRenderContext frc) {
 505 
 506         if (text == null) {
 507             throw new IllegalArgumentException("Null iterator passed to TextLayout constructor.");
 508         }
 509 
 510         int start = text.getBeginIndex();
 511         int limit = text.getEndIndex();
 512         if (start == limit) {
 513             throw new IllegalArgumentException("Zero length iterator passed to TextLayout constructor.");
 514         }
 515 
 516         int len = limit - start;
 517         text.first();
 518         char[] chars = new char[len];
 519         int n = 0;
 520         for (char c = text.first();
 521              c != CharacterIterator.DONE;
 522              c = text.next())
 523         {
 524             chars[n++] = c;
 525         }
 526 
 527         text.first();
 528         if (text.getRunLimit() == limit) {
 529 
 530             Map<? extends Attribute, ?> attributes = text.getAttributes();
 531             Font font = singleFont(chars, 0, len, attributes);
 532             if (font != null) {
 533                 fastInit(chars, font, attributes, frc);
 534                 return;
 535             }
 536         }
 537 
 538         standardInit(text, chars, frc);
 539     }
 540 
 541     /**
 542      * Creates a {@code TextLayout} from a {@link TextLine} and
 543      * some paragraph data.  This method is used by {@link TextMeasurer}.
 544      * @param textLine the line measurement attributes to apply to the
 545      *       the resulting {@code TextLayout}
 546      * @param baseline the baseline of the text
 547      * @param baselineOffsets the baseline offsets for this
 548      * {@code TextLayout}.  This should already be normalized to
 549      * {@code baseline}
 550      * @param justifyRatio {@code 0} if the {@code TextLayout}
 551      *     cannot be justified; {@code 1} otherwise.
 552      */
 553     TextLayout(TextLine textLine,
 554                byte baseline,
 555                float[] baselineOffsets,
 556                float justifyRatio) {
 557 
 558         this.characterCount = textLine.characterCount();
 559         this.baseline = baseline;
 560         this.baselineOffsets = baselineOffsets;
 561         this.textLine = textLine;
 562         this.justifyRatio = justifyRatio;
 563     }
 564 
 565     /**
 566      * Initialize the paragraph-specific data.
 567      */
 568     private void paragraphInit(byte aBaseline, CoreMetrics lm,
 569                                Map<? extends Attribute, ?> paragraphAttrs,
 570                                char[] text) {
 571 


 726         hashCodeCache = 0;
 727 
 728         cacheIsValid = true;
 729     }
 730 
 731     /**
 732      * The 'natural bounds' encloses all the carets the layout can draw.
 733      *
 734      */
 735     private Rectangle2D getNaturalBounds() {
 736         ensureCache();
 737 
 738         if (naturalBounds == null) {
 739             naturalBounds = textLine.getItalicBounds();
 740         }
 741 
 742         return naturalBounds;
 743     }
 744 
 745     /**
 746      * Creates a copy of this {@code TextLayout}.
 747      */
 748     protected Object clone() {
 749         /*
 750          * !!! I think this is safe.  Once created, nothing mutates the
 751          * glyphvectors or arrays.  But we need to make sure.
 752          * {jbr} actually, that's not quite true.  The justification code
 753          * mutates after cloning.  It doesn't actually change the glyphvectors
 754          * (that's impossible) but it replaces them with justified sets.  This
 755          * is a problem for GlyphIterator creation, since new GlyphIterators
 756          * are created by cloning a prototype.  If the prototype has outdated
 757          * glyphvectors, so will the new ones.  A partial solution is to set the
 758          * prototypical GlyphIterator to null when the glyphvectors change.  If
 759          * you forget this one time, you're hosed.
 760          */
 761         try {
 762             return super.clone();
 763         }
 764         catch (CloneNotSupportedException e) {
 765             throw new InternalError(e);
 766         }
 767     }
 768 
 769     /*
 770      * Utility to throw an exception if an invalid TextHitInfo is passed
 771      * as a parameter.  Avoids code duplication.
 772      */
 773     private void checkTextHit(TextHitInfo hit) {
 774         if (hit == null) {
 775             throw new IllegalArgumentException("TextHitInfo is null.");
 776         }
 777 
 778         if (hit.getInsertionIndex() < 0 ||
 779             hit.getInsertionIndex() > characterCount) {
 780             throw new IllegalArgumentException("TextHitInfo is out of range");
 781         }
 782     }
 783 
 784     /**
 785      * Creates a copy of this {@code TextLayout} justified to the
 786      * specified width.
 787      * <p>
 788      * If this {@code TextLayout} has already been justified, an
 789      * exception is thrown.  If this {@code TextLayout} object's
 790      * justification ratio is zero, a {@code TextLayout} identical
 791      * to this {@code TextLayout} is returned.
 792      * @param justificationWidth the width to use when justifying the line.
 793      * For best results, it should not be too different from the current
 794      * advance of the line.
 795      * @return a {@code TextLayout} justified to the specified width.
 796      * @exception Error if this layout has already been justified, an Error is
 797      * thrown.
 798      */
 799     public TextLayout getJustifiedLayout(float justificationWidth) {
 800 
 801         if (justificationWidth <= 0) {
 802             throw new IllegalArgumentException("justificationWidth <= 0 passed to TextLayout.getJustifiedLayout()");
 803         }
 804 
 805         if (justifyRatio == ALREADY_JUSTIFIED) {
 806             throw new Error("Can't justify again.");
 807         }
 808 
 809         ensureCache(); // make sure textLine is not null
 810 
 811         // default justification range to exclude trailing logical whitespace
 812         int limit = characterCount;
 813         while (limit > 0 && textLine.isCharWhitespace(limit-1)) {
 814             --limit;
 815         }


 832      * indicated width.  The current implementation also adjusts hanging
 833      * punctuation and trailing whitespace to overhang the justification width.
 834      * Once justified, the layout may not be rejustified.
 835      * <p>
 836      * Some code may rely on immutability of layouts.  Subclassers should not
 837      * call this directly, but instead should call getJustifiedLayout, which
 838      * will call this method on a clone of this layout, preserving
 839      * the original.
 840      *
 841      * @param justificationWidth the width to use when justifying the line.
 842      * For best results, it should not be too different from the current
 843      * advance of the line.
 844      * @see #getJustifiedLayout(float)
 845      */
 846     protected void handleJustify(float justificationWidth) {
 847       // never called
 848     }
 849 
 850 
 851     /**
 852      * Returns the baseline for this {@code TextLayout}.
 853      * The baseline is one of the values defined in {@code Font},
 854      * which are roman, centered and hanging.  Ascent and descent are
 855      * relative to this baseline.  The {@code baselineOffsets}
 856      * are also relative to this baseline.
 857      * @return the baseline of this {@code TextLayout}.
 858      * @see #getBaselineOffsets()
 859      * @see Font
 860      */
 861     public byte getBaseline() {
 862         return baseline;
 863     }
 864 
 865     /**
 866      * Returns the offsets array for the baselines used for this
 867      * {@code TextLayout}.
 868      * <p>
 869      * The array is indexed by one of the values defined in
 870      * {@code Font}, which are roman, centered and hanging.  The
 871      * values are relative to this {@code TextLayout} object's
 872      * baseline, so that {@code getBaselineOffsets[getBaseline()] == 0}.
 873      * Offsets are added to the position of the {@code TextLayout}
 874      * object's baseline to get the position for the new baseline.
 875      * @return the offsets array containing the baselines used for this
 876      *    {@code TextLayout}.
 877      * @see #getBaseline()
 878      * @see Font
 879      */
 880     public float[] getBaselineOffsets() {
 881         float[] offsets = new float[baselineOffsets.length];
 882         System.arraycopy(baselineOffsets, 0, offsets, 0, offsets.length);
 883         return offsets;
 884     }
 885 
 886     /**
 887      * Returns the advance of this {@code TextLayout}.
 888      * The advance is the distance from the origin to the advance of the
 889      * rightmost (bottommost) character.  This is in baseline-relative
 890      * coordinates.
 891      * @return the advance of this {@code TextLayout}.
 892      */
 893     public float getAdvance() {
 894         ensureCache();
 895         return lineMetrics.advance;
 896     }
 897 
 898     /**
 899      * Returns the advance of this {@code TextLayout}, minus trailing
 900      * whitespace.  This is in baseline-relative coordinates.
 901      * @return the advance of this {@code TextLayout} without the
 902      *      trailing whitespace.
 903      * @see #getAdvance()
 904      */
 905     public float getVisibleAdvance() {
 906         ensureCache();
 907         return visibleAdvance;
 908     }
 909 
 910     /**
 911      * Returns the ascent of this {@code TextLayout}.
 912      * The ascent is the distance from the top (right) of the
 913      * {@code TextLayout} to the baseline.  It is always either
 914      * positive or zero.  The ascent is sufficient to
 915      * accommodate superscripted text and is the maximum of the sum of the
 916      * ascent, offset, and baseline of each glyph.  The ascent is
 917      * the maximum ascent from the baseline of all the text in the
 918      * TextLayout.  It is in baseline-relative coordinates.
 919      * @return the ascent of this {@code TextLayout}.
 920      */
 921     public float getAscent() {
 922         ensureCache();
 923         return lineMetrics.ascent;
 924     }
 925 
 926     /**
 927      * Returns the descent of this {@code TextLayout}.
 928      * The descent is the distance from the baseline to the bottom (left) of
 929      * the {@code TextLayout}.  It is always either positive or zero.
 930      * The descent is sufficient to accommodate subscripted text and is the
 931      * maximum of the sum of the descent, offset, and baseline of each glyph.
 932      * This is the maximum descent from the baseline of all the text in
 933      * the TextLayout.  It is in baseline-relative coordinates.
 934      * @return the descent of this {@code TextLayout}.
 935      */
 936     public float getDescent() {
 937         ensureCache();
 938         return lineMetrics.descent;
 939     }
 940 
 941     /**
 942      * Returns the leading of the {@code TextLayout}.
 943      * The leading is the suggested interline spacing for this
 944      * {@code TextLayout}.  This is in baseline-relative
 945      * coordinates.
 946      * <p>
 947      * The leading is computed from the leading, descent, and baseline
 948      * of all glyphvectors in the {@code TextLayout}.  The algorithm
 949      * is roughly as follows:
 950      * <blockquote><pre>
 951      * maxD = 0;
 952      * maxDL = 0;
 953      * for (GlyphVector g in all glyphvectors) {
 954      *    maxD = max(maxD, g.getDescent() + offsets[g.getBaseline()]);
 955      *    maxDL = max(maxDL, g.getDescent() + g.getLeading() +
 956      *                       offsets[g.getBaseline()]);
 957      * }
 958      * return maxDL - maxD;
 959      * </pre></blockquote>
 960      * @return the leading of this {@code TextLayout}.
 961      */
 962     public float getLeading() {
 963         ensureCache();
 964         return lineMetrics.leading;
 965     }
 966 
 967     /**
 968      * Returns the bounds of this {@code TextLayout}.
 969      * The bounds are in standard coordinates.
 970      * <p>Due to rasterization effects, this bounds might not enclose all of the
 971      * pixels rendered by the TextLayout.</p>
 972      * It might not coincide exactly with the ascent, descent,
 973      * origin or advance of the {@code TextLayout}.
 974      * @return a {@link Rectangle2D} that is the bounds of this
 975      *        {@code TextLayout}.
 976      */
 977     public Rectangle2D getBounds() {
 978         ensureCache();
 979 
 980         if (boundsRect == null) {
 981             Rectangle2D vb = textLine.getVisualBounds();
 982             if (dx != 0 || dy != 0) {
 983                 vb.setRect(vb.getX() - dx,
 984                            vb.getY() - dy,
 985                            vb.getWidth(),
 986                            vb.getHeight());
 987             }
 988             boundsRect = vb;
 989         }
 990 
 991         Rectangle2D bounds = new Rectangle2D.Float();
 992         bounds.setRect(boundsRect);
 993 
 994         return bounds;
 995     }
 996 
 997     /**
 998      * Returns the pixel bounds of this {@code TextLayout} when
 999      * rendered in a graphics with the given
1000      * {@code FontRenderContext} at the given location.  The
1001      * graphics render context need not be the same as the
1002      * {@code FontRenderContext} used to create this
1003      * {@code TextLayout}, and can be null.  If it is null, the
1004      * {@code FontRenderContext} of this {@code TextLayout}
1005      * is used.
1006      * @param frc the {@code FontRenderContext} of the {@code Graphics}.
1007      * @param x the x-coordinate at which to render this {@code TextLayout}.
1008      * @param y the y-coordinate at which to render this {@code TextLayout}.
1009      * @return a {@code Rectangle} bounding the pixels that would be affected.
1010      * @see GlyphVector#getPixelBounds
1011      * @since 1.6
1012      */
1013     public Rectangle getPixelBounds(FontRenderContext frc, float x, float y) {
1014         return textLine.getPixelBounds(frc, x, y);
1015     }
1016 
1017     /**
1018      * Returns {@code true} if this {@code TextLayout} has
1019      * a left-to-right base direction or {@code false} if it has
1020      * a right-to-left base direction.  The {@code TextLayout}
1021      * has a base direction of either left-to-right (LTR) or
1022      * right-to-left (RTL).  The base direction is independent of the
1023      * actual direction of text on the line, which may be either LTR,
1024      * RTL, or mixed. Left-to-right layouts by default should position
1025      * flush left.  If the layout is on a tabbed line, the
1026      * tabs run left to right, so that logically successive layouts position
1027      * left to right.  The opposite is true for RTL layouts. By default they
1028      * should position flush left, and tabs run right-to-left.
1029      * @return {@code true} if the base direction of this
1030      *         {@code TextLayout} is left-to-right; {@code false}
1031      *         otherwise.
1032      */
1033     public boolean isLeftToRight() {
1034         return textLine.isDirectionLTR();
1035     }
1036 
1037     /**
1038      * Returns {@code true} if this {@code TextLayout} is vertical.
1039      * @return {@code true} if this {@code TextLayout} is vertical;
1040      *      {@code false} otherwise.
1041      */
1042     public boolean isVertical() {
1043         return isVerticalLine;
1044     }
1045 
1046     /**
1047      * Returns the number of characters represented by this
1048      * {@code TextLayout}.
1049      * @return the number of characters in this {@code TextLayout}.
1050      */
1051     public int getCharacterCount() {
1052         return characterCount;
1053     }
1054 
1055     /*
1056      * carets and hit testing
1057      *
1058      * Positions on a text line are represented by instances of TextHitInfo.
1059      * Any TextHitInfo with characterOffset between 0 and characterCount-1,
1060      * inclusive, represents a valid position on the line.  Additionally,
1061      * [-1, trailing] and [characterCount, leading] are valid positions, and
1062      * represent positions at the logical start and end of the line,
1063      * respectively.
1064      *
1065      * The characterOffsets in TextHitInfo's used and returned by TextLayout
1066      * are relative to the beginning of the text layout, not necessarily to
1067      * the beginning of the text storage the client is using.
1068      *
1069      *


1182         float topX = (top1X + top2X) / 2;
1183         float bottomX = (bottom1X + bottom2X) / 2;
1184 
1185         if (info == null) {
1186             info = new float[2];
1187         }
1188 
1189         if (isVerticalLine) {
1190             info[1] = (float) ((topX - bottomX) / bounds.getWidth());
1191             info[0] = (float) (topX + (info[1]*bounds.getX()));
1192         }
1193         else {
1194             info[1] = (float) ((topX - bottomX) / bounds.getHeight());
1195             info[0] = (float) (bottomX + (info[1]*bounds.getMaxY()));
1196         }
1197 
1198         return info;
1199     }
1200 
1201     /**
1202      * Returns information about the caret corresponding to {@code hit}.
1203      * The first element of the array is the intersection of the caret with
1204      * the baseline, as a distance along the baseline. The second element
1205      * of the array is the inverse slope (run/rise) of the caret, measured
1206      * with respect to the baseline at that point.
1207      * <p>
1208      * This method is meant for informational use.  To display carets, it
1209      * is better to use {@code getCaretShapes}.
1210      * @param hit a hit on a character in this {@code TextLayout}
1211      * @param bounds the bounds to which the caret info is constructed.
1212      *     The bounds is in baseline-relative coordinates.
1213      * @return a two-element array containing the position and slope of
1214      * the caret.  The returned caret info is in baseline-relative coordinates.
1215      * @see #getCaretShapes(int, Rectangle2D, TextLayout.CaretPolicy)
1216      * @see Font#getItalicAngle
1217      */
1218     public float[] getCaretInfo(TextHitInfo hit, Rectangle2D bounds) {
1219         ensureCache();
1220         checkTextHit(hit);
1221 
1222         return getCaretInfoTestInternal(hit, bounds);
1223     }
1224 
1225     // this version provides extra info in the float array
1226     // the first two values are as above
1227     // the next four values are the endpoints of the caret, as computed
1228     // using the hit character's offset (baseline + ssoffset) and
1229     // natural ascent and descent.
1230     // these  values are trimmed to the bounds where required to fit,


1293                     p2y = bo + thiscm.descent;
1294                 } else {
1295                     ixbase -= iangle * thiscm.ssOffset;
1296                     p1y = ixbase + iangle * thiscm.ascent;
1297                     p2y = ixbase - iangle * thiscm.descent;
1298                     p1x = bo + thiscm.ascent;
1299                     p2x = bo + thiscm.descent;
1300                 }
1301             }
1302         }
1303 
1304         info[2] = (float)p1x;
1305         info[3] = (float)p1y;
1306         info[4] = (float)p2x;
1307         info[5] = (float)p2y;
1308 
1309         return info;
1310     }
1311 
1312     /**
1313      * Returns information about the caret corresponding to {@code hit}.
1314      * This method is a convenience overload of {@code getCaretInfo} and
1315      * uses the natural bounds of this {@code TextLayout}.
1316      * @param hit a hit on a character in this {@code TextLayout}
1317      * @return the information about a caret corresponding to a hit.  The
1318      *     returned caret info is in baseline-relative coordinates.
1319      */
1320     public float[] getCaretInfo(TextHitInfo hit) {
1321 
1322         return getCaretInfo(hit, getNaturalBounds());
1323     }
1324 
1325     /**
1326      * Returns a caret index corresponding to {@code hit}.
1327      * Carets are numbered from left to right (top to bottom) starting from
1328      * zero. This always places carets next to the character hit, on the
1329      * indicated side of the character.
1330      * @param hit a hit on a character in this {@code TextLayout}
1331      * @return a caret index corresponding to the specified hit.
1332      */
1333     private int hitToCaret(TextHitInfo hit) {
1334 
1335         int hitIndex = hit.getCharIndex();
1336 
1337         if (hitIndex < 0) {
1338             return textLine.isDirectionLTR() ? 0 : characterCount;
1339         } else if (hitIndex >= characterCount) {
1340             return textLine.isDirectionLTR() ? characterCount : 0;
1341         }
1342 
1343         int visIndex = textLine.logicalToVisual(hitIndex);
1344 
1345         if (hit.isLeadingEdge() != textLine.isCharLTR(hitIndex)) {
1346             ++visIndex;
1347         }
1348 
1349         return visIndex;
1350     }


1384             return true;
1385         }
1386 
1387         int offset = textLine.visualToLogical(caret);
1388 
1389         if (!textLine.isCharLTR(offset)) {
1390             offset = textLine.visualToLogical(caret-1);
1391             if (textLine.isCharLTR(offset)) {
1392                 return true;
1393             }
1394         }
1395 
1396         // At this point, the leading edge of the character
1397         // at offset is at the given caret.
1398 
1399         return textLine.caretAtOffsetIsValid(offset);
1400     }
1401 
1402     /**
1403      * Returns the hit for the next caret to the right (bottom); if there
1404      * is no such hit, returns {@code null}.
1405      * If the hit character index is out of bounds, an
1406      * {@link IllegalArgumentException} is thrown.
1407      * @param hit a hit on a character in this layout
1408      * @return a hit whose caret appears at the next position to the
1409      * right (bottom) of the caret of the provided hit or {@code null}.
1410      */
1411     public TextHitInfo getNextRightHit(TextHitInfo hit) {
1412         ensureCache();
1413         checkTextHit(hit);
1414 
1415         int caret = hitToCaret(hit);
1416 
1417         if (caret == characterCount) {
1418             return null;
1419         }
1420 
1421         do {
1422             ++caret;
1423         } while (!caretIsValid(caret));
1424 
1425         return caretToHit(caret);
1426     }
1427 
1428     /**
1429      * Returns the hit for the next caret to the right (bottom); if no
1430      * such hit, returns {@code null}.  The hit is to the right of
1431      * the strong caret at the specified offset, as determined by the
1432      * specified policy.
1433      * The returned hit is the stronger of the two possible
1434      * hits, as determined by the specified policy.
1435      * @param offset an insertion offset in this {@code TextLayout}.
1436      * Cannot be less than 0 or greater than this {@code TextLayout}
1437      * object's character count.
1438      * @param policy the policy used to select the strong caret
1439      * @return a hit whose caret appears at the next position to the
1440      * right (bottom) of the caret of the provided hit, or {@code null}.
1441      */
1442     public TextHitInfo getNextRightHit(int offset, CaretPolicy policy) {
1443 
1444         if (offset < 0 || offset > characterCount) {
1445             throw new IllegalArgumentException("Offset out of bounds in TextLayout.getNextRightHit()");
1446         }
1447 
1448         if (policy == null) {
1449             throw new IllegalArgumentException("Null CaretPolicy passed to TextLayout.getNextRightHit()");
1450         }
1451 
1452         TextHitInfo hit1 = TextHitInfo.afterOffset(offset);
1453         TextHitInfo hit2 = hit1.getOtherHit();
1454 
1455         TextHitInfo nextHit = getNextRightHit(policy.getStrongCaret(hit1, hit2, this));
1456 
1457         if (nextHit != null) {
1458             TextHitInfo otherHit = getVisualOtherHit(nextHit);
1459             return policy.getStrongCaret(otherHit, nextHit, this);
1460         }
1461         else {
1462             return null;
1463         }
1464     }
1465 
1466     /**
1467      * Returns the hit for the next caret to the right (bottom); if no
1468      * such hit, returns {@code null}.  The hit is to the right of
1469      * the strong caret at the specified offset, as determined by the
1470      * default policy.
1471      * The returned hit is the stronger of the two possible
1472      * hits, as determined by the default policy.
1473      * @param offset an insertion offset in this {@code TextLayout}.
1474      * Cannot be less than 0 or greater than the {@code TextLayout}
1475      * object's character count.
1476      * @return a hit whose caret appears at the next position to the
1477      * right (bottom) of the caret of the provided hit, or {@code null}.
1478      */
1479     public TextHitInfo getNextRightHit(int offset) {
1480 
1481         return getNextRightHit(offset, DEFAULT_CARET_POLICY);
1482     }
1483 
1484     /**
1485      * Returns the hit for the next caret to the left (top); if no such
1486      * hit, returns {@code null}.
1487      * If the hit character index is out of bounds, an
1488      * {@code IllegalArgumentException} is thrown.
1489      * @param hit a hit on a character in this {@code TextLayout}.
1490      * @return a hit whose caret appears at the next position to the
1491      * left (top) of the caret of the provided hit, or {@code null}.
1492      */
1493     public TextHitInfo getNextLeftHit(TextHitInfo hit) {
1494         ensureCache();
1495         checkTextHit(hit);
1496 
1497         int caret = hitToCaret(hit);
1498 
1499         if (caret == 0) {
1500             return null;
1501         }
1502 
1503         do {
1504             --caret;
1505         } while(!caretIsValid(caret));
1506 
1507         return caretToHit(caret);
1508     }
1509 
1510     /**
1511      * Returns the hit for the next caret to the left (top); if no
1512      * such hit, returns {@code null}.  The hit is to the left of
1513      * the strong caret at the specified offset, as determined by the
1514      * specified policy.
1515      * The returned hit is the stronger of the two possible
1516      * hits, as determined by the specified policy.
1517      * @param offset an insertion offset in this {@code TextLayout}.
1518      * Cannot be less than 0 or greater than this {@code TextLayout}
1519      * object's character count.
1520      * @param policy the policy used to select the strong caret
1521      * @return a hit whose caret appears at the next position to the
1522      * left (top) of the caret of the provided hit, or {@code null}.
1523      */
1524     public TextHitInfo getNextLeftHit(int offset, CaretPolicy policy) {
1525 
1526         if (policy == null) {
1527             throw new IllegalArgumentException("Null CaretPolicy passed to TextLayout.getNextLeftHit()");
1528         }
1529 
1530         if (offset < 0 || offset > characterCount) {
1531             throw new IllegalArgumentException("Offset out of bounds in TextLayout.getNextLeftHit()");
1532         }
1533 
1534         TextHitInfo hit1 = TextHitInfo.afterOffset(offset);
1535         TextHitInfo hit2 = hit1.getOtherHit();
1536 
1537         TextHitInfo nextHit = getNextLeftHit(policy.getStrongCaret(hit1, hit2, this));
1538 
1539         if (nextHit != null) {
1540             TextHitInfo otherHit = getVisualOtherHit(nextHit);
1541             return policy.getStrongCaret(otherHit, nextHit, this);
1542         }
1543         else {
1544             return null;
1545         }
1546     }
1547 
1548     /**
1549      * Returns the hit for the next caret to the left (top); if no
1550      * such hit, returns {@code null}.  The hit is to the left of
1551      * the strong caret at the specified offset, as determined by the
1552      * default policy.
1553      * The returned hit is the stronger of the two possible
1554      * hits, as determined by the default policy.
1555      * @param offset an insertion offset in this {@code TextLayout}.
1556      * Cannot be less than 0 or greater than this {@code TextLayout}
1557      * object's character count.
1558      * @return a hit whose caret appears at the next position to the
1559      * left (top) of the caret of the provided hit, or {@code null}.
1560      */
1561     public TextHitInfo getNextLeftHit(int offset) {
1562 
1563         return getNextLeftHit(offset, DEFAULT_CARET_POLICY);
1564     }
1565 
1566     /**
1567      * Returns the hit on the opposite side of the specified hit's caret.
1568      * @param hit the specified hit
1569      * @return a hit that is on the opposite side of the specified hit's
1570      *    caret.
1571      */
1572     public TextHitInfo getVisualOtherHit(TextHitInfo hit) {
1573 
1574         ensureCache();
1575         checkTextHit(hit);
1576 
1577         int hitCharIndex = hit.getCharIndex();
1578 
1579         int charIndex;


1760     private static GeneralPath pathToShape(double[] path, boolean close, LayoutPathImpl lp) {
1761         GeneralPath result = new GeneralPath(GeneralPath.WIND_EVEN_ODD, path.length);
1762         result.moveTo((float)path[0], (float)path[1]);
1763         for (int i = 2; i < path.length; i += 2) {
1764             result.lineTo((float)path[i], (float)path[i+1]);
1765         }
1766         if (close) {
1767             result.closePath();
1768         }
1769 
1770         if (lp != null) {
1771             result = (GeneralPath)lp.mapShape(result);
1772         }
1773         return result;
1774     }
1775 
1776     /**
1777      * Returns a {@link Shape} representing the caret at the specified
1778      * hit inside the specified bounds.
1779      * @param hit the hit at which to generate the caret
1780      * @param bounds the bounds of the {@code TextLayout} to use
1781      *    in generating the caret.  The bounds is in baseline-relative
1782      *    coordinates.
1783      * @return a {@code Shape} representing the caret.  The returned
1784      *    shape is in standard coordinates.
1785      */
1786     public Shape getCaretShape(TextHitInfo hit, Rectangle2D bounds) {
1787         ensureCache();
1788         checkTextHit(hit);
1789 
1790         if (bounds == null) {
1791             throw new IllegalArgumentException("Null Rectangle2D passed to TextLayout.getCaret()");
1792         }
1793 
1794         return pathToShape(getCaretPath(hit, bounds), false, textLine.getLayoutPath());
1795     }
1796 
1797     /**
1798      * Returns a {@code Shape} representing the caret at the specified
1799      * hit inside the natural bounds of this {@code TextLayout}.
1800      * @param hit the hit at which to generate the caret
1801      * @return a {@code Shape} representing the caret.  The returned
1802      *     shape is in standard coordinates.
1803      */
1804     public Shape getCaretShape(TextHitInfo hit) {
1805 
1806         return getCaretShape(hit, getNaturalBounds());
1807     }
1808 
1809     /**
1810      * Return the "stronger" of the TextHitInfos.  The TextHitInfos
1811      * should be logical or visual counterparts.  They are not
1812      * checked for validity.
1813      */
1814     private TextHitInfo getStrongHit(TextHitInfo hit1, TextHitInfo hit2) {
1815 
1816         // right now we're using the following rule for strong hits:
1817         // A hit on a character with a lower level
1818         // is stronger than one on a character with a higher level.
1819         // If this rule ties, the hit on the leading edge of a character wins.
1820         // If THIS rule ties, hit1 wins.  Both rules shouldn't tie, unless the
1821         // infos aren't counterparts of some sort.
1822 
1823         byte hit1Level = getCharacterLevel(hit1.getCharIndex());
1824         byte hit2Level = getCharacterLevel(hit2.getCharIndex());
1825 
1826         if (hit1Level == hit2Level) {
1827             if (hit2.isLeadingEdge() && !hit1.isLeadingEdge()) {
1828                 return hit2;
1829             }
1830             else {
1831                 return hit1;
1832             }
1833         }
1834         else {
1835             return (hit1Level < hit2Level)? hit1 : hit2;
1836         }
1837     }
1838 
1839     /**
1840      * Returns the level of the character at {@code index}.
1841      * Indices -1 and {@code characterCount} are assigned the base
1842      * level of this {@code TextLayout}.
1843      * @param index the index of the character from which to get the level
1844      * @return the level of the character at the specified index.
1845      */
1846     public byte getCharacterLevel(int index) {
1847 
1848         // hmm, allow indices at endpoints?  For now, yes.
1849         if (index < -1 || index > characterCount) {
1850             throw new IllegalArgumentException("Index is out of range in getCharacterLevel.");
1851         }
1852 
1853         ensureCache();
1854         if (index == -1 || index == characterCount) {
1855              return (byte) (textLine.isDirectionLTR()? 0 : 1);
1856         }
1857 
1858         return textLine.getCharLevel(index);
1859     }
1860 
1861     /**
1862      * Returns two paths corresponding to the strong and weak caret.
1863      * @param offset an offset in this {@code TextLayout}
1864      * @param bounds the bounds to which to extend the carets.  The
1865      * bounds is in baseline-relative coordinates.
1866      * @param policy the specified {@code CaretPolicy}
1867      * @return an array of two paths.  Element zero is the strong
1868      * caret.  If there are two carets, element one is the weak caret,
1869      * otherwise it is {@code null}. The returned shapes
1870      * are in standard coordinates.
1871      */
1872     public Shape[] getCaretShapes(int offset, Rectangle2D bounds, CaretPolicy policy) {
1873 
1874         ensureCache();
1875 
1876         if (offset < 0 || offset > characterCount) {
1877             throw new IllegalArgumentException("Offset out of bounds in TextLayout.getCaretShapes()");
1878         }
1879 
1880         if (bounds == null) {
1881             throw new IllegalArgumentException("Null Rectangle2D passed to TextLayout.getCaretShapes()");
1882         }
1883 
1884         if (policy == null) {
1885             throw new IllegalArgumentException("Null CaretPolicy passed to TextLayout.getCaretShapes()");
1886         }
1887 
1888         Shape[] result = new Shape[2];
1889 


1903             Shape otherShape = pathToShape(getCaretPath(otherHit, bounds), false, lp);
1904 
1905             TextHitInfo strongHit = policy.getStrongCaret(hit, otherHit, this);
1906             boolean hitIsStrong = strongHit.equals(hit);
1907 
1908             if (hitIsStrong) {// then other is weak
1909                 result[0] = hitShape;
1910                 result[1] = otherShape;
1911             }
1912             else {
1913                 result[0] = otherShape;
1914                 result[1] = hitShape;
1915             }
1916         }
1917 
1918         return result;
1919     }
1920 
1921     /**
1922      * Returns two paths corresponding to the strong and weak caret.
1923      * This method is a convenience overload of {@code getCaretShapes}
1924      * that uses the default caret policy.
1925      * @param offset an offset in this {@code TextLayout}
1926      * @param bounds the bounds to which to extend the carets.  This is
1927      *     in baseline-relative coordinates.
1928      * @return two paths corresponding to the strong and weak caret as
1929      *    defined by the {@code DEFAULT_CARET_POLICY}.  These are
1930      *    in standard coordinates.
1931      */
1932     public Shape[] getCaretShapes(int offset, Rectangle2D bounds) {
1933         // {sfb} parameter checking is done in overloaded version
1934         return getCaretShapes(offset, bounds, DEFAULT_CARET_POLICY);
1935     }
1936 
1937     /**
1938      * Returns two paths corresponding to the strong and weak caret.
1939      * This method is a convenience overload of {@code getCaretShapes}
1940      * that uses the default caret policy and this {@code TextLayout}
1941      * object's natural bounds.
1942      * @param offset an offset in this {@code TextLayout}
1943      * @return two paths corresponding to the strong and weak caret as
1944      *    defined by the {@code DEFAULT_CARET_POLICY}.  These are
1945      *    in standard coordinates.
1946      */
1947     public Shape[] getCaretShapes(int offset) {
1948         // {sfb} parameter checking is done in overloaded version
1949         return getCaretShapes(offset, getNaturalBounds(), DEFAULT_CARET_POLICY);
1950     }
1951 
1952     // A utility to return a path enclosing the given path
1953     // Path0 must be left or top of path1
1954     // {jbr} no assumptions about size of path0, path1 anymore.
1955     private GeneralPath boundingShape(double[] path0, double[] path1) {
1956 
1957         // Really, we want the path to be a convex hull around all of the
1958         // points in path0 and path1.  But we can get by with less than
1959         // that.  We do need to prevent the two segments which
1960         // join path0 to path1 from crossing each other.  So, if we
1961         // traverse path0 from top to bottom, we'll traverse path1 from
1962         // bottom to top (and vice versa).
1963 
1964         GeneralPath result = pathToShape(path0, false, null);


2051                 bounds.getY() + bounds.getHeight()
2052             };
2053         } else {
2054             path1 = new double[] {
2055                 bounds.getX() + bounds.getWidth(),
2056                 bounds.getY() + bounds.getHeight(),
2057                 bounds.getX() + bounds.getWidth(),
2058                 bounds.getY()
2059             };
2060         }
2061 
2062         double[] path0 = getCaretPath(characterCount, bounds, true);
2063 
2064         return boundingShape(path0, path1);
2065     }
2066 
2067     /**
2068      * Returns the logical ranges of text corresponding to a visual selection.
2069      * @param firstEndpoint an endpoint of the visual range
2070      * @param secondEndpoint the other endpoint of the visual range.
2071      * This endpoint can be less than {@code firstEndpoint}.
2072      * @return an array of integers representing start/limit pairs for the
2073      * selected ranges.
2074      * @see #getVisualHighlightShape(TextHitInfo, TextHitInfo, Rectangle2D)
2075      */
2076     public int[] getLogicalRangesForVisualSelection(TextHitInfo firstEndpoint,
2077                                                     TextHitInfo secondEndpoint) {
2078         ensureCache();
2079 
2080         checkTextHit(firstEndpoint);
2081         checkTextHit(secondEndpoint);
2082 
2083         // !!! probably want to optimize for all LTR text
2084 
2085         boolean[] included = new boolean[characterCount];
2086 
2087         int startIndex = hitToCaret(firstEndpoint);
2088         int limitIndex = hitToCaret(secondEndpoint);
2089 
2090         if (startIndex > limitIndex) {
2091             int t = startIndex;


2124         }
2125 
2126         int[] ranges = new int[count * 2];
2127         count = 0;
2128         inrun = false;
2129         for (int i = 0; i < characterCount; i++) {
2130             if (included[i] != inrun) {
2131                 ranges[count++] = i;
2132                 inrun = !inrun;
2133             }
2134         }
2135         if (inrun) {
2136             ranges[count++] = characterCount;
2137         }
2138 
2139         return ranges;
2140     }
2141 
2142     /**
2143      * Returns a path enclosing the visual selection in the specified range,
2144      * extended to {@code bounds}.
2145      * <p>
2146      * If the selection includes the leftmost (topmost) position, the selection
2147      * is extended to the left (top) of {@code bounds}.  If the
2148      * selection includes the rightmost (bottommost) position, the selection
2149      * is extended to the right (bottom) of the bounds.  The height
2150      * (width on vertical lines) of the selection is always extended to
2151      * {@code bounds}.
2152      * <p>
2153      * Although the selection is always contiguous, the logically selected
2154      * text can be discontiguous on lines with mixed-direction text.  The
2155      * logical ranges of text selected can be retrieved using
2156      * {@code getLogicalRangesForVisualSelection}.  For example,
2157      * consider the text 'ABCdef' where capital letters indicate
2158      * right-to-left text, rendered on a right-to-left line, with a visual
2159      * selection from 0L (the leading edge of 'A') to 3T (the trailing edge
2160      * of 'd').  The text appears as follows, with bold underlined areas
2161      * representing the selection:
2162      * <br><pre>
2163      *    d<u><b>efCBA  </b></u>
2164      * </pre>
2165      * The logical selection ranges are 0-3, 4-6 (ABC, ef) because the
2166      * visually contiguous text is logically discontiguous.  Also note that
2167      * since the rightmost position on the layout (to the right of 'A') is
2168      * selected, the selection is extended to the right of the bounds.
2169      * @param firstEndpoint one end of the visual selection
2170      * @param secondEndpoint the other end of the visual selection
2171      * @param bounds the bounding rectangle to which to extend the selection.
2172      *     This is in baseline-relative coordinates.
2173      * @return a {@code Shape} enclosing the selection.  This is in
2174      *     standard coordinates.
2175      * @see #getLogicalRangesForVisualSelection(TextHitInfo, TextHitInfo)
2176      * @see #getLogicalHighlightShape(int, int, Rectangle2D)
2177      */
2178     public Shape getVisualHighlightShape(TextHitInfo firstEndpoint,
2179                                         TextHitInfo secondEndpoint,
2180                                         Rectangle2D bounds)
2181     {
2182         ensureCache();
2183 
2184         checkTextHit(firstEndpoint);
2185         checkTextHit(secondEndpoint);
2186 
2187         if(bounds == null) {
2188                 throw new IllegalArgumentException("Null Rectangle2D passed to TextLayout.getVisualHighlightShape()");
2189         }
2190 
2191         GeneralPath result = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
2192 
2193         int firstCaret = hitToCaret(firstEndpoint);


2201             if (!ls.getBounds().isEmpty())
2202                 result.append(ls, false);
2203         }
2204 
2205         if (firstCaret == characterCount || secondCaret == characterCount) {
2206             GeneralPath rs = rightShape(bounds);
2207             if (!rs.getBounds().isEmpty()) {
2208                 result.append(rs, false);
2209             }
2210         }
2211 
2212         LayoutPathImpl lp = textLine.getLayoutPath();
2213         if (lp != null) {
2214             result = (GeneralPath)lp.mapShape(result); // dlf cast safe?
2215         }
2216 
2217         return  result;
2218     }
2219 
2220     /**
2221      * Returns a {@code Shape} enclosing the visual selection in the
2222      * specified range, extended to the bounds.  This method is a
2223      * convenience overload of {@code getVisualHighlightShape} that
2224      * uses the natural bounds of this {@code TextLayout}.
2225      * @param firstEndpoint one end of the visual selection
2226      * @param secondEndpoint the other end of the visual selection
2227      * @return a {@code Shape} enclosing the selection.  This is
2228      *     in standard coordinates.
2229      */
2230     public Shape getVisualHighlightShape(TextHitInfo firstEndpoint,
2231                                              TextHitInfo secondEndpoint) {
2232         return getVisualHighlightShape(firstEndpoint, secondEndpoint, getNaturalBounds());
2233     }
2234 
2235     /**
2236      * Returns a {@code Shape} enclosing the logical selection in the
2237      * specified range, extended to the specified {@code bounds}.
2238      * <p>
2239      * If the selection range includes the first logical character, the
2240      * selection is extended to the portion of {@code bounds} before
2241      * the start of this {@code TextLayout}.  If the range includes
2242      * the last logical character, the selection is extended to the portion
2243      * of {@code bounds} after the end of this {@code TextLayout}.
2244      * The height (width on vertical lines) of the selection is always
2245      * extended to {@code bounds}.
2246      * <p>
2247      * The selection can be discontiguous on lines with mixed-direction text.
2248      * Only those characters in the logical range between start and limit
2249      * appear selected.  For example, consider the text 'ABCdef' where capital
2250      * letters indicate right-to-left text, rendered on a right-to-left line,
2251      * with a logical selection from 0 to 4 ('ABCd').  The text appears as
2252      * follows, with bold standing in for the selection, and underlining for
2253      * the extension:
2254      * <br><pre>
2255      *    <u><b>d</b></u>ef<u><b>CBA  </b></u>
2256      * </pre>
2257      * The selection is discontiguous because the selected characters are
2258      * visually discontiguous. Also note that since the range includes the
2259      * first logical character (A), the selection is extended to the portion
2260      * of the {@code bounds} before the start of the layout, which in
2261      * this case (a right-to-left line) is the right portion of the
2262      * {@code bounds}.
2263      * @param firstEndpoint an endpoint in the range of characters to select
2264      * @param secondEndpoint the other endpoint of the range of characters
2265      * to select. Can be less than {@code firstEndpoint}.  The range
2266      * includes the character at min(firstEndpoint, secondEndpoint), but
2267      * excludes max(firstEndpoint, secondEndpoint).
2268      * @param bounds the bounding rectangle to which to extend the selection.
2269      *     This is in baseline-relative coordinates.
2270      * @return an area enclosing the selection.  This is in standard
2271      *     coordinates.
2272      * @see #getVisualHighlightShape(TextHitInfo, TextHitInfo, Rectangle2D)
2273      */
2274     public Shape getLogicalHighlightShape(int firstEndpoint,
2275                                          int secondEndpoint,
2276                                          Rectangle2D bounds) {
2277         if (bounds == null) {
2278             throw new IllegalArgumentException("Null Rectangle2D passed to TextLayout.getLogicalHighlightShape()");
2279         }
2280 
2281         ensureCache();
2282 
2283         if (firstEndpoint > secondEndpoint) {
2284             int t = firstEndpoint;
2285             firstEndpoint = secondEndpoint;


2337             }
2338 
2339             if ((textLine.isDirectionLTR() && secondEndpoint == characterCount) ||
2340                 (!textLine.isDirectionLTR() && firstEndpoint == 0)) {
2341 
2342                 GeneralPath rs = rightShape(bounds);
2343                 if (!rs.getBounds().isEmpty()) {
2344                     result.append(rs, false);
2345                 }
2346             }
2347         }
2348 
2349         LayoutPathImpl lp = textLine.getLayoutPath();
2350         if (lp != null) {
2351             result = (GeneralPath)lp.mapShape(result); // dlf cast safe?
2352         }
2353         return result;
2354     }
2355 
2356     /**
2357      * Returns a {@code Shape} enclosing the logical selection in the
2358      * specified range, extended to the natural bounds of this
2359      * {@code TextLayout}.  This method is a convenience overload of
2360      * {@code getLogicalHighlightShape} that uses the natural bounds of
2361      * this {@code TextLayout}.
2362      * @param firstEndpoint an endpoint in the range of characters to select
2363      * @param secondEndpoint the other endpoint of the range of characters
2364      * to select. Can be less than {@code firstEndpoint}.  The range
2365      * includes the character at min(firstEndpoint, secondEndpoint), but
2366      * excludes max(firstEndpoint, secondEndpoint).
2367      * @return a {@code Shape} enclosing the selection.  This is in
2368      *     standard coordinates.
2369      */
2370     public Shape getLogicalHighlightShape(int firstEndpoint, int secondEndpoint) {
2371 
2372         return getLogicalHighlightShape(firstEndpoint, secondEndpoint, getNaturalBounds());
2373     }
2374 
2375     /**
2376      * Returns the black box bounds of the characters in the specified range.
2377      * The black box bounds is an area consisting of the union of the bounding
2378      * boxes of all the glyphs corresponding to the characters between start
2379      * and limit.  This area can be disjoint.
2380      * @param firstEndpoint one end of the character range
2381      * @param secondEndpoint the other end of the character range.  Can be
2382      * less than {@code firstEndpoint}.
2383      * @return a {@code Shape} enclosing the black box bounds.  This is
2384      *     in standard coordinates.
2385      */
2386     public Shape getBlackBoxBounds(int firstEndpoint, int secondEndpoint) {
2387         ensureCache();
2388 
2389         if (firstEndpoint > secondEndpoint) {
2390             int t = firstEndpoint;
2391             firstEndpoint = secondEndpoint;
2392             secondEndpoint = t;
2393         }
2394 
2395         if (firstEndpoint < 0 || secondEndpoint > characterCount) {
2396             throw new IllegalArgumentException("Invalid range passed to TextLayout.getBlackBoxBounds()");
2397         }
2398 
2399         /*
2400          * return an area that consists of the bounding boxes of all the
2401          * characters from firstEndpoint to limit
2402          */
2403 


2413                     result.append(r, false);
2414                 }
2415             }
2416         }
2417 
2418         if (dx != 0 || dy != 0) {
2419             AffineTransform tx = AffineTransform.getTranslateInstance(dx, dy);
2420             result = (GeneralPath)tx.createTransformedShape(result);
2421         }
2422         LayoutPathImpl lp = textLine.getLayoutPath();
2423         if (lp != null) {
2424             result = (GeneralPath)lp.mapShape(result);
2425         }
2426 
2427         //return new Highlight(result, false);
2428         return result;
2429     }
2430 
2431     /**
2432      * Returns the distance from the point (x,&nbsp;y) to the caret along
2433      * the line direction defined in {@code caretInfo}.  Distance is
2434      * negative if the point is to the left of the caret on a horizontal
2435      * line, or above the caret on a vertical line.
2436      * Utility for use by hitTestChar.
2437      */
2438     private float caretToPointDistance(float[] caretInfo, float x, float y) {
2439         // distanceOffBaseline is negative if you're 'above' baseline
2440 
2441         float lineDistance = isVerticalLine? y : x;
2442         float distanceOffBaseline = isVerticalLine? -x : y;
2443 
2444         return lineDistance - caretInfo[0] +
2445             (distanceOffBaseline*caretInfo[1]);
2446     }
2447 
2448     /**
2449      * Returns a {@code TextHitInfo} corresponding to the
2450      * specified point.
2451      * Coordinates outside the bounds of the {@code TextLayout}
2452      * map to hits on the leading edge of the first logical character,
2453      * or the trailing edge of the last logical character, as appropriate,
2454      * regardless of the position of that character in the line.  Only the
2455      * direction along the baseline is used to make this evaluation.
2456      * @param x the x offset from the origin of this
2457      *     {@code TextLayout}.  This is in standard coordinates.
2458      * @param y the y offset from the origin of this
2459      *     {@code TextLayout}.  This is in standard coordinates.
2460      * @param bounds the bounds of the {@code TextLayout}.  This
2461      *     is in baseline-relative coordinates.
2462      * @return a hit describing the character and edge (leading or trailing)
2463      *     under the specified point.
2464      */
2465     public TextHitInfo hitTestChar(float x, float y, Rectangle2D bounds) {
2466         // check boundary conditions
2467 
2468         LayoutPathImpl lp = textLine.getLayoutPath();
2469         boolean prev = false;
2470         if (lp != null) {
2471             Point2D.Float pt = new Point2D.Float(x, y);
2472             prev = lp.pointToPath(pt, pt);
2473             x = pt.x;
2474             y = pt.y;
2475         }
2476 
2477         if (isVertical()) {
2478             if (y < bounds.getMinY()) {
2479                 return TextHitInfo.leading(0);
2480             } else if (y >= bounds.getMaxY()) {


2535             // proximity in x (along baseline) is two times as important as proximity in y
2536             double nd = Math.sqrt(4*(cx - x)*(cx - x) + ydsq);
2537             if (nd < distance) {
2538                 distance = nd;
2539                 index = i;
2540                 trail = -1;
2541                 icx = cx; icy = cy; ia = cm.italicAngle;
2542             }
2543         }
2544         boolean left = x < icx - (y - icy) * ia;
2545         boolean leading = textLine.isCharLTR(index) == left;
2546         if (trail == -1) {
2547             trail = characterCount;
2548         }
2549         TextHitInfo result = leading ? TextHitInfo.leading(index) :
2550             TextHitInfo.trailing(trail-1);
2551         return result;
2552     }
2553 
2554     /**
2555      * Returns a {@code TextHitInfo} corresponding to the
2556      * specified point.  This method is a convenience overload of
2557      * {@code hitTestChar} that uses the natural bounds of this
2558      * {@code TextLayout}.
2559      * @param x the x offset from the origin of this
2560      *     {@code TextLayout}.  This is in standard coordinates.
2561      * @param y the y offset from the origin of this
2562      *     {@code TextLayout}.  This is in standard coordinates.
2563      * @return a hit describing the character and edge (leading or trailing)
2564      * under the specified point.
2565      */
2566     public TextHitInfo hitTestChar(float x, float y) {
2567 
2568         return hitTestChar(x, y, getNaturalBounds());
2569     }
2570 
2571     /**
2572      * Returns the hash code of this {@code TextLayout}.
2573      * @return the hash code of this {@code TextLayout}.
2574      */
2575     public int hashCode() {
2576         if (hashCodeCache == 0) {
2577             ensureCache();
2578             hashCodeCache = textLine.hashCode();
2579         }
2580         return hashCodeCache;
2581     }
2582 
2583     /**
2584      * Returns {@code true} if the specified {@code Object} is a
2585      * {@code TextLayout} object and if the specified {@code Object}
2586      * equals this {@code TextLayout}.
2587      * @param obj an {@code Object} to test for equality
2588      * @return {@code true} if the specified {@code Object}
2589      *      equals this {@code TextLayout}; {@code false}
2590      *      otherwise.
2591      */
2592     public boolean equals(Object obj) {
2593         return (obj instanceof TextLayout) && equals((TextLayout)obj);
2594     }
2595 
2596     /**
2597      * Returns {@code true} if the two layouts are equal.
2598      * Two layouts are equal if they contain equal glyphvectors in the same order.
2599      * @param rhs the {@code TextLayout} to compare to this
2600      *       {@code TextLayout}
2601      * @return {@code true} if the specified {@code TextLayout}
2602      *      equals this {@code TextLayout}.
2603      *
2604      */
2605     public boolean equals(TextLayout rhs) {
2606 
2607         if (rhs == null) {
2608             return false;
2609         }
2610         if (rhs == this) {
2611             return true;
2612         }
2613 
2614         ensureCache();
2615         return textLine.equals(rhs.textLine);
2616     }
2617 
2618     /**
2619      * Returns debugging information for this {@code TextLayout}.
2620      * @return the {@code textLine} of this {@code TextLayout}
2621      *        as a {@code String}.
2622      */
2623     public String toString() {
2624         ensureCache();
2625         return textLine.toString();
2626      }
2627 
2628     /**
2629      * Renders this {@code TextLayout} at the specified location in
2630      * the specified {@link java.awt.Graphics2D Graphics2D} context.
2631      * The origin of the layout is placed at x,&nbsp;y.  Rendering may touch
2632      * any point within {@code getBounds()} of this position.  This
2633      * leaves the {@code g2} unchanged.  Text is rendered along the
2634      * baseline path.
2635      * @param g2 the {@code Graphics2D} context into which to render
2636      *         the layout
2637      * @param x the X coordinate of the origin of this {@code TextLayout}
2638      * @param y the Y coordinate of the origin of this {@code TextLayout}
2639      * @see #getBounds()
2640      */
2641     public void draw(Graphics2D g2, float x, float y) {
2642 
2643         if (g2 == null) {
2644             throw new IllegalArgumentException("Null Graphics2D passed to TextLayout.draw()");
2645         }
2646 
2647         textLine.draw(g2, x - dx, y - dy);
2648     }
2649 
2650     /**
2651      * Package-only method for testing ONLY.  Please don't abuse.
2652      */
2653     TextLine getTextLineForTesting() {
2654 
2655         return textLine;
2656     }
2657 
2658     /**


2672         }
2673         return start;
2674         */
2675     }
2676 
2677     static byte getBaselineFromGraphic(GraphicAttribute graphic) {
2678 
2679         byte alignment = (byte) graphic.getAlignment();
2680 
2681         if (alignment == GraphicAttribute.BOTTOM_ALIGNMENT ||
2682                 alignment == GraphicAttribute.TOP_ALIGNMENT) {
2683 
2684             return (byte)GraphicAttribute.ROMAN_BASELINE;
2685         }
2686         else {
2687             return alignment;
2688         }
2689     }
2690 
2691     /**
2692      * Returns a {@code Shape} representing the outline of this
2693      * {@code TextLayout}.
2694      * @param tx an optional {@link AffineTransform} to apply to the
2695      *     outline of this {@code TextLayout}.
2696      * @return a {@code Shape} that is the outline of this
2697      *     {@code TextLayout}.  This is in standard coordinates.
2698      */
2699     public Shape getOutline(AffineTransform tx) {
2700         ensureCache();
2701         Shape result = textLine.getOutline(tx);
2702         LayoutPathImpl lp = textLine.getLayoutPath();
2703         if (lp != null) {
2704             result = lp.mapShape(result);
2705         }
2706         return result;
2707     }
2708 
2709     /**
2710      * Return the LayoutPath, or null if the layout path is the
2711      * default path (x maps to advance, y maps to offset).
2712      * @return the layout path
2713      * @since 1.6
2714      */
2715     public LayoutPath getLayoutPath() {
2716         return textLine.getLayoutPath();
2717     }


< prev index next >