History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/java/awt/font/TextMeasurer.java (1 of 18352 files). Show all

Unified · Split

java/pkg/java/awt/font/TextMeasurer.java+759−0
@@ -0,0 +1,759 @@
1+/*
2+ * Copyright (c) 1997, 2005, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+/*
27+ * (C) Copyright Taligent, Inc. 1996 - 1997, All Rights Reserved
28+ * (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
29+ *
30+ * The original version of this source code and documentation is
31+ * copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
32+ * of IBM. These materials are provided under terms of a License
33+ * Agreement between Taligent and Sun. This technology is protected
34+ * by multiple US and International patents.
35+ *
36+ * This notice and attribution to Taligent may not be removed.
37+ * Taligent is a registered trademark of Taligent, Inc.
38+ *
39+ */
40+
41+package java.awt.font;
42+
43+import java.awt.Font;
44+
45+import java.text.AttributedCharacterIterator;
46+import java.text.AttributedString;
47+import java.text.Bidi;
48+import java.text.BreakIterator;
49+import java.text.CharacterIterator;
50+
51+import java.awt.font.FontRenderContext;
52+
53+import java.util.Hashtable;
54+import java.util.Map;
55+
56+import sun.font.AttributeValues;
57+import sun.font.BidiUtils;
58+import sun.font.TextLineComponent;
59+import sun.font.TextLabelFactory;
60+import sun.font.FontResolver;
61+
62+/**
63+ * The <code>TextMeasurer</code> class provides the primitive operations
64+ * needed for line break: measuring up to a given advance, determining the
65+ * advance of a range of characters, and generating a
66+ * <code>TextLayout</code> for a range of characters. It also provides
67+ * methods for incremental editing of paragraphs.
68+ * <p>
69+ * A <code>TextMeasurer</code> object is constructed with an
70+ * {@link java.text.AttributedCharacterIterator AttributedCharacterIterator}
71+ * representing a single paragraph of text. The value returned by the
72+ * {@link AttributedCharacterIterator#getBeginIndex() getBeginIndex}
73+ * method of <code>AttributedCharacterIterator</code>
74+ * defines the absolute index of the first character. The value
75+ * returned by the
76+ * {@link AttributedCharacterIterator#getEndIndex() getEndIndex}
77+ * method of <code>AttributedCharacterIterator</code> defines the index
78+ * past the last character. These values define the range of indexes to
79+ * use in calls to the <code>TextMeasurer</code>. For example, calls to
80+ * get the advance of a range of text or the line break of a range of text
81+ * must use indexes between the beginning and end index values. Calls to
82+ * {@link #insertChar(java.text.AttributedCharacterIterator, int) insertChar}
83+ * and
84+ * {@link #deleteChar(java.text.AttributedCharacterIterator, int) deleteChar}
85+ * reset the <code>TextMeasurer</code> to use the beginning index and end
86+ * index of the <code>AttributedCharacterIterator</code> passed in those calls.
87+ * <p>
88+ * Most clients will use the more convenient <code>LineBreakMeasurer</code>,
89+ * which implements the standard line break policy (placing as many words
90+ * as will fit on each line).
91+ *
92+ * @author John Raley
93+ * @see LineBreakMeasurer
94+ * @since 1.3
95+ */
96+
97+public final class TextMeasurer implements Cloneable {
98+
99+ // Number of lines to format to.
100+ private static float EST_LINES = (float) 2.1;
101+
102+ /*
103+ static {
104+ String s = System.getProperty("estLines");
105+ if (s != null) {
106+ try {
107+ Float f = new Float(s);
108+ EST_LINES = f.floatValue();
109+ }
110+ catch(NumberFormatException e) {
111+ }
112+ }
113+ //System.out.println("EST_LINES="+EST_LINES);
114+ }
115+ */
116+
117+ private FontRenderContext fFrc;
118+
119+ private int fStart;
120+
121+ // characters in source text
122+ private char[] fChars;
123+
124+ // Bidi for this paragraph
125+ private Bidi fBidi;
126+
127+ // Levels array for chars in this paragraph - needed to reorder
128+ // trailing counterdirectional whitespace
129+ private byte[] fLevels;
130+
131+ // line components in logical order
132+ private TextLineComponent[] fComponents;
133+
134+ // index where components begin
135+ private int fComponentStart;
136+
137+ // index where components end
138+ private int fComponentLimit;
139+
140+ private boolean haveLayoutWindow;
141+
142+ // used to find valid starting points for line components
143+ private BreakIterator fLineBreak = null;
144+ private CharArrayIterator charIter = null;
145+ int layoutCount = 0;
146+ int layoutCharCount = 0;
147+
148+ // paragraph, with resolved fonts and styles
149+ private StyledParagraph fParagraph;
150+
151+ // paragraph data - same across all layouts
152+ private boolean fIsDirectionLTR;
153+ private byte fBaseline;
154+ private float[] fBaselineOffsets;
155+ private float fJustifyRatio = 1;
156+
157+ /**
158+ * Constructs a <code>TextMeasurer</code> from the source text.
159+ * The source text should be a single entire paragraph.
160+ * @param text the source paragraph. Cannot be null.
161+ * @param frc the information about a graphics device which is needed
162+ * to measure the text correctly. Cannot be null.
163+ */
164+ public TextMeasurer(AttributedCharacterIterator text, FontRenderContext frc) {
165+
166+ fFrc = frc;
167+ initAll(text);
168+ }
169+
170+ protected Object clone() {
171+ TextMeasurer other;
172+ try {
173+ other = (TextMeasurer) super.clone();
174+ }
175+ catch(CloneNotSupportedException e) {
176+ throw new Error();
177+ }
178+ if (fComponents != null) {
179+ other.fComponents = (TextLineComponent[]) fComponents.clone();
180+ }
181+ return other;
182+ }
183+
184+ private void invalidateComponents() {
185+ fComponentStart = fComponentLimit = fChars.length;
186+ fComponents = null;
187+ haveLayoutWindow = false;
188+ }
189+
190+ /**
191+ * Initialize state, including fChars array, direction, and
192+ * fBidi.
193+ */
194+ private void initAll(AttributedCharacterIterator text) {
195+
196+ fStart = text.getBeginIndex();
197+
198+ // extract chars
199+ fChars = new char[text.getEndIndex() - fStart];
200+
201+ int n = 0;
202+ for (char c = text.first(); c != text.DONE; c = text.next()) {
203+ fChars[n++] = c;
204+ }
205+
206+ text.first();
207+
208+ fBidi = new Bidi(text);
209+ if (fBidi.isLeftToRight()) {
210+ fBidi = null;
211+ }
212+
213+ text.first();
214+ Map paragraphAttrs = text.getAttributes();
215+ NumericShaper shaper = AttributeValues.getNumericShaping(paragraphAttrs);
216+ if (shaper != null) {
217+ shaper.shape(fChars, 0, fChars.length);
218+ }
219+
220+ fParagraph = new StyledParagraph(text, fChars);
221+
222+ // set paragraph attributes
223+ {
224+ // If there's an embedded graphic at the start of the
225+ // paragraph, look for the first non-graphic character
226+ // and use it and its font to initialize the paragraph.
227+ // If not, use the first graphic to initialize.
228+ fJustifyRatio = AttributeValues.getJustification(paragraphAttrs);
229+
230+ boolean haveFont = TextLine.advanceToFirstFont(text);
231+
232+ if (haveFont) {
233+ Font defaultFont = TextLine.getFontAtCurrentPos(text);
234+ int charsStart = text.getIndex() - text.getBeginIndex();
235+ LineMetrics lm = defaultFont.getLineMetrics(fChars, charsStart, charsStart+1, fFrc);
236+ fBaseline = (byte) lm.getBaselineIndex();
237+ fBaselineOffsets = lm.getBaselineOffsets();
238+ }
239+ else {
240+ // hmmm what to do here? Just try to supply reasonable
241+ // values I guess.
242+
243+ GraphicAttribute graphic = (GraphicAttribute)
244+ paragraphAttrs.get(TextAttribute.CHAR_REPLACEMENT);
245+ fBaseline = TextLayout.getBaselineFromGraphic(graphic);
246+ Font dummyFont = new Font(new Hashtable(5, (float)0.9));
247+ LineMetrics lm = dummyFont.getLineMetrics(" ", 0, 1, fFrc);
248+ fBaselineOffsets = lm.getBaselineOffsets();
249+ }
250+ fBaselineOffsets = TextLine.getNormalizedOffsets(fBaselineOffsets, fBaseline);
251+ }
252+
253+ invalidateComponents();
254+ }
255+
256+ /**
257+ * Generate components for the paragraph. fChars, fBidi should have been
258+ * initialized already.
259+ */
260+ private void generateComponents(int startingAt, int endingAt) {
261+
262+ if (collectStats) {
263+ formattedChars += (endingAt-startingAt);
264+ }
265+ int layoutFlags = 0; // no extra info yet, bidi determines run and line direction
266+ TextLabelFactory factory = new TextLabelFactory(fFrc, fChars, fBidi, layoutFlags);
267+
268+ int[] charsLtoV = null;
269+
270+ if (fBidi != null) {
271+ fLevels = BidiUtils.getLevels(fBidi);
272+ int[] charsVtoL = BidiUtils.createVisualToLogicalMap(fLevels);
273+ charsLtoV = BidiUtils.createInverseMap(charsVtoL);
274+ fIsDirectionLTR = fBidi.baseIsLeftToRight();
275+ }
276+ else {
277+ fLevels = null;
278+ fIsDirectionLTR = true;
279+ }
280+
281+ try {
282+ fComponents = TextLine.getComponents(
283+ fParagraph, fChars, startingAt, endingAt, charsLtoV, fLevels, factory);
284+ }
285+ catch(IllegalArgumentException e) {
286+ System.out.println("startingAt="+startingAt+"; endingAt="+endingAt);
287+ System.out.println("fComponentLimit="+fComponentLimit);
288+ throw e;
289+ }
290+
291+ fComponentStart = startingAt;
292+ fComponentLimit = endingAt;
293+ //debugFormatCount += (endingAt-startingAt);
294+ }
295+
296+ private int calcLineBreak(final int pos, final float maxAdvance) {
297+
298+ // either of these statements removes the bug:
299+ //generateComponents(0, fChars.length);
300+ //generateComponents(pos, fChars.length);
301+
302+ int startPos = pos;
303+ float width = maxAdvance;
304+
305+ int tlcIndex;
306+ int tlcStart = fComponentStart;
307+
308+ for (tlcIndex = 0; tlcIndex < fComponents.length; tlcIndex++) {
309+ int gaLimit = tlcStart + fComponents[tlcIndex].getNumCharacters();
310+ if (gaLimit > startPos) {
311+ break;
312+ }
313+ else {
314+ tlcStart = gaLimit;
315+ }
316+ }
317+
318+ // tlcStart is now the start of the tlc at tlcIndex
319+
320+ for (; tlcIndex < fComponents.length; tlcIndex++) {
321+
322+ TextLineComponent tlc = fComponents[tlcIndex];
323+ int numCharsInGa = tlc.getNumCharacters();
324+
325+ int lineBreak = tlc.getLineBreakIndex(startPos - tlcStart, width);
326+ if (lineBreak == numCharsInGa && tlcIndex < fComponents.length) {
327+ width -= tlc.getAdvanceBetween(startPos - tlcStart, lineBreak);
328+ tlcStart += numCharsInGa;
329+ startPos = tlcStart;
330+ }
331+ else {
332+ return tlcStart + lineBreak;
333+ }
334+ }
335+
336+ if (fComponentLimit < fChars.length) {
337+ // format more text and try again
338+ //if (haveLayoutWindow) {
339+ // outOfWindow++;
340+ //}
341+
342+ generateComponents(pos, fChars.length);
343+ return calcLineBreak(pos, maxAdvance);
344+ }
345+
346+ return fChars.length;
347+ }
348+
349+ /**
350+ * According to the Unicode Bidirectional Behavior specification
351+ * (Unicode Standard 2.0, section 3.11), whitespace at the ends
352+ * of lines which would naturally flow against the base direction
353+ * must be made to flow with the line direction, and moved to the
354+ * end of the line. This method returns the start of the sequence
355+ * of trailing whitespace characters to move to the end of a
356+ * line taken from the given range.
357+ */
358+ private int trailingCdWhitespaceStart(int startPos, int limitPos) {
359+
360+ if (fLevels != null) {
361+ // Back up over counterdirectional whitespace
362+ final byte baseLevel = (byte) (fIsDirectionLTR? 0 : 1);
363+ for (int cdWsStart = limitPos; --cdWsStart >= startPos;) {
364+ if ((fLevels[cdWsStart] % 2) == baseLevel ||
365+ Character.getDirectionality(fChars[cdWsStart]) != Character.DIRECTIONALITY_WHITESPACE) {
366+ return ++cdWsStart;
367+ }
368+ }
369+ }
370+
371+ return startPos;
372+ }
373+
374+ private TextLineComponent[] makeComponentsOnRange(int startPos,
375+ int limitPos) {
376+
377+ // sigh I really hate to do this here since it's part of the
378+ // bidi algorithm.
379+ // cdWsStart is the start of the trailing counterdirectional
380+ // whitespace
381+ final int cdWsStart = trailingCdWhitespaceStart(startPos, limitPos);
382+
383+ int tlcIndex;
384+ int tlcStart = fComponentStart;
385+
386+ for (tlcIndex = 0; tlcIndex < fComponents.length; tlcIndex++) {
387+ int gaLimit = tlcStart + fComponents[tlcIndex].getNumCharacters();
388+ if (gaLimit > startPos) {
389+ break;
390+ }
391+ else {
392+ tlcStart = gaLimit;
393+ }
394+ }
395+
396+ // tlcStart is now the start of the tlc at tlcIndex
397+
398+ int componentCount;
399+ {
400+ boolean split = false;
401+ int compStart = tlcStart;
402+ int lim=tlcIndex;
403+ for (boolean cont=true; cont; lim++) {
404+ int gaLimit = compStart + fComponents[lim].getNumCharacters();
405+ if (cdWsStart > Math.max(compStart, startPos)
406+ && cdWsStart < Math.min(gaLimit, limitPos)) {
407+ split = true;
408+ }
409+ if (gaLimit >= limitPos) {
410+ cont=false;
411+ }
412+ else {
413+ compStart = gaLimit;
414+ }
415+ }
416+ componentCount = lim-tlcIndex;
417+ if (split) {
418+ componentCount++;
419+ }
420+ }
421+
422+ TextLineComponent[] components = new TextLineComponent[componentCount];
423+ int newCompIndex = 0;
424+ int linePos = startPos;
425+
426+ int breakPt = cdWsStart;
427+
428+ int subsetFlag;
429+ if (breakPt == startPos) {
430+ subsetFlag = fIsDirectionLTR? TextLineComponent.LEFT_TO_RIGHT :
431+ TextLineComponent.RIGHT_TO_LEFT;
432+ breakPt = limitPos;
433+ }
434+ else {
435+ subsetFlag = TextLineComponent.UNCHANGED;
436+ }
437+
438+ while (linePos < limitPos) {
439+
440+ int compLength = fComponents[tlcIndex].getNumCharacters();
441+ int tlcLimit = tlcStart + compLength;
442+
443+ int start = Math.max(linePos, tlcStart);
444+ int limit = Math.min(breakPt, tlcLimit);
445+
446+ components[newCompIndex++] = fComponents[tlcIndex].getSubset(
447+ start-tlcStart,
448+ limit-tlcStart,
449+ subsetFlag);
450+ linePos += (limit-start);
451+ if (linePos == breakPt) {
452+ breakPt = limitPos;
453+ subsetFlag = fIsDirectionLTR? TextLineComponent.LEFT_TO_RIGHT :
454+ TextLineComponent.RIGHT_TO_LEFT;
455+ }
456+ if (linePos == tlcLimit) {
457+ tlcIndex++;
458+ tlcStart = tlcLimit;
459+ }
460+ }
461+
462+ return components;
463+ }
464+
465+ private TextLine makeTextLineOnRange(int startPos, int limitPos) {
466+
467+ int[] charsLtoV = null;
468+ byte[] charLevels = null;
469+
470+ if (fBidi != null) {
471+ Bidi lineBidi = fBidi.createLineBidi(startPos, limitPos);
472+ charLevels = BidiUtils.getLevels(lineBidi);
473+ int[] charsVtoL = BidiUtils.createVisualToLogicalMap(charLevels);
474+ charsLtoV = BidiUtils.createInverseMap(charsVtoL);
475+ }
476+
477+ TextLineComponent[] components = makeComponentsOnRange(startPos, limitPos);
478+
479+ return new TextLine(fFrc,
480+ components,
481+ fBaselineOffsets,
482+ fChars,
483+ startPos,
484+ limitPos,
485+ charsLtoV,
486+ charLevels,
487+ fIsDirectionLTR);
488+
489+ }
490+
491+ private void ensureComponents(int start, int limit) {
492+
493+ if (start < fComponentStart || limit > fComponentLimit) {
494+ generateComponents(start, limit);
495+ }
496+ }
497+
498+ private void makeLayoutWindow(int localStart) {
499+
500+ int compStart = localStart;
501+ int compLimit = fChars.length;
502+
503+ // If we've already gone past the layout window, format to end of paragraph
504+ if (layoutCount > 0 && !haveLayoutWindow) {
505+ float avgLineLength = Math.max(layoutCharCount / layoutCount, 1);
506+ compLimit = Math.min(localStart + (int)(avgLineLength*EST_LINES), fChars.length);
507+ }
508+
509+ if (localStart > 0 || compLimit < fChars.length) {
510+ if (charIter == null) {
511+ charIter = new CharArrayIterator(fChars);
512+ }
513+ else {
514+ charIter.reset(fChars);
515+ }
516+ if (fLineBreak == null) {
517+ fLineBreak = BreakIterator.getLineInstance();
518+ }
519+ fLineBreak.setText(charIter);
520+ if (localStart > 0) {
521+ if (!fLineBreak.isBoundary(localStart)) {
522+ compStart = fLineBreak.preceding(localStart);
523+ }
524+ }
525+ if (compLimit < fChars.length) {
526+ if (!fLineBreak.isBoundary(compLimit)) {
527+ compLimit = fLineBreak.following(compLimit);
528+ }
529+ }
530+ }
531+
532+ ensureComponents(compStart, compLimit);
533+ haveLayoutWindow = true;
534+ }
535+
536+ /**
537+ * Returns the index of the first character which will not fit on
538+ * on a line beginning at <code>start</code> and possible
539+ * measuring up to <code>maxAdvance</code> in graphical width.
540+ *
541+ * @param start the character index at which to start measuring.
542+ * <code>start</code> is an absolute index, not relative to the
543+ * start of the paragraph
544+ * @param maxAdvance the graphical width in which the line must fit
545+ * @return the index after the last character that will fit
546+ * on a line beginning at <code>start</code>, which is not longer
547+ * than <code>maxAdvance</code> in graphical width
548+ * @throws IllegalArgumentException if <code>start</code> is
549+ * less than the beginning of the paragraph.
550+ */
551+ public int getLineBreakIndex(int start, float maxAdvance) {
552+
553+ int localStart = start - fStart;
554+
555+ if (!haveLayoutWindow ||
556+ localStart < fComponentStart ||
557+ localStart >= fComponentLimit) {
558+ makeLayoutWindow(localStart);
559+ }
560+
561+ return calcLineBreak(localStart, maxAdvance) + fStart;
562+ }
563+
564+ /**
565+ * Returns the graphical width of a line beginning at <code>start</code>
566+ * and including characters up to <code>limit</code>.
567+ * <code>start</code> and <code>limit</code> are absolute indices,
568+ * not relative to the start of the paragraph.
569+ *
570+ * @param start the character index at which to start measuring
571+ * @param limit the character index at which to stop measuring
572+ * @return the graphical width of a line beginning at <code>start</code>
573+ * and including characters up to <code>limit</code>
574+ * @throws IndexOutOfBoundsException if <code>limit</code> is less
575+ * than <code>start</code>
576+ * @throws IllegalArgumentException if <code>start</code> or
577+ * <code>limit</code> is not between the beginning of
578+ * the paragraph and the end of the paragraph.
579+ */
580+ public float getAdvanceBetween(int start, int limit) {
581+
582+ int localStart = start - fStart;
583+ int localLimit = limit - fStart;
584+
585+ ensureComponents(localStart, localLimit);
586+ TextLine line = makeTextLineOnRange(localStart, localLimit);
587+ return line.getMetrics().advance;
588+ // could cache line in case getLayout is called with same start, limit
589+ }
590+
591+ /**
592+ * Returns a <code>TextLayout</code> on the given character range.
593+ *
594+ * @param start the index of the first character
595+ * @param limit the index after the last character. Must be greater
596+ * than <code>start</code>
597+ * @return a <code>TextLayout</code> for the characters beginning at
598+ * <code>start</code> up to (but not including) <code>limit</code>
599+ * @throws IndexOutOfBoundsException if <code>limit</code> is less
600+ * than <code>start</code>
601+ * @throws IllegalArgumentException if <code>start</code> or
602+ * <code>limit</code> is not between the beginning of
603+ * the paragraph and the end of the paragraph.
604+ */
605+ public TextLayout getLayout(int start, int limit) {
606+
607+ int localStart = start - fStart;
608+ int localLimit = limit - fStart;
609+
610+ ensureComponents(localStart, localLimit);
611+ TextLine textLine = makeTextLineOnRange(localStart, localLimit);
612+
613+ if (localLimit < fChars.length) {
614+ layoutCharCount += limit-start;
615+ layoutCount++;
616+ }
617+
618+ return new TextLayout(textLine,
619+ fBaseline,
620+ fBaselineOffsets,
621+ fJustifyRatio);
622+ }
623+
624+ private int formattedChars = 0;
625+ private static boolean wantStats = false;/*"true".equals(System.getProperty("collectStats"));*/
626+ private boolean collectStats = false;
627+
628+ private void printStats() {
629+ System.out.println("formattedChars: " + formattedChars);
630+ //formattedChars = 0;
631+ collectStats = false;
632+ }
633+
634+ /**
635+ * Updates the <code>TextMeasurer</code> after a single character has
636+ * been inserted
637+ * into the paragraph currently represented by this
638+ * <code>TextMeasurer</code>. After this call, this
639+ * <code>TextMeasurer</code> is equivalent to a new
640+ * <code>TextMeasurer</code> created from the text; however, it will
641+ * usually be more efficient to update an existing
642+ * <code>TextMeasurer</code> than to create a new one from scratch.
643+ *
644+ * @param newParagraph the text of the paragraph after performing
645+ * the insertion. Cannot be null.
646+ * @param insertPos the position in the text where the character was
647+ * inserted. Must not be less than the start of
648+ * <code>newParagraph</code>, and must be less than the end of
649+ * <code>newParagraph</code>.
650+ * @throws IndexOutOfBoundsException if <code>insertPos</code> is less
651+ * than the start of <code>newParagraph</code> or greater than
652+ * or equal to the end of <code>newParagraph</code>
653+ * @throws NullPointerException if <code>newParagraph</code> is
654+ * <code>null</code>
655+ */
656+ public void insertChar(AttributedCharacterIterator newParagraph, int insertPos) {
657+
658+ if (collectStats) {
659+ printStats();
660+ }
661+ if (wantStats) {
662+ collectStats = true;
663+ }
664+
665+ fStart = newParagraph.getBeginIndex();
666+ int end = newParagraph.getEndIndex();
667+ if (end - fStart != fChars.length+1) {
668+ initAll(newParagraph);
669+ }
670+
671+ char[] newChars = new char[end-fStart];
672+ int newCharIndex = insertPos - fStart;
673+ System.arraycopy(fChars, 0, newChars, 0, newCharIndex);
674+
675+ char newChar = newParagraph.setIndex(insertPos);
676+ newChars[newCharIndex] = newChar;
677+ System.arraycopy(fChars,
678+ newCharIndex,
679+ newChars,
680+ newCharIndex+1,
681+ end-insertPos-1);
682+ fChars = newChars;
683+
684+ if (fBidi != null || Bidi.requiresBidi(newChars, newCharIndex, newCharIndex + 1) ||
685+ newParagraph.getAttribute(TextAttribute.BIDI_EMBEDDING) != null) {
686+
687+ fBidi = new Bidi(newParagraph);
688+ if (fBidi.isLeftToRight()) {
689+ fBidi = null;
690+ }
691+ }
692+
693+ fParagraph = StyledParagraph.insertChar(newParagraph,
694+ fChars,
695+ insertPos,
696+ fParagraph);
697+ invalidateComponents();
698+ }
699+
700+ /**
701+ * Updates the <code>TextMeasurer</code> after a single character has
702+ * been deleted
703+ * from the paragraph currently represented by this
704+ * <code>TextMeasurer</code>. After this call, this
705+ * <code>TextMeasurer</code> is equivalent to a new <code>TextMeasurer</code>
706+ * created from the text; however, it will usually be more efficient
707+ * to update an existing <code>TextMeasurer</code> than to create a new one
708+ * from scratch.
709+ *
710+ * @param newParagraph the text of the paragraph after performing
711+ * the deletion. Cannot be null.
712+ * @param deletePos the position in the text where the character was removed.
713+ * Must not be less than
714+ * the start of <code>newParagraph</code>, and must not be greater than the
715+ * end of <code>newParagraph</code>.
716+ * @throws IndexOutOfBoundsException if <code>deletePos</code> is
717+ * less than the start of <code>newParagraph</code> or greater
718+ * than the end of <code>newParagraph</code>
719+ * @throws NullPointerException if <code>newParagraph</code> is
720+ * <code>null</code>
721+ */
722+ public void deleteChar(AttributedCharacterIterator newParagraph, int deletePos) {
723+
724+ fStart = newParagraph.getBeginIndex();
725+ int end = newParagraph.getEndIndex();
726+ if (end - fStart != fChars.length-1) {
727+ initAll(newParagraph);
728+ }
729+
730+ char[] newChars = new char[end-fStart];
731+ int changedIndex = deletePos-fStart;
732+
733+ System.arraycopy(fChars, 0, newChars, 0, deletePos-fStart);
734+ System.arraycopy(fChars, changedIndex+1, newChars, changedIndex, end-deletePos);
735+ fChars = newChars;
736+
737+ if (fBidi != null) {
738+ fBidi = new Bidi(newParagraph);
739+ if (fBidi.isLeftToRight()) {
740+ fBidi = null;
741+ }
742+ }
743+
744+ fParagraph = StyledParagraph.deleteChar(newParagraph,
745+ fChars,
746+ deletePos,
747+ fParagraph);
748+ invalidateComponents();
749+ }
750+
751+ /**
752+ * NOTE: This method is only for LineBreakMeasurer's use. It is package-
753+ * private because it returns internal data.
754+ */
755+ char[] getChars() {
756+
757+ return fChars;
758+ }
759+}