001 /*
002 * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 /*
027 * (C) Copyright Taligent, Inc. 1996 - 1997, All Rights Reserved
028 * (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
029 *
030 * The original version of this source code and documentation is
031 * copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
032 * of IBM. These materials are provided under terms of a License
033 * Agreement between Taligent and Sun. This technology is protected
034 * by multiple US and International patents.
035 *
036 * This notice and attribution to Taligent may not be removed.
037 * Taligent is a registered trademark of Taligent, Inc.
038 *
039 */
040
041 package java.awt.font;
042
043 import java.awt.Font;
044
045 import java.text.AttributedCharacterIterator;
046 import java.text.AttributedString;
047 import java.text.Bidi;
048 import java.text.BreakIterator;
049 import java.text.CharacterIterator;
050
051 import java.awt.font.FontRenderContext;
052
053 import java.util.Hashtable;
054 import java.util.Map;
055
056 import sun.font.AttributeValues;
057 import sun.font.BidiUtils;
058 import sun.font.TextLineComponent;
059 import sun.font.TextLabelFactory;
060 import sun.font.FontResolver;
061
062 /**
063 * The <code>TextMeasurer</code> class provides the primitive operations
064 * needed for line break: measuring up to a given advance, determining the
065 * advance of a range of characters, and generating a
066 * <code>TextLayout</code> for a range of characters. It also provides
067 * methods for incremental editing of paragraphs.
068 * <p>
069 * A <code>TextMeasurer</code> object is constructed with an
070 * {@link java.text.AttributedCharacterIterator AttributedCharacterIterator}
071 * representing a single paragraph of text. The value returned by the
072 * {@link AttributedCharacterIterator#getBeginIndex() getBeginIndex}
073 * method of <code>AttributedCharacterIterator</code>
074 * defines the absolute index of the first character. The value
075 * returned by the
076 * {@link AttributedCharacterIterator#getEndIndex() getEndIndex}
077 * method of <code>AttributedCharacterIterator</code> defines the index
078 * past the last character. These values define the range of indexes to
079 * use in calls to the <code>TextMeasurer</code>. For example, calls to
080 * get the advance of a range of text or the line break of a range of text
081 * must use indexes between the beginning and end index values. Calls to
082 * {@link #insertChar(java.text.AttributedCharacterIterator, int) insertChar}
083 * and
084 * {@link #deleteChar(java.text.AttributedCharacterIterator, int) deleteChar}
085 * reset the <code>TextMeasurer</code> to use the beginning index and end
086 * index of the <code>AttributedCharacterIterator</code> passed in those calls.
087 * <p>
088 * Most clients will use the more convenient <code>LineBreakMeasurer</code>,
089 * which implements the standard line break policy (placing as many words
090 * as will fit on each line).
091 *
092 * @author John Raley
093 * @version 1.31, 04/20/01
094 * @see LineBreakMeasurer
095 * @since 1.3
096 */
097
098 public final class TextMeasurer implements Cloneable {
099
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();
110 }
111 catch(NumberFormatException e) {
112 }
113 }
114 //System.out.println("EST_LINES="+EST_LINES);
115 }
116 */
117
118 private FontRenderContext fFrc;
119
120 private int fStart;
121
122 // characters in source text
123 private char[] fChars;
124
125 // Bidi for this paragraph
126 private Bidi fBidi;
127
128 // Levels array for chars in this paragraph - needed to reorder
129 // trailing counterdirectional whitespace
130 private byte[] fLevels;
131
132 // line components in logical order
133 private TextLineComponent[] fComponents;
134
135 // index where components begin
136 private int fComponentStart;
137
138 // index where components end
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,
166 FontRenderContext frc) {
167
168 fFrc = frc;
169 initAll(text);
170 }
171
172 protected Object clone() {
173 TextMeasurer other;
174 try {
175 other = (TextMeasurer) super .clone();
176 } catch (CloneNotSupportedException e) {
177 throw new Error();
178 }
179 if (fComponents != null) {
180 other.fComponents = (TextLineComponent[]) fComponents
181 .clone();
182 }
183 return other;
184 }
185
186 private void invalidateComponents() {
187 fComponentStart = fComponentLimit = fChars.length;
188 fComponents = null;
189 haveLayoutWindow = false;
190 }
191
192 /**
193 * Initialize state, including fChars array, direction, and
194 * fBidi.
195 */
196 private void initAll(AttributedCharacterIterator text) {
197
198 fStart = text.getBeginIndex();
199
200 // extract chars
201 fChars = new char[text.getEndIndex() - fStart];
202
203 int n = 0;
204 for (char c = text.first(); c != text.DONE; c = text.next()) {
205 fChars[n++] = c;
206 }
207
208 text.first();
209
210 fBidi = new Bidi(text);
211 if (fBidi.isLeftToRight()) {
212 fBidi = null;
213 }
214
215 text.first();
216 Map paragraphAttrs = text.getAttributes();
217 NumericShaper shaper = AttributeValues
218 .getNumericShaping(paragraphAttrs);
219 if (shaper != null) {
220 shaper.shape(fChars, 0, fChars.length);
221 }
222
223 fParagraph = new StyledParagraph(text, fChars);
224
225 // set paragraph attributes
226 {
227 // If there's an embedded graphic at the start of the
228 // paragraph, look for the first non-graphic character
229 // and use it and its font to initialize the paragraph.
230 // If not, use the first graphic to initialize.
231 fJustifyRatio = AttributeValues
232 .getJustification(paragraphAttrs);
233
234 boolean haveFont = TextLine.advanceToFirstFont(text);
235
236 if (haveFont) {
237 Font defaultFont = TextLine.getFontAtCurrentPos(text);
238 int charsStart = text.getIndex() - text.getBeginIndex();
239 LineMetrics lm = defaultFont.getLineMetrics(fChars,
240 charsStart, charsStart + 1, fFrc);
241 fBaseline = (byte) lm.getBaselineIndex();
242 fBaselineOffsets = lm.getBaselineOffsets();
243 } else {
244 // hmmm what to do here? Just try to supply reasonable
245 // values I guess.
246
247 GraphicAttribute graphic = (GraphicAttribute) paragraphAttrs
248 .get(TextAttribute.CHAR_REPLACEMENT);
249 fBaseline = TextLayout.getBaselineFromGraphic(graphic);
250 Font dummyFont = new Font(new Hashtable(5, (float) 0.9));
251 LineMetrics lm = dummyFont.getLineMetrics(" ", 0, 1,
252 fFrc);
253 fBaselineOffsets = lm.getBaselineOffsets();
254 }
255 fBaselineOffsets = TextLine.getNormalizedOffsets(
256 fBaselineOffsets, fBaseline);
257 }
258
259 invalidateComponents();
260 }
261
262 /**
263 * Generate components for the paragraph. fChars, fBidi should have been
264 * initialized already.
265 */
266 private void generateComponents(int startingAt, int endingAt) {
267
268 if (collectStats) {
269 formattedChars += (endingAt - startingAt);
270 }
271 int layoutFlags = 0; // no extra info yet, bidi determines run and line direction
272 TextLabelFactory factory = new TextLabelFactory(fFrc, fChars,
273 fBidi, layoutFlags);
274
275 int[] charsLtoV = null;
276
277 if (fBidi != null) {
278 fLevels = BidiUtils.getLevels(fBidi);
279 int[] charsVtoL = BidiUtils
280 .createVisualToLogicalMap(fLevels);
281 charsLtoV = BidiUtils.createInverseMap(charsVtoL);
282 fIsDirectionLTR = fBidi.baseIsLeftToRight();
283 } else {
284 fLevels = null;
285 fIsDirectionLTR = true;
286 }
287
288 try {
289 fComponents = TextLine.getComponents(fParagraph, fChars,
290 startingAt, endingAt, charsLtoV, fLevels, factory);
291 } catch (IllegalArgumentException e) {
292 System.out.println("startingAt=" + startingAt
293 + "; endingAt=" + endingAt);
294 System.out.println("fComponentLimit=" + fComponentLimit);
295 throw e;
296 }
297
298 fComponentStart = startingAt;
299 fComponentLimit = endingAt;
300 //debugFormatCount += (endingAt-startingAt);
301 }
302
303 private int calcLineBreak(final int pos, final float maxAdvance) {
304
305 // either of these statements removes the bug:
306 //generateComponents(0, fChars.length);
307 //generateComponents(pos, fChars.length);
308
309 int startPos = pos;
310 float width = maxAdvance;
311
312 int tlcIndex;
313 int tlcStart = fComponentStart;
314
315 for (tlcIndex = 0; tlcIndex < fComponents.length; tlcIndex++) {
316 int gaLimit = tlcStart
317 + fComponents[tlcIndex].getNumCharacters();
318 if (gaLimit > startPos) {
319 break;
320 } else {
321 tlcStart = gaLimit;
322 }
323 }
324
325 // tlcStart is now the start of the tlc at tlcIndex
326
327 for (; tlcIndex < fComponents.length; tlcIndex++) {
328
329 TextLineComponent tlc = fComponents[tlcIndex];
330 int numCharsInGa = tlc.getNumCharacters();
331
332 int lineBreak = tlc.getLineBreakIndex(startPos - tlcStart,
333 width);
334 if (lineBreak == numCharsInGa
335 && tlcIndex < fComponents.length) {
336 width -= tlc.getAdvanceBetween(startPos - tlcStart,
337 lineBreak);
338 tlcStart += numCharsInGa;
339 startPos = tlcStart;
340 } else {
341 return tlcStart + lineBreak;
342 }
343 }
344
345 if (fComponentLimit < fChars.length) {
346 // format more text and try again
347 //if (haveLayoutWindow) {
348 // outOfWindow++;
349 //}
350
351 generateComponents(pos, fChars.length);
352 return calcLineBreak(pos, maxAdvance);
353 }
354
355 return fChars.length;
356 }
357
358 /**
359 * According to the Unicode Bidirectional Behavior specification
360 * (Unicode Standard 2.0, section 3.11), whitespace at the ends
361 * of lines which would naturally flow against the base direction
362 * must be made to flow with the line direction, and moved to the
363 * end of the line. This method returns the start of the sequence
364 * of trailing whitespace characters to move to the end of a
365 * line taken from the given range.
366 */
367 private int trailingCdWhitespaceStart(int startPos, int limitPos) {
368
369 if (fLevels != null) {
370 // Back up over counterdirectional whitespace
371 final byte baseLevel = (byte) (fIsDirectionLTR ? 0 : 1);
372 for (int cdWsStart = limitPos; --cdWsStart >= startPos;) {
373 if ((fLevels[cdWsStart] % 2) == baseLevel
374 || Character
375 .getDirectionality(fChars[cdWsStart]) != Character.DIRECTIONALITY_WHITESPACE) {
376 return ++cdWsStart;
377 }
378 }
379 }
380
381 return startPos;
382 }
383
384 private TextLineComponent[] makeComponentsOnRange(int startPos,
385 int limitPos) {
386
387 // sigh I really hate to do this here since it's part of the
388 // bidi algorithm.
389 // cdWsStart is the start of the trailing counterdirectional
390 // whitespace
391 final int cdWsStart = trailingCdWhitespaceStart(startPos,
392 limitPos);
393
394 int tlcIndex;
395 int tlcStart = fComponentStart;
396
397 for (tlcIndex = 0; tlcIndex < fComponents.length; tlcIndex++) {
398 int gaLimit = tlcStart
399 + fComponents[tlcIndex].getNumCharacters();
400 if (gaLimit > startPos) {
401 break;
402 } else {
403 tlcStart = gaLimit;
404 }
405 }
406
407 // tlcStart is now the start of the tlc at tlcIndex
408
409 int componentCount;
410 {
411 boolean split = false;
412 int compStart = tlcStart;
413 int lim = tlcIndex;
414 for (boolean cont = true; cont; lim++) {
415 int gaLimit = compStart
416 + fComponents[lim].getNumCharacters();
417 if (cdWsStart > Math.max(compStart, startPos)
418 && cdWsStart < Math.min(gaLimit, limitPos)) {
419 split = true;
420 }
421 if (gaLimit >= limitPos) {
422 cont = false;
423 } else {
424 compStart = gaLimit;
425 }
426 }
427 componentCount = lim - tlcIndex;
428 if (split) {
429 componentCount++;
430 }
431 }
432
433 TextLineComponent[] components = new TextLineComponent[componentCount];
434 int newCompIndex = 0;
435 int linePos = startPos;
436
437 int breakPt = cdWsStart;
438
439 int subsetFlag;
440 if (breakPt == startPos) {
441 subsetFlag = fIsDirectionLTR ? TextLineComponent.LEFT_TO_RIGHT
442 : TextLineComponent.RIGHT_TO_LEFT;
443 breakPt = limitPos;
444 } else {
445 subsetFlag = TextLineComponent.UNCHANGED;
446 }
447
448 while (linePos < limitPos) {
449
450 int compLength = fComponents[tlcIndex].getNumCharacters();
451 int tlcLimit = tlcStart + compLength;
452
453 int start = Math.max(linePos, tlcStart);
454 int limit = Math.min(breakPt, tlcLimit);
455
456 components[newCompIndex++] = fComponents[tlcIndex]
457 .getSubset(start - tlcStart, limit - tlcStart,
458 subsetFlag);
459 linePos += (limit - start);
460 if (linePos == breakPt) {
461 breakPt = limitPos;
462 subsetFlag = fIsDirectionLTR ? TextLineComponent.LEFT_TO_RIGHT
463 : TextLineComponent.RIGHT_TO_LEFT;
464 }
465 if (linePos == tlcLimit) {
466 tlcIndex++;
467 tlcStart = tlcLimit;
468 }
469 }
470
471 return components;
472 }
473
474 private TextLine makeTextLineOnRange(int startPos, int limitPos) {
475
476 int[] charsLtoV = null;
477 byte[] charLevels = null;
478
479 if (fBidi != null) {
480 Bidi lineBidi = fBidi.createLineBidi(startPos, limitPos);
481 charLevels = BidiUtils.getLevels(lineBidi);
482 int[] charsVtoL = BidiUtils
483 .createVisualToLogicalMap(charLevels);
484 charsLtoV = BidiUtils.createInverseMap(charsVtoL);
485 }
486
487 TextLineComponent[] components = makeComponentsOnRange(
488 startPos, limitPos);
489
490 return new TextLine(fFrc, components, fBaselineOffsets, fChars,
491 startPos, limitPos, charsLtoV, charLevels,
492 fIsDirectionLTR);
493
494 }
495
496 private void ensureComponents(int start, int limit) {
497
498 if (start < fComponentStart || limit > fComponentLimit) {
499 generateComponents(start, limit);
500 }
501 }
502
503 private void makeLayoutWindow(int localStart) {
504
505 int compStart = localStart;
506 int compLimit = fChars.length;
507
508 // If we've already gone past the layout window, format to end of paragraph
509 if (layoutCount > 0 && !haveLayoutWindow) {
510 float avgLineLength = Math.max(layoutCharCount
511 / layoutCount, 1);
512 compLimit = Math.min(localStart
513 + (int) (avgLineLength * EST_LINES), fChars.length);
514 }
515
516 if (localStart > 0 || compLimit < fChars.length) {
517 if (charIter == null) {
518 charIter = new CharArrayIterator(fChars);
519 } else {
520 charIter.reset(fChars);
521 }
522 if (fLineBreak == null) {
523 fLineBreak = BreakIterator.getLineInstance();
524 }
525 fLineBreak.setText(charIter);
526 if (localStart > 0) {
527 if (!fLineBreak.isBoundary(localStart)) {
528 compStart = fLineBreak.preceding(localStart);
529 }
530 }
531 if (compLimit < fChars.length) {
532 if (!fLineBreak.isBoundary(compLimit)) {
533 compLimit = fLineBreak.following(compLimit);
534 }
535 }
536 }
537
538 ensureComponents(compStart, compLimit);
539 haveLayoutWindow = true;
540 }
541
542 /**
543 * Returns the index of the first character which will not fit on
544 * on a line beginning at <code>start</code> and possible
545 * measuring up to <code>maxAdvance</code> in graphical width.
546 *
547 * @param start the character index at which to start measuring.
548 * <code>start</code> is an absolute index, not relative to the
549 * start of the paragraph
550 * @param maxAdvance the graphical width in which the line must fit
551 * @return the index after the last character that will fit
552 * on a line beginning at <code>start</code>, which is not longer
553 * than <code>maxAdvance</code> in graphical width
554 * @throws IllegalArgumentException if <code>start</code> is
555 * less than the beginning of the paragraph.
556 */
557 public int getLineBreakIndex(int start, float maxAdvance) {
558
559 int localStart = start - fStart;
560
561 if (!haveLayoutWindow || 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, fBaseline, fBaselineOffsets,
624 fJustifyRatio);
625 }
626
627 private int formattedChars = 0;
628 private static boolean wantStats = false;/*"true".equals(System.getProperty("collectStats"));*/
629 private boolean collectStats = false;
630
631 private void printStats() {
632 System.out.println("formattedChars: " + formattedChars);
633 //formattedChars = 0;
634 collectStats = false;
635 }
636
637 /**
638 * Updates the <code>TextMeasurer</code> after a single character has
639 * been inserted
640 * into the paragraph currently represented by this
641 * <code>TextMeasurer</code>. After this call, this
642 * <code>TextMeasurer</code> is equivalent to a new
643 * <code>TextMeasurer</code> created from the text; however, it will
644 * usually be more efficient to update an existing
645 * <code>TextMeasurer</code> than to create a new one from scratch.
646 *
647 * @param newParagraph the text of the paragraph after performing
648 * the insertion. Cannot be null.
649 * @param insertPos the position in the text where the character was
650 * inserted. Must not be less than the start of
651 * <code>newParagraph</code>, and must be less than the end of
652 * <code>newParagraph</code>.
653 * @throws IndexOutOfBoundsException if <code>insertPos</code> is less
654 * than the start of <code>newParagraph</code> or greater than
655 * or equal to the end of <code>newParagraph</code>
656 * @throws NullPointerException if <code>newParagraph</code> is
657 * <code>null</code>
658 */
659 public void insertChar(AttributedCharacterIterator newParagraph,
660 int insertPos) {
661
662 if (collectStats) {
663 printStats();
664 }
665 if (wantStats) {
666 collectStats = true;
667 }
668
669 fStart = newParagraph.getBeginIndex();
670 int end = newParagraph.getEndIndex();
671 if (end - fStart != fChars.length + 1) {
672 initAll(newParagraph);
673 }
674
675 char[] newChars = new char[end - fStart];
676 int newCharIndex = insertPos - fStart;
677 System.arraycopy(fChars, 0, newChars, 0, newCharIndex);
678
679 char newChar = newParagraph.setIndex(insertPos);
680 newChars[newCharIndex] = newChar;
681 System.arraycopy(fChars, newCharIndex, newChars,
682 newCharIndex + 1, end - insertPos - 1);
683 fChars = newChars;
684
685 if (fBidi != null
686 || Bidi.requiresBidi(newChars, newCharIndex,
687 newCharIndex + 1)
688 || newParagraph
689 .getAttribute(TextAttribute.BIDI_EMBEDDING) != null) {
690
691 fBidi = new Bidi(newParagraph);
692 if (fBidi.isLeftToRight()) {
693 fBidi = null;
694 }
695 }
696
697 fParagraph = StyledParagraph.insertChar(newParagraph, fChars,
698 insertPos, fParagraph);
699 invalidateComponents();
700 }
701
702 /**
703 * Updates the <code>TextMeasurer</code> after a single character has
704 * been deleted
705 * from the paragraph currently represented by this
706 * <code>TextMeasurer</code>. After this call, this
707 * <code>TextMeasurer</code> is equivalent to a new <code>TextMeasurer</code>
708 * created from the text; however, it will usually be more efficient
709 * to update an existing <code>TextMeasurer</code> than to create a new one
710 * from scratch.
711 *
712 * @param newParagraph the text of the paragraph after performing
713 * the deletion. Cannot be null.
714 * @param deletePos the position in the text where the character was removed.
715 * Must not be less than
716 * the start of <code>newParagraph</code>, and must not be greater than the
717 * end of <code>newParagraph</code>.
718 * @throws IndexOutOfBoundsException if <code>deletePos</code> is
719 * less than the start of <code>newParagraph</code> or greater
720 * than the end of <code>newParagraph</code>
721 * @throws NullPointerException if <code>newParagraph</code> is
722 * <code>null</code>
723 */
724 public void deleteChar(AttributedCharacterIterator newParagraph,
725 int deletePos) {
726
727 fStart = newParagraph.getBeginIndex();
728 int end = newParagraph.getEndIndex();
729 if (end - fStart != fChars.length - 1) {
730 initAll(newParagraph);
731 }
732
733 char[] newChars = new char[end - fStart];
734 int changedIndex = deletePos - fStart;
735
736 System.arraycopy(fChars, 0, newChars, 0, deletePos - fStart);
737 System.arraycopy(fChars, changedIndex + 1, newChars,
738 changedIndex, end - deletePos);
739 fChars = newChars;
740
741 if (fBidi != null) {
742 fBidi = new Bidi(newParagraph);
743 if (fBidi.isLeftToRight()) {
744 fBidi = null;
745 }
746 }
747
748 fParagraph = StyledParagraph.deleteChar(newParagraph, fChars,
749 deletePos, fParagraph);
750 invalidateComponents();
751 }
752
753 /**
754 * NOTE: This method is only for LineBreakMeasurer's use. It is package-
755 * private because it returns internal data.
756 */
757 char[] getChars() {
758
759 return fChars;
760 }
761 }
|