< prev index next >

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

Print this page




  44 
  45 import java.text.AttributedCharacterIterator;
  46 import java.text.AttributedCharacterIterator.Attribute;
  47 import java.text.AttributedString;
  48 import java.text.Bidi;
  49 import java.text.BreakIterator;
  50 import java.text.CharacterIterator;
  51 
  52 import java.awt.font.FontRenderContext;
  53 
  54 import java.util.Hashtable;
  55 import java.util.Map;
  56 
  57 import sun.font.AttributeValues;
  58 import sun.font.BidiUtils;
  59 import sun.font.TextLineComponent;
  60 import sun.font.TextLabelFactory;
  61 import sun.font.FontResolver;
  62 
  63 /**
  64  * The <code>TextMeasurer</code> class provides the primitive operations
  65  * needed for line break: measuring up to a given advance, determining the
  66  * advance of a range of characters, and generating a
  67  * <code>TextLayout</code> for a range of characters. It also provides
  68  * methods for incremental editing of paragraphs.
  69  * <p>
  70  * A <code>TextMeasurer</code> object is constructed with an
  71  * {@link java.text.AttributedCharacterIterator AttributedCharacterIterator}
  72  * representing a single paragraph of text.  The value returned by the
  73  * {@link AttributedCharacterIterator#getBeginIndex() getBeginIndex}
  74  * method of <code>AttributedCharacterIterator</code>
  75  * defines the absolute index of the first character.  The value
  76  * returned by the
  77  * {@link AttributedCharacterIterator#getEndIndex() getEndIndex}
  78  * method of <code>AttributedCharacterIterator</code> defines the index
  79  * past the last character.  These values define the range of indexes to
  80  * use in calls to the <code>TextMeasurer</code>.  For example, calls to
  81  * get the advance of a range of text or the line break of a range of text
  82  * must use indexes between the beginning and end index values.  Calls to
  83  * {@link #insertChar(java.text.AttributedCharacterIterator, int) insertChar}
  84  * and
  85  * {@link #deleteChar(java.text.AttributedCharacterIterator, int) deleteChar}
  86  * reset the <code>TextMeasurer</code> to use the beginning index and end
  87  * index of the <code>AttributedCharacterIterator</code> passed in those calls.
  88  * <p>
  89  * Most clients will use the more convenient <code>LineBreakMeasurer</code>,
  90  * which implements the standard line break policy (placing as many words
  91  * as will fit on each line).
  92  *
  93  * @author John Raley
  94  * @see LineBreakMeasurer
  95  * @since 1.3
  96  */
  97 
  98 public final class TextMeasurer implements Cloneable {
  99 
 100     // Number of lines to format to.
 101     private static float EST_LINES = (float) 2.1;
 102 
 103     /*
 104     static {
 105         String s = System.getProperty("estLines");
 106         if (s != null) {
 107             try {
 108                 Float f = new Float(s);
 109                 EST_LINES = f.floatValue();


 139     private int fComponentLimit;
 140 
 141     private boolean haveLayoutWindow;
 142 
 143     // used to find valid starting points for line components
 144     private BreakIterator fLineBreak = null;
 145     private CharArrayIterator charIter = null;
 146     int layoutCount = 0;
 147     int layoutCharCount = 0;
 148 
 149     // paragraph, with resolved fonts and styles
 150     private StyledParagraph fParagraph;
 151 
 152     // paragraph data - same across all layouts
 153     private boolean fIsDirectionLTR;
 154     private byte fBaseline;
 155     private float[] fBaselineOffsets;
 156     private float fJustifyRatio = 1;
 157 
 158     /**
 159      * Constructs a <code>TextMeasurer</code> from the source text.
 160      * The source text should be a single entire paragraph.
 161      * @param text the source paragraph.  Cannot be null.
 162      * @param frc the information about a graphics device which is needed
 163      *       to measure the text correctly.  Cannot be null.
 164      */
 165     public TextMeasurer(AttributedCharacterIterator text, FontRenderContext frc) {
 166 
 167         fFrc = frc;
 168         initAll(text);
 169     }
 170 
 171     protected Object clone() {
 172         TextMeasurer other;
 173         try {
 174             other = (TextMeasurer) super.clone();
 175         }
 176         catch(CloneNotSupportedException e) {
 177             throw new Error();
 178         }
 179         if (fComponents != null) {


 523             }
 524             fLineBreak.setText(charIter);
 525             if (localStart > 0) {
 526                 if (!fLineBreak.isBoundary(localStart)) {
 527                     compStart = fLineBreak.preceding(localStart);
 528                 }
 529             }
 530             if (compLimit < fChars.length) {
 531                 if (!fLineBreak.isBoundary(compLimit)) {
 532                     compLimit = fLineBreak.following(compLimit);
 533                 }
 534             }
 535         }
 536 
 537         ensureComponents(compStart, compLimit);
 538         haveLayoutWindow = true;
 539     }
 540 
 541     /**
 542      * Returns the index of the first character which will not fit on
 543      * on a line beginning at <code>start</code> and possible
 544      * measuring up to <code>maxAdvance</code> in graphical width.
 545      *
 546      * @param start the character index at which to start measuring.
 547      *  <code>start</code> is an absolute index, not relative to the
 548      *  start of the paragraph
 549      * @param maxAdvance the graphical width in which the line must fit
 550      * @return the index after the last character that will fit
 551      *  on a line beginning at <code>start</code>, which is not longer
 552      *  than <code>maxAdvance</code> in graphical width
 553      * @throws IllegalArgumentException if <code>start</code> is
 554      *          less than the beginning of the paragraph.
 555      */
 556     public int getLineBreakIndex(int start, float maxAdvance) {
 557 
 558         int localStart = start - fStart;
 559 
 560         if (!haveLayoutWindow ||
 561                 localStart < fComponentStart ||
 562                 localStart >= fComponentLimit) {
 563             makeLayoutWindow(localStart);
 564         }
 565 
 566         return calcLineBreak(localStart, maxAdvance) + fStart;
 567     }
 568 
 569     /**
 570      * Returns the graphical width of a line beginning at <code>start</code>
 571      * and including characters up to <code>limit</code>.
 572      * <code>start</code> and <code>limit</code> are absolute indices,
 573      * not relative to the start of the paragraph.
 574      *
 575      * @param start the character index at which to start measuring
 576      * @param limit the character index at which to stop measuring
 577      * @return the graphical width of a line beginning at <code>start</code>
 578      *   and including characters up to <code>limit</code>
 579      * @throws IndexOutOfBoundsException if <code>limit</code> is less
 580      *         than <code>start</code>
 581      * @throws IllegalArgumentException if <code>start</code> or
 582      *          <code>limit</code> is not between the beginning of
 583      *          the paragraph and the end of the paragraph.
 584      */
 585     public float getAdvanceBetween(int start, int limit) {
 586 
 587         int localStart = start - fStart;
 588         int localLimit = limit - fStart;
 589 
 590         ensureComponents(localStart, localLimit);
 591         TextLine line = makeTextLineOnRange(localStart, localLimit);
 592         return line.getMetrics().advance;
 593         // could cache line in case getLayout is called with same start, limit
 594     }
 595 
 596     /**
 597      * Returns a <code>TextLayout</code> on the given character range.
 598      *
 599      * @param start the index of the first character
 600      * @param limit the index after the last character.  Must be greater
 601      *   than <code>start</code>
 602      * @return a <code>TextLayout</code> for the characters beginning at
 603      *  <code>start</code> up to (but not including) <code>limit</code>
 604      * @throws IndexOutOfBoundsException if <code>limit</code> is less
 605      *         than <code>start</code>
 606      * @throws IllegalArgumentException if <code>start</code> or
 607      *          <code>limit</code> is not between the beginning of
 608      *          the paragraph and the end of the paragraph.
 609      */
 610     public TextLayout getLayout(int start, int limit) {
 611 
 612         int localStart = start - fStart;
 613         int localLimit = limit - fStart;
 614 
 615         ensureComponents(localStart, localLimit);
 616         TextLine textLine = makeTextLineOnRange(localStart, localLimit);
 617 
 618         if (localLimit < fChars.length) {
 619             layoutCharCount += limit-start;
 620             layoutCount++;
 621         }
 622 
 623         return new TextLayout(textLine,
 624                               fBaseline,
 625                               fBaselineOffsets,
 626                               fJustifyRatio);
 627     }
 628 
 629     private int formattedChars = 0;
 630     private static boolean wantStats = false;/*"true".equals(System.getProperty("collectStats"));*/
 631     private boolean collectStats = false;
 632 
 633     private void printStats() {
 634         System.out.println("formattedChars: " + formattedChars);
 635         //formattedChars = 0;
 636         collectStats = false;
 637     }
 638 
 639     /**
 640      * Updates the <code>TextMeasurer</code> after a single character has
 641      * been inserted
 642      * into the paragraph currently represented by this
 643      * <code>TextMeasurer</code>.  After this call, this
 644      * <code>TextMeasurer</code> is equivalent to a new
 645      * <code>TextMeasurer</code> created from the text;  however, it will
 646      * usually be more efficient to update an existing
 647      * <code>TextMeasurer</code> than to create a new one from scratch.
 648      *
 649      * @param newParagraph the text of the paragraph after performing
 650      * the insertion.  Cannot be null.
 651      * @param insertPos the position in the text where the character was
 652      * inserted.  Must not be less than the start of
 653      * <code>newParagraph</code>, and must be less than the end of
 654      * <code>newParagraph</code>.
 655      * @throws IndexOutOfBoundsException if <code>insertPos</code> is less
 656      *         than the start of <code>newParagraph</code> or greater than
 657      *         or equal to the end of <code>newParagraph</code>
 658      * @throws NullPointerException if <code>newParagraph</code> is
 659      *         <code>null</code>
 660      */
 661     public void insertChar(AttributedCharacterIterator newParagraph, int insertPos) {
 662 
 663         if (collectStats) {
 664             printStats();
 665         }
 666         if (wantStats) {
 667             collectStats = true;
 668         }
 669 
 670         fStart = newParagraph.getBeginIndex();
 671         int end = newParagraph.getEndIndex();
 672         if (end - fStart != fChars.length+1) {
 673             initAll(newParagraph);
 674         }
 675 
 676         char[] newChars = new char[end-fStart];
 677         int newCharIndex = insertPos - fStart;
 678         System.arraycopy(fChars, 0, newChars, 0, newCharIndex);
 679 


 686                          end-insertPos-1);
 687         fChars = newChars;
 688 
 689         if (fBidi != null || Bidi.requiresBidi(newChars, newCharIndex, newCharIndex + 1) ||
 690                 newParagraph.getAttribute(TextAttribute.BIDI_EMBEDDING) != null) {
 691 
 692             fBidi = new Bidi(newParagraph);
 693             if (fBidi.isLeftToRight()) {
 694                 fBidi = null;
 695             }
 696         }
 697 
 698         fParagraph = StyledParagraph.insertChar(newParagraph,
 699                                                 fChars,
 700                                                 insertPos,
 701                                                 fParagraph);
 702         invalidateComponents();
 703     }
 704 
 705     /**
 706      * Updates the <code>TextMeasurer</code> after a single character has
 707      * been deleted
 708      * from the paragraph currently represented by this
 709      * <code>TextMeasurer</code>.  After this call, this
 710      * <code>TextMeasurer</code> is equivalent to a new <code>TextMeasurer</code>
 711      * created from the text;  however, it will usually be more efficient
 712      * to update an existing <code>TextMeasurer</code> than to create a new one
 713      * from scratch.
 714      *
 715      * @param newParagraph the text of the paragraph after performing
 716      * the deletion.  Cannot be null.
 717      * @param deletePos the position in the text where the character was removed.
 718      * Must not be less than
 719      * the start of <code>newParagraph</code>, and must not be greater than the
 720      * end of <code>newParagraph</code>.
 721      * @throws IndexOutOfBoundsException if <code>deletePos</code> is
 722      *         less than the start of <code>newParagraph</code> or greater
 723      *         than the end of <code>newParagraph</code>
 724      * @throws NullPointerException if <code>newParagraph</code> is
 725      *         <code>null</code>
 726      */
 727     public void deleteChar(AttributedCharacterIterator newParagraph, int deletePos) {
 728 
 729         fStart = newParagraph.getBeginIndex();
 730         int end = newParagraph.getEndIndex();
 731         if (end - fStart != fChars.length-1) {
 732             initAll(newParagraph);
 733         }
 734 
 735         char[] newChars = new char[end-fStart];
 736         int changedIndex = deletePos-fStart;
 737 
 738         System.arraycopy(fChars, 0, newChars, 0, deletePos-fStart);
 739         System.arraycopy(fChars, changedIndex+1, newChars, changedIndex, end-deletePos);
 740         fChars = newChars;
 741 
 742         if (fBidi != null) {
 743             fBidi = new Bidi(newParagraph);
 744             if (fBidi.isLeftToRight()) {
 745                 fBidi = null;


  44 
  45 import java.text.AttributedCharacterIterator;
  46 import java.text.AttributedCharacterIterator.Attribute;
  47 import java.text.AttributedString;
  48 import java.text.Bidi;
  49 import java.text.BreakIterator;
  50 import java.text.CharacterIterator;
  51 
  52 import java.awt.font.FontRenderContext;
  53 
  54 import java.util.Hashtable;
  55 import java.util.Map;
  56 
  57 import sun.font.AttributeValues;
  58 import sun.font.BidiUtils;
  59 import sun.font.TextLineComponent;
  60 import sun.font.TextLabelFactory;
  61 import sun.font.FontResolver;
  62 
  63 /**
  64  * The {@code TextMeasurer} class provides the primitive operations
  65  * needed for line break: measuring up to a given advance, determining the
  66  * advance of a range of characters, and generating a
  67  * {@code TextLayout} for a range of characters. It also provides
  68  * methods for incremental editing of paragraphs.
  69  * <p>
  70  * A {@code TextMeasurer} object is constructed with an
  71  * {@link java.text.AttributedCharacterIterator AttributedCharacterIterator}
  72  * representing a single paragraph of text.  The value returned by the
  73  * {@link AttributedCharacterIterator#getBeginIndex() getBeginIndex}
  74  * method of {@code AttributedCharacterIterator}
  75  * defines the absolute index of the first character.  The value
  76  * returned by the
  77  * {@link AttributedCharacterIterator#getEndIndex() getEndIndex}
  78  * method of {@code AttributedCharacterIterator} defines the index
  79  * past the last character.  These values define the range of indexes to
  80  * use in calls to the {@code TextMeasurer}.  For example, calls to
  81  * get the advance of a range of text or the line break of a range of text
  82  * must use indexes between the beginning and end index values.  Calls to
  83  * {@link #insertChar(java.text.AttributedCharacterIterator, int) insertChar}
  84  * and
  85  * {@link #deleteChar(java.text.AttributedCharacterIterator, int) deleteChar}
  86  * reset the {@code TextMeasurer} to use the beginning index and end
  87  * index of the {@code AttributedCharacterIterator} passed in those calls.
  88  * <p>
  89  * Most clients will use the more convenient {@code LineBreakMeasurer},
  90  * which implements the standard line break policy (placing as many words
  91  * as will fit on each line).
  92  *
  93  * @author John Raley
  94  * @see LineBreakMeasurer
  95  * @since 1.3
  96  */
  97 
  98 public final class TextMeasurer implements Cloneable {
  99 
 100     // Number of lines to format to.
 101     private static float EST_LINES = (float) 2.1;
 102 
 103     /*
 104     static {
 105         String s = System.getProperty("estLines");
 106         if (s != null) {
 107             try {
 108                 Float f = new Float(s);
 109                 EST_LINES = f.floatValue();


 139     private int fComponentLimit;
 140 
 141     private boolean haveLayoutWindow;
 142 
 143     // used to find valid starting points for line components
 144     private BreakIterator fLineBreak = null;
 145     private CharArrayIterator charIter = null;
 146     int layoutCount = 0;
 147     int layoutCharCount = 0;
 148 
 149     // paragraph, with resolved fonts and styles
 150     private StyledParagraph fParagraph;
 151 
 152     // paragraph data - same across all layouts
 153     private boolean fIsDirectionLTR;
 154     private byte fBaseline;
 155     private float[] fBaselineOffsets;
 156     private float fJustifyRatio = 1;
 157 
 158     /**
 159      * Constructs a {@code TextMeasurer} from the source text.
 160      * The source text should be a single entire paragraph.
 161      * @param text the source paragraph.  Cannot be null.
 162      * @param frc the information about a graphics device which is needed
 163      *       to measure the text correctly.  Cannot be null.
 164      */
 165     public TextMeasurer(AttributedCharacterIterator text, FontRenderContext frc) {
 166 
 167         fFrc = frc;
 168         initAll(text);
 169     }
 170 
 171     protected Object clone() {
 172         TextMeasurer other;
 173         try {
 174             other = (TextMeasurer) super.clone();
 175         }
 176         catch(CloneNotSupportedException e) {
 177             throw new Error();
 178         }
 179         if (fComponents != null) {


 523             }
 524             fLineBreak.setText(charIter);
 525             if (localStart > 0) {
 526                 if (!fLineBreak.isBoundary(localStart)) {
 527                     compStart = fLineBreak.preceding(localStart);
 528                 }
 529             }
 530             if (compLimit < fChars.length) {
 531                 if (!fLineBreak.isBoundary(compLimit)) {
 532                     compLimit = fLineBreak.following(compLimit);
 533                 }
 534             }
 535         }
 536 
 537         ensureComponents(compStart, compLimit);
 538         haveLayoutWindow = true;
 539     }
 540 
 541     /**
 542      * Returns the index of the first character which will not fit on
 543      * on a line beginning at {@code start} and possible
 544      * measuring up to {@code maxAdvance} in graphical width.
 545      *
 546      * @param start the character index at which to start measuring.
 547      *  {@code start} is an absolute index, not relative to the
 548      *  start of the paragraph
 549      * @param maxAdvance the graphical width in which the line must fit
 550      * @return the index after the last character that will fit
 551      *  on a line beginning at {@code start}, which is not longer
 552      *  than {@code maxAdvance} in graphical width
 553      * @throws IllegalArgumentException if {@code start} is
 554      *          less than the beginning of the paragraph.
 555      */
 556     public int getLineBreakIndex(int start, float maxAdvance) {
 557 
 558         int localStart = start - fStart;
 559 
 560         if (!haveLayoutWindow ||
 561                 localStart < fComponentStart ||
 562                 localStart >= fComponentLimit) {
 563             makeLayoutWindow(localStart);
 564         }
 565 
 566         return calcLineBreak(localStart, maxAdvance) + fStart;
 567     }
 568 
 569     /**
 570      * Returns the graphical width of a line beginning at {@code start}
 571      * and including characters up to {@code limit}.
 572      * {@code start} and {@code limit} are absolute indices,
 573      * not relative to the start of the paragraph.
 574      *
 575      * @param start the character index at which to start measuring
 576      * @param limit the character index at which to stop measuring
 577      * @return the graphical width of a line beginning at {@code start}
 578      *   and including characters up to {@code limit}
 579      * @throws IndexOutOfBoundsException if {@code limit} is less
 580      *         than {@code start}
 581      * @throws IllegalArgumentException if {@code start} or
 582      *          {@code limit} is not between the beginning of
 583      *          the paragraph and the end of the paragraph.
 584      */
 585     public float getAdvanceBetween(int start, int limit) {
 586 
 587         int localStart = start - fStart;
 588         int localLimit = limit - fStart;
 589 
 590         ensureComponents(localStart, localLimit);
 591         TextLine line = makeTextLineOnRange(localStart, localLimit);
 592         return line.getMetrics().advance;
 593         // could cache line in case getLayout is called with same start, limit
 594     }
 595 
 596     /**
 597      * Returns a {@code TextLayout} on the given character range.
 598      *
 599      * @param start the index of the first character
 600      * @param limit the index after the last character.  Must be greater
 601      *   than {@code start}
 602      * @return a {@code TextLayout} for the characters beginning at
 603      *  {@code start} up to (but not including) {@code limit}
 604      * @throws IndexOutOfBoundsException if {@code limit} is less
 605      *         than {@code start}
 606      * @throws IllegalArgumentException if {@code start} or
 607      *          {@code limit} is not between the beginning of
 608      *          the paragraph and the end of the paragraph.
 609      */
 610     public TextLayout getLayout(int start, int limit) {
 611 
 612         int localStart = start - fStart;
 613         int localLimit = limit - fStart;
 614 
 615         ensureComponents(localStart, localLimit);
 616         TextLine textLine = makeTextLineOnRange(localStart, localLimit);
 617 
 618         if (localLimit < fChars.length) {
 619             layoutCharCount += limit-start;
 620             layoutCount++;
 621         }
 622 
 623         return new TextLayout(textLine,
 624                               fBaseline,
 625                               fBaselineOffsets,
 626                               fJustifyRatio);
 627     }
 628 
 629     private int formattedChars = 0;
 630     private static boolean wantStats = false;/*"true".equals(System.getProperty("collectStats"));*/
 631     private boolean collectStats = false;
 632 
 633     private void printStats() {
 634         System.out.println("formattedChars: " + formattedChars);
 635         //formattedChars = 0;
 636         collectStats = false;
 637     }
 638 
 639     /**
 640      * Updates the {@code TextMeasurer} after a single character has
 641      * been inserted
 642      * into the paragraph currently represented by this
 643      * {@code TextMeasurer}.  After this call, this
 644      * {@code TextMeasurer} is equivalent to a new
 645      * {@code TextMeasurer} created from the text;  however, it will
 646      * usually be more efficient to update an existing
 647      * {@code TextMeasurer} than to create a new one from scratch.
 648      *
 649      * @param newParagraph the text of the paragraph after performing
 650      * the insertion.  Cannot be null.
 651      * @param insertPos the position in the text where the character was
 652      * inserted.  Must not be less than the start of
 653      * {@code newParagraph}, and must be less than the end of
 654      * {@code newParagraph}.
 655      * @throws IndexOutOfBoundsException if {@code insertPos} is less
 656      *         than the start of {@code newParagraph} or greater than
 657      *         or equal to the end of {@code newParagraph}
 658      * @throws NullPointerException if {@code newParagraph} is
 659      *         {@code null}
 660      */
 661     public void insertChar(AttributedCharacterIterator newParagraph, int insertPos) {
 662 
 663         if (collectStats) {
 664             printStats();
 665         }
 666         if (wantStats) {
 667             collectStats = true;
 668         }
 669 
 670         fStart = newParagraph.getBeginIndex();
 671         int end = newParagraph.getEndIndex();
 672         if (end - fStart != fChars.length+1) {
 673             initAll(newParagraph);
 674         }
 675 
 676         char[] newChars = new char[end-fStart];
 677         int newCharIndex = insertPos - fStart;
 678         System.arraycopy(fChars, 0, newChars, 0, newCharIndex);
 679 


 686                          end-insertPos-1);
 687         fChars = newChars;
 688 
 689         if (fBidi != null || Bidi.requiresBidi(newChars, newCharIndex, newCharIndex + 1) ||
 690                 newParagraph.getAttribute(TextAttribute.BIDI_EMBEDDING) != null) {
 691 
 692             fBidi = new Bidi(newParagraph);
 693             if (fBidi.isLeftToRight()) {
 694                 fBidi = null;
 695             }
 696         }
 697 
 698         fParagraph = StyledParagraph.insertChar(newParagraph,
 699                                                 fChars,
 700                                                 insertPos,
 701                                                 fParagraph);
 702         invalidateComponents();
 703     }
 704 
 705     /**
 706      * Updates the {@code TextMeasurer} after a single character has
 707      * been deleted
 708      * from the paragraph currently represented by this
 709      * {@code TextMeasurer}.  After this call, this
 710      * {@code TextMeasurer} is equivalent to a new {@code TextMeasurer}
 711      * created from the text;  however, it will usually be more efficient
 712      * to update an existing {@code TextMeasurer} than to create a new one
 713      * from scratch.
 714      *
 715      * @param newParagraph the text of the paragraph after performing
 716      * the deletion.  Cannot be null.
 717      * @param deletePos the position in the text where the character was removed.
 718      * Must not be less than
 719      * the start of {@code newParagraph}, and must not be greater than the
 720      * end of {@code newParagraph}.
 721      * @throws IndexOutOfBoundsException if {@code deletePos} is
 722      *         less than the start of {@code newParagraph} or greater
 723      *         than the end of {@code newParagraph}
 724      * @throws NullPointerException if {@code newParagraph} is
 725      *         {@code null}
 726      */
 727     public void deleteChar(AttributedCharacterIterator newParagraph, int deletePos) {
 728 
 729         fStart = newParagraph.getBeginIndex();
 730         int end = newParagraph.getEndIndex();
 731         if (end - fStart != fChars.length-1) {
 732             initAll(newParagraph);
 733         }
 734 
 735         char[] newChars = new char[end-fStart];
 736         int changedIndex = deletePos-fStart;
 737 
 738         System.arraycopy(fChars, 0, newChars, 0, deletePos-fStart);
 739         System.arraycopy(fChars, changedIndex+1, newChars, changedIndex, end-deletePos);
 740         fChars = newChars;
 741 
 742         if (fBidi != null) {
 743             fBidi = new Bidi(newParagraph);
 744             if (fBidi.isLeftToRight()) {
 745                 fBidi = null;
< prev index next >