0N/A/*
2362N/A * Copyright (c) 1997, 2005, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/A/*
0N/A * (C) Copyright Taligent, Inc. 1996 - 1997, All Rights Reserved
0N/A * (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
0N/A *
0N/A * The original version of this source code and documentation is
0N/A * copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
0N/A * of IBM. These materials are provided under terms of a License
0N/A * Agreement between Taligent and Sun. This technology is protected
0N/A * by multiple US and International patents.
0N/A *
0N/A * This notice and attribution to Taligent may not be removed.
0N/A * Taligent is a registered trademark of Taligent, Inc.
0N/A *
0N/A */
0N/A
0N/Apackage java.awt.font;
0N/A
0N/Aimport java.awt.Font;
0N/A
0N/Aimport java.text.AttributedCharacterIterator;
0N/Aimport java.text.AttributedString;
0N/Aimport java.text.Bidi;
0N/Aimport java.text.BreakIterator;
0N/Aimport java.text.CharacterIterator;
0N/A
0N/Aimport java.awt.font.FontRenderContext;
0N/A
0N/Aimport java.util.Hashtable;
0N/Aimport java.util.Map;
0N/A
0N/Aimport sun.font.AttributeValues;
0N/Aimport sun.font.BidiUtils;
0N/Aimport sun.font.TextLineComponent;
0N/Aimport sun.font.TextLabelFactory;
0N/Aimport sun.font.FontResolver;
0N/A
0N/A/**
0N/A * The <code>TextMeasurer</code> class provides the primitive operations
0N/A * needed for line break: measuring up to a given advance, determining the
0N/A * advance of a range of characters, and generating a
0N/A * <code>TextLayout</code> for a range of characters. It also provides
0N/A * methods for incremental editing of paragraphs.
0N/A * <p>
0N/A * A <code>TextMeasurer</code> object is constructed with an
0N/A * {@link java.text.AttributedCharacterIterator AttributedCharacterIterator}
0N/A * representing a single paragraph of text. The value returned by the
0N/A * {@link AttributedCharacterIterator#getBeginIndex() getBeginIndex}
0N/A * method of <code>AttributedCharacterIterator</code>
0N/A * defines the absolute index of the first character. The value
0N/A * returned by the
0N/A * {@link AttributedCharacterIterator#getEndIndex() getEndIndex}
0N/A * method of <code>AttributedCharacterIterator</code> defines the index
0N/A * past the last character. These values define the range of indexes to
0N/A * use in calls to the <code>TextMeasurer</code>. For example, calls to
0N/A * get the advance of a range of text or the line break of a range of text
0N/A * must use indexes between the beginning and end index values. Calls to
0N/A * {@link #insertChar(java.text.AttributedCharacterIterator, int) insertChar}
0N/A * and
0N/A * {@link #deleteChar(java.text.AttributedCharacterIterator, int) deleteChar}
0N/A * reset the <code>TextMeasurer</code> to use the beginning index and end
0N/A * index of the <code>AttributedCharacterIterator</code> passed in those calls.
0N/A * <p>
0N/A * Most clients will use the more convenient <code>LineBreakMeasurer</code>,
0N/A * which implements the standard line break policy (placing as many words
0N/A * as will fit on each line).
0N/A *
0N/A * @author John Raley
0N/A * @see LineBreakMeasurer
0N/A * @since 1.3
0N/A */
0N/A
0N/Apublic final class TextMeasurer implements Cloneable {
0N/A
0N/A // Number of lines to format to.
0N/A private static float EST_LINES = (float) 2.1;
0N/A
0N/A /*
0N/A static {
0N/A String s = System.getProperty("estLines");
0N/A if (s != null) {
0N/A try {
0N/A Float f = new Float(s);
0N/A EST_LINES = f.floatValue();
0N/A }
0N/A catch(NumberFormatException e) {
0N/A }
0N/A }
0N/A //System.out.println("EST_LINES="+EST_LINES);
0N/A }
0N/A */
0N/A
0N/A private FontRenderContext fFrc;
0N/A
0N/A private int fStart;
0N/A
0N/A // characters in source text
0N/A private char[] fChars;
0N/A
0N/A // Bidi for this paragraph
0N/A private Bidi fBidi;
0N/A
0N/A // Levels array for chars in this paragraph - needed to reorder
0N/A // trailing counterdirectional whitespace
0N/A private byte[] fLevels;
0N/A
0N/A // line components in logical order
0N/A private TextLineComponent[] fComponents;
0N/A
0N/A // index where components begin
0N/A private int fComponentStart;
0N/A
0N/A // index where components end
0N/A private int fComponentLimit;
0N/A
0N/A private boolean haveLayoutWindow;
0N/A
0N/A // used to find valid starting points for line components
0N/A private BreakIterator fLineBreak = null;
0N/A private CharArrayIterator charIter = null;
0N/A int layoutCount = 0;
0N/A int layoutCharCount = 0;
0N/A
0N/A // paragraph, with resolved fonts and styles
0N/A private StyledParagraph fParagraph;
0N/A
0N/A // paragraph data - same across all layouts
0N/A private boolean fIsDirectionLTR;
0N/A private byte fBaseline;
0N/A private float[] fBaselineOffsets;
0N/A private float fJustifyRatio = 1;
0N/A
0N/A /**
0N/A * Constructs a <code>TextMeasurer</code> from the source text.
0N/A * The source text should be a single entire paragraph.
0N/A * @param text the source paragraph. Cannot be null.
0N/A * @param frc the information about a graphics device which is needed
0N/A * to measure the text correctly. Cannot be null.
0N/A */
0N/A public TextMeasurer(AttributedCharacterIterator text, FontRenderContext frc) {
0N/A
0N/A fFrc = frc;
0N/A initAll(text);
0N/A }
0N/A
0N/A protected Object clone() {
0N/A TextMeasurer other;
0N/A try {
0N/A other = (TextMeasurer) super.clone();
0N/A }
0N/A catch(CloneNotSupportedException e) {
0N/A throw new Error();
0N/A }
0N/A if (fComponents != null) {
0N/A other.fComponents = (TextLineComponent[]) fComponents.clone();
0N/A }
0N/A return other;
0N/A }
0N/A
0N/A private void invalidateComponents() {
0N/A fComponentStart = fComponentLimit = fChars.length;
0N/A fComponents = null;
0N/A haveLayoutWindow = false;
0N/A }
0N/A
0N/A /**
0N/A * Initialize state, including fChars array, direction, and
0N/A * fBidi.
0N/A */
0N/A private void initAll(AttributedCharacterIterator text) {
0N/A
0N/A fStart = text.getBeginIndex();
0N/A
0N/A // extract chars
0N/A fChars = new char[text.getEndIndex() - fStart];
0N/A
0N/A int n = 0;
0N/A for (char c = text.first(); c != text.DONE; c = text.next()) {
0N/A fChars[n++] = c;
0N/A }
0N/A
0N/A text.first();
0N/A
0N/A fBidi = new Bidi(text);
0N/A if (fBidi.isLeftToRight()) {
0N/A fBidi = null;
0N/A }
0N/A
0N/A text.first();
0N/A Map paragraphAttrs = text.getAttributes();
0N/A NumericShaper shaper = AttributeValues.getNumericShaping(paragraphAttrs);
0N/A if (shaper != null) {
0N/A shaper.shape(fChars, 0, fChars.length);
0N/A }
0N/A
0N/A fParagraph = new StyledParagraph(text, fChars);
0N/A
0N/A // set paragraph attributes
0N/A {
0N/A // If there's an embedded graphic at the start of the
0N/A // paragraph, look for the first non-graphic character
0N/A // and use it and its font to initialize the paragraph.
0N/A // If not, use the first graphic to initialize.
0N/A fJustifyRatio = AttributeValues.getJustification(paragraphAttrs);
0N/A
0N/A boolean haveFont = TextLine.advanceToFirstFont(text);
0N/A
0N/A if (haveFont) {
0N/A Font defaultFont = TextLine.getFontAtCurrentPos(text);
0N/A int charsStart = text.getIndex() - text.getBeginIndex();
0N/A LineMetrics lm = defaultFont.getLineMetrics(fChars, charsStart, charsStart+1, fFrc);
0N/A fBaseline = (byte) lm.getBaselineIndex();
0N/A fBaselineOffsets = lm.getBaselineOffsets();
0N/A }
0N/A else {
0N/A // hmmm what to do here? Just try to supply reasonable
0N/A // values I guess.
0N/A
0N/A GraphicAttribute graphic = (GraphicAttribute)
0N/A paragraphAttrs.get(TextAttribute.CHAR_REPLACEMENT);
0N/A fBaseline = TextLayout.getBaselineFromGraphic(graphic);
0N/A Font dummyFont = new Font(new Hashtable(5, (float)0.9));
0N/A LineMetrics lm = dummyFont.getLineMetrics(" ", 0, 1, fFrc);
0N/A fBaselineOffsets = lm.getBaselineOffsets();
0N/A }
0N/A fBaselineOffsets = TextLine.getNormalizedOffsets(fBaselineOffsets, fBaseline);
0N/A }
0N/A
0N/A invalidateComponents();
0N/A }
0N/A
0N/A /**
0N/A * Generate components for the paragraph. fChars, fBidi should have been
0N/A * initialized already.
0N/A */
0N/A private void generateComponents(int startingAt, int endingAt) {
0N/A
0N/A if (collectStats) {
0N/A formattedChars += (endingAt-startingAt);
0N/A }
0N/A int layoutFlags = 0; // no extra info yet, bidi determines run and line direction
0N/A TextLabelFactory factory = new TextLabelFactory(fFrc, fChars, fBidi, layoutFlags);
0N/A
0N/A int[] charsLtoV = null;
0N/A
0N/A if (fBidi != null) {
0N/A fLevels = BidiUtils.getLevels(fBidi);
0N/A int[] charsVtoL = BidiUtils.createVisualToLogicalMap(fLevels);
0N/A charsLtoV = BidiUtils.createInverseMap(charsVtoL);
0N/A fIsDirectionLTR = fBidi.baseIsLeftToRight();
0N/A }
0N/A else {
0N/A fLevels = null;
0N/A fIsDirectionLTR = true;
0N/A }
0N/A
0N/A try {
0N/A fComponents = TextLine.getComponents(
0N/A fParagraph, fChars, startingAt, endingAt, charsLtoV, fLevels, factory);
0N/A }
0N/A catch(IllegalArgumentException e) {
0N/A System.out.println("startingAt="+startingAt+"; endingAt="+endingAt);
0N/A System.out.println("fComponentLimit="+fComponentLimit);
0N/A throw e;
0N/A }
0N/A
0N/A fComponentStart = startingAt;
0N/A fComponentLimit = endingAt;
0N/A //debugFormatCount += (endingAt-startingAt);
0N/A }
0N/A
0N/A private int calcLineBreak(final int pos, final float maxAdvance) {
0N/A
0N/A // either of these statements removes the bug:
0N/A //generateComponents(0, fChars.length);
0N/A //generateComponents(pos, fChars.length);
0N/A
0N/A int startPos = pos;
0N/A float width = maxAdvance;
0N/A
0N/A int tlcIndex;
0N/A int tlcStart = fComponentStart;
0N/A
0N/A for (tlcIndex = 0; tlcIndex < fComponents.length; tlcIndex++) {
0N/A int gaLimit = tlcStart + fComponents[tlcIndex].getNumCharacters();
0N/A if (gaLimit > startPos) {
0N/A break;
0N/A }
0N/A else {
0N/A tlcStart = gaLimit;
0N/A }
0N/A }
0N/A
0N/A // tlcStart is now the start of the tlc at tlcIndex
0N/A
0N/A for (; tlcIndex < fComponents.length; tlcIndex++) {
0N/A
0N/A TextLineComponent tlc = fComponents[tlcIndex];
0N/A int numCharsInGa = tlc.getNumCharacters();
0N/A
0N/A int lineBreak = tlc.getLineBreakIndex(startPos - tlcStart, width);
0N/A if (lineBreak == numCharsInGa && tlcIndex < fComponents.length) {
0N/A width -= tlc.getAdvanceBetween(startPos - tlcStart, lineBreak);
0N/A tlcStart += numCharsInGa;
0N/A startPos = tlcStart;
0N/A }
0N/A else {
0N/A return tlcStart + lineBreak;
0N/A }
0N/A }
0N/A
0N/A if (fComponentLimit < fChars.length) {
0N/A // format more text and try again
0N/A //if (haveLayoutWindow) {
0N/A // outOfWindow++;
0N/A //}
0N/A
0N/A generateComponents(pos, fChars.length);
0N/A return calcLineBreak(pos, maxAdvance);
0N/A }
0N/A
0N/A return fChars.length;
0N/A }
0N/A
0N/A /**
0N/A * According to the Unicode Bidirectional Behavior specification
0N/A * (Unicode Standard 2.0, section 3.11), whitespace at the ends
0N/A * of lines which would naturally flow against the base direction
0N/A * must be made to flow with the line direction, and moved to the
0N/A * end of the line. This method returns the start of the sequence
0N/A * of trailing whitespace characters to move to the end of a
0N/A * line taken from the given range.
0N/A */
0N/A private int trailingCdWhitespaceStart(int startPos, int limitPos) {
0N/A
0N/A if (fLevels != null) {
0N/A // Back up over counterdirectional whitespace
0N/A final byte baseLevel = (byte) (fIsDirectionLTR? 0 : 1);
0N/A for (int cdWsStart = limitPos; --cdWsStart >= startPos;) {
0N/A if ((fLevels[cdWsStart] % 2) == baseLevel ||
0N/A Character.getDirectionality(fChars[cdWsStart]) != Character.DIRECTIONALITY_WHITESPACE) {
0N/A return ++cdWsStart;
0N/A }
0N/A }
0N/A }
0N/A
0N/A return startPos;
0N/A }
0N/A
0N/A private TextLineComponent[] makeComponentsOnRange(int startPos,
0N/A int limitPos) {
0N/A
0N/A // sigh I really hate to do this here since it's part of the
0N/A // bidi algorithm.
0N/A // cdWsStart is the start of the trailing counterdirectional
0N/A // whitespace
0N/A final int cdWsStart = trailingCdWhitespaceStart(startPos, limitPos);
0N/A
0N/A int tlcIndex;
0N/A int tlcStart = fComponentStart;
0N/A
0N/A for (tlcIndex = 0; tlcIndex < fComponents.length; tlcIndex++) {
0N/A int gaLimit = tlcStart + fComponents[tlcIndex].getNumCharacters();
0N/A if (gaLimit > startPos) {
0N/A break;
0N/A }
0N/A else {
0N/A tlcStart = gaLimit;
0N/A }
0N/A }
0N/A
0N/A // tlcStart is now the start of the tlc at tlcIndex
0N/A
0N/A int componentCount;
0N/A {
0N/A boolean split = false;
0N/A int compStart = tlcStart;
0N/A int lim=tlcIndex;
0N/A for (boolean cont=true; cont; lim++) {
0N/A int gaLimit = compStart + fComponents[lim].getNumCharacters();
0N/A if (cdWsStart > Math.max(compStart, startPos)
0N/A && cdWsStart < Math.min(gaLimit, limitPos)) {
0N/A split = true;
0N/A }
0N/A if (gaLimit >= limitPos) {
0N/A cont=false;
0N/A }
0N/A else {
0N/A compStart = gaLimit;
0N/A }
0N/A }
0N/A componentCount = lim-tlcIndex;
0N/A if (split) {
0N/A componentCount++;
0N/A }
0N/A }
0N/A
0N/A TextLineComponent[] components = new TextLineComponent[componentCount];
0N/A int newCompIndex = 0;
0N/A int linePos = startPos;
0N/A
0N/A int breakPt = cdWsStart;
0N/A
0N/A int subsetFlag;
0N/A if (breakPt == startPos) {
0N/A subsetFlag = fIsDirectionLTR? TextLineComponent.LEFT_TO_RIGHT :
0N/A TextLineComponent.RIGHT_TO_LEFT;
0N/A breakPt = limitPos;
0N/A }
0N/A else {
0N/A subsetFlag = TextLineComponent.UNCHANGED;
0N/A }
0N/A
0N/A while (linePos < limitPos) {
0N/A
0N/A int compLength = fComponents[tlcIndex].getNumCharacters();
0N/A int tlcLimit = tlcStart + compLength;
0N/A
0N/A int start = Math.max(linePos, tlcStart);
0N/A int limit = Math.min(breakPt, tlcLimit);
0N/A
0N/A components[newCompIndex++] = fComponents[tlcIndex].getSubset(
0N/A start-tlcStart,
0N/A limit-tlcStart,
0N/A subsetFlag);
0N/A linePos += (limit-start);
0N/A if (linePos == breakPt) {
0N/A breakPt = limitPos;
0N/A subsetFlag = fIsDirectionLTR? TextLineComponent.LEFT_TO_RIGHT :
0N/A TextLineComponent.RIGHT_TO_LEFT;
0N/A }
0N/A if (linePos == tlcLimit) {
0N/A tlcIndex++;
0N/A tlcStart = tlcLimit;
0N/A }
0N/A }
0N/A
0N/A return components;
0N/A }
0N/A
0N/A private TextLine makeTextLineOnRange(int startPos, int limitPos) {
0N/A
0N/A int[] charsLtoV = null;
0N/A byte[] charLevels = null;
0N/A
0N/A if (fBidi != null) {
0N/A Bidi lineBidi = fBidi.createLineBidi(startPos, limitPos);
0N/A charLevels = BidiUtils.getLevels(lineBidi);
0N/A int[] charsVtoL = BidiUtils.createVisualToLogicalMap(charLevels);
0N/A charsLtoV = BidiUtils.createInverseMap(charsVtoL);
0N/A }
0N/A
0N/A TextLineComponent[] components = makeComponentsOnRange(startPos, limitPos);
0N/A
0N/A return new TextLine(fFrc,
0N/A components,
0N/A fBaselineOffsets,
0N/A fChars,
0N/A startPos,
0N/A limitPos,
0N/A charsLtoV,
0N/A charLevels,
0N/A fIsDirectionLTR);
0N/A
0N/A }
0N/A
0N/A private void ensureComponents(int start, int limit) {
0N/A
0N/A if (start < fComponentStart || limit > fComponentLimit) {
0N/A generateComponents(start, limit);
0N/A }
0N/A }
0N/A
0N/A private void makeLayoutWindow(int localStart) {
0N/A
0N/A int compStart = localStart;
0N/A int compLimit = fChars.length;
0N/A
0N/A // If we've already gone past the layout window, format to end of paragraph
0N/A if (layoutCount > 0 && !haveLayoutWindow) {
0N/A float avgLineLength = Math.max(layoutCharCount / layoutCount, 1);
0N/A compLimit = Math.min(localStart + (int)(avgLineLength*EST_LINES), fChars.length);
0N/A }
0N/A
0N/A if (localStart > 0 || compLimit < fChars.length) {
0N/A if (charIter == null) {
0N/A charIter = new CharArrayIterator(fChars);
0N/A }
0N/A else {
0N/A charIter.reset(fChars);
0N/A }
0N/A if (fLineBreak == null) {
0N/A fLineBreak = BreakIterator.getLineInstance();
0N/A }
0N/A fLineBreak.setText(charIter);
0N/A if (localStart > 0) {
0N/A if (!fLineBreak.isBoundary(localStart)) {
0N/A compStart = fLineBreak.preceding(localStart);
0N/A }
0N/A }
0N/A if (compLimit < fChars.length) {
0N/A if (!fLineBreak.isBoundary(compLimit)) {
0N/A compLimit = fLineBreak.following(compLimit);
0N/A }
0N/A }
0N/A }
0N/A
0N/A ensureComponents(compStart, compLimit);
0N/A haveLayoutWindow = true;
0N/A }
0N/A
0N/A /**
0N/A * Returns the index of the first character which will not fit on
0N/A * on a line beginning at <code>start</code> and possible
0N/A * measuring up to <code>maxAdvance</code> in graphical width.
0N/A *
0N/A * @param start the character index at which to start measuring.
0N/A * <code>start</code> is an absolute index, not relative to the
0N/A * start of the paragraph
0N/A * @param maxAdvance the graphical width in which the line must fit
0N/A * @return the index after the last character that will fit
0N/A * on a line beginning at <code>start</code>, which is not longer
0N/A * than <code>maxAdvance</code> in graphical width
0N/A * @throws IllegalArgumentException if <code>start</code> is
0N/A * less than the beginning of the paragraph.
0N/A */
0N/A public int getLineBreakIndex(int start, float maxAdvance) {
0N/A
0N/A int localStart = start - fStart;
0N/A
0N/A if (!haveLayoutWindow ||
0N/A localStart < fComponentStart ||
0N/A localStart >= fComponentLimit) {
0N/A makeLayoutWindow(localStart);
0N/A }
0N/A
0N/A return calcLineBreak(localStart, maxAdvance) + fStart;
0N/A }
0N/A
0N/A /**
0N/A * Returns the graphical width of a line beginning at <code>start</code>
0N/A * and including characters up to <code>limit</code>.
0N/A * <code>start</code> and <code>limit</code> are absolute indices,
0N/A * not relative to the start of the paragraph.
0N/A *
0N/A * @param start the character index at which to start measuring
0N/A * @param limit the character index at which to stop measuring
0N/A * @return the graphical width of a line beginning at <code>start</code>
0N/A * and including characters up to <code>limit</code>
0N/A * @throws IndexOutOfBoundsException if <code>limit</code> is less
0N/A * than <code>start</code>
0N/A * @throws IllegalArgumentException if <code>start</code> or
0N/A * <code>limit</code> is not between the beginning of
0N/A * the paragraph and the end of the paragraph.
0N/A */
0N/A public float getAdvanceBetween(int start, int limit) {
0N/A
0N/A int localStart = start - fStart;
0N/A int localLimit = limit - fStart;
0N/A
0N/A ensureComponents(localStart, localLimit);
0N/A TextLine line = makeTextLineOnRange(localStart, localLimit);
0N/A return line.getMetrics().advance;
0N/A // could cache line in case getLayout is called with same start, limit
0N/A }
0N/A
0N/A /**
0N/A * Returns a <code>TextLayout</code> on the given character range.
0N/A *
0N/A * @param start the index of the first character
0N/A * @param limit the index after the last character. Must be greater
0N/A * than <code>start</code>
0N/A * @return a <code>TextLayout</code> for the characters beginning at
0N/A * <code>start</code> up to (but not including) <code>limit</code>
0N/A * @throws IndexOutOfBoundsException if <code>limit</code> is less
0N/A * than <code>start</code>
0N/A * @throws IllegalArgumentException if <code>start</code> or
0N/A * <code>limit</code> is not between the beginning of
0N/A * the paragraph and the end of the paragraph.
0N/A */
0N/A public TextLayout getLayout(int start, int limit) {
0N/A
0N/A int localStart = start - fStart;
0N/A int localLimit = limit - fStart;
0N/A
0N/A ensureComponents(localStart, localLimit);
0N/A TextLine textLine = makeTextLineOnRange(localStart, localLimit);
0N/A
0N/A if (localLimit < fChars.length) {
0N/A layoutCharCount += limit-start;
0N/A layoutCount++;
0N/A }
0N/A
0N/A return new TextLayout(textLine,
0N/A fBaseline,
0N/A fBaselineOffsets,
0N/A fJustifyRatio);
0N/A }
0N/A
0N/A private int formattedChars = 0;
0N/A private static boolean wantStats = false;/*"true".equals(System.getProperty("collectStats"));*/
0N/A private boolean collectStats = false;
0N/A
0N/A private void printStats() {
0N/A System.out.println("formattedChars: " + formattedChars);
0N/A //formattedChars = 0;
0N/A collectStats = false;
0N/A }
0N/A
0N/A /**
0N/A * Updates the <code>TextMeasurer</code> after a single character has
0N/A * been inserted
0N/A * into the paragraph currently represented by this
0N/A * <code>TextMeasurer</code>. After this call, this
0N/A * <code>TextMeasurer</code> is equivalent to a new
0N/A * <code>TextMeasurer</code> created from the text; however, it will
0N/A * usually be more efficient to update an existing
0N/A * <code>TextMeasurer</code> than to create a new one from scratch.
0N/A *
0N/A * @param newParagraph the text of the paragraph after performing
0N/A * the insertion. Cannot be null.
0N/A * @param insertPos the position in the text where the character was
0N/A * inserted. Must not be less than the start of
0N/A * <code>newParagraph</code>, and must be less than the end of
0N/A * <code>newParagraph</code>.
0N/A * @throws IndexOutOfBoundsException if <code>insertPos</code> is less
0N/A * than the start of <code>newParagraph</code> or greater than
0N/A * or equal to the end of <code>newParagraph</code>
0N/A * @throws NullPointerException if <code>newParagraph</code> is
0N/A * <code>null</code>
0N/A */
0N/A public void insertChar(AttributedCharacterIterator newParagraph, int insertPos) {
0N/A
0N/A if (collectStats) {
0N/A printStats();
0N/A }
0N/A if (wantStats) {
0N/A collectStats = true;
0N/A }
0N/A
0N/A fStart = newParagraph.getBeginIndex();
0N/A int end = newParagraph.getEndIndex();
0N/A if (end - fStart != fChars.length+1) {
0N/A initAll(newParagraph);
0N/A }
0N/A
0N/A char[] newChars = new char[end-fStart];
0N/A int newCharIndex = insertPos - fStart;
0N/A System.arraycopy(fChars, 0, newChars, 0, newCharIndex);
0N/A
0N/A char newChar = newParagraph.setIndex(insertPos);
0N/A newChars[newCharIndex] = newChar;
0N/A System.arraycopy(fChars,
0N/A newCharIndex,
0N/A newChars,
0N/A newCharIndex+1,
0N/A end-insertPos-1);
0N/A fChars = newChars;
0N/A
0N/A if (fBidi != null || Bidi.requiresBidi(newChars, newCharIndex, newCharIndex + 1) ||
0N/A newParagraph.getAttribute(TextAttribute.BIDI_EMBEDDING) != null) {
0N/A
0N/A fBidi = new Bidi(newParagraph);
0N/A if (fBidi.isLeftToRight()) {
0N/A fBidi = null;
0N/A }
0N/A }
0N/A
0N/A fParagraph = StyledParagraph.insertChar(newParagraph,
0N/A fChars,
0N/A insertPos,
0N/A fParagraph);
0N/A invalidateComponents();
0N/A }
0N/A
0N/A /**
0N/A * Updates the <code>TextMeasurer</code> after a single character has
0N/A * been deleted
0N/A * from the paragraph currently represented by this
0N/A * <code>TextMeasurer</code>. After this call, this
0N/A * <code>TextMeasurer</code> is equivalent to a new <code>TextMeasurer</code>
0N/A * created from the text; however, it will usually be more efficient
0N/A * to update an existing <code>TextMeasurer</code> than to create a new one
0N/A * from scratch.
0N/A *
0N/A * @param newParagraph the text of the paragraph after performing
0N/A * the deletion. Cannot be null.
0N/A * @param deletePos the position in the text where the character was removed.
0N/A * Must not be less than
0N/A * the start of <code>newParagraph</code>, and must not be greater than the
0N/A * end of <code>newParagraph</code>.
0N/A * @throws IndexOutOfBoundsException if <code>deletePos</code> is
0N/A * less than the start of <code>newParagraph</code> or greater
0N/A * than the end of <code>newParagraph</code>
0N/A * @throws NullPointerException if <code>newParagraph</code> is
0N/A * <code>null</code>
0N/A */
0N/A public void deleteChar(AttributedCharacterIterator newParagraph, int deletePos) {
0N/A
0N/A fStart = newParagraph.getBeginIndex();
0N/A int end = newParagraph.getEndIndex();
0N/A if (end - fStart != fChars.length-1) {
0N/A initAll(newParagraph);
0N/A }
0N/A
0N/A char[] newChars = new char[end-fStart];
0N/A int changedIndex = deletePos-fStart;
0N/A
0N/A System.arraycopy(fChars, 0, newChars, 0, deletePos-fStart);
0N/A System.arraycopy(fChars, changedIndex+1, newChars, changedIndex, end-deletePos);
0N/A fChars = newChars;
0N/A
0N/A if (fBidi != null) {
0N/A fBidi = new Bidi(newParagraph);
0N/A if (fBidi.isLeftToRight()) {
0N/A fBidi = null;
0N/A }
0N/A }
0N/A
0N/A fParagraph = StyledParagraph.deleteChar(newParagraph,
0N/A fChars,
0N/A deletePos,
0N/A fParagraph);
0N/A invalidateComponents();
0N/A }
0N/A
0N/A /**
0N/A * NOTE: This method is only for LineBreakMeasurer's use. It is package-
0N/A * private because it returns internal data.
0N/A */
0N/A char[] getChars() {
0N/A
0N/A return fChars;
0N/A }
0N/A}