📄 textmeasurer.java
字号:
/* * @(#)TextMeasurer.java 1.37 03/01/23 * * Copyright 2003 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. *//* * (C) Copyright Taligent, Inc. 1996 - 1997, All Rights Reserved * (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved * * The original version of this source code and documentation is * copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary * of IBM. These materials are provided under terms of a License * Agreement between Taligent and Sun. This technology is protected * by multiple US and International patents. * * This notice and attribution to Taligent may not be removed. * Taligent is a registered trademark of Taligent, Inc. * */package java.awt.font;import java.awt.Font;import java.text.AttributedCharacterIterator;import java.text.AttributedString;import java.text.Bidi;import java.text.BreakIterator;import java.text.CharacterIterator;import java.awt.font.FontRenderContext;import java.util.Hashtable;import java.util.Map;import sun.awt.font.BidiUtils;import sun.awt.font.TextLineComponent;import sun.awt.font.TextLabelFactory;import sun.awt.font.FontResolver;/** * The <code>TextMeasurer</code> class provides the primitive operations * needed for line break: measuring up to a given advance, determining the * advance of a range of characters, and generating a * <code>TextLayout</code> for a range of characters. It also provides * methods for incremental editing of paragraphs. * <p> * A <code>TextMeasurer</code> object is constructed with an * {@link java.text.AttributedCharacterIterator AttributedCharacterIterator} * representing a single paragraph of text. The value returned by the * {@link AttributedCharacterIterator#getBeginIndex() getBeginIndex} * method of <code>AttributedCharacterIterator</code> * defines the absolute index of the first character. The value * returned by the * {@link AttributedCharacterIterator#getEndIndex() getEndIndex} * method of <code>AttributedCharacterIterator</code> defines the index * past the last character. These values define the range of indexes to * use in calls to the <code>TextMeasurer</code>. For example, calls to * get the advance of a range of text or the line break of a range of text * must use indexes between the beginning and end index values. Calls to * {@link #insertChar(java.text.AttributedCharacterIterator, int) insertChar} * and * {@link #deleteChar(java.text.AttributedCharacterIterator, int) deleteChar} * reset the <code>TextMeasurer</code> to use the beginning index and end * index of the <code>AttributedCharacterIterator</code> passed in those calls. * <p> * Most clients will use the more convenient <code>LineBreakMeasurer</code>, * which implements the standard line break policy (placing as many words * as will fit on each line). * * @author John Raley * @version 1.31, 04/20/01 * @see LineBreakMeasurer * @since 1.3 */public final class TextMeasurer implements Cloneable { // Number of lines to format to. private static float EST_LINES = (float) 2.1; /* static { String s = System.getProperty("estLines"); if (s != null) { try { Float f = new Float(s); EST_LINES = f.floatValue(); } catch(NumberFormatException e) { } } //System.out.println("EST_LINES="+EST_LINES); } */ private FontRenderContext fFrc; private int fStart; // characters in source text private char[] fChars; // Bidi for this paragraph private Bidi fBidi; // Levels array for chars in this paragraph - needed to reorder // trailing counterdirectional whitespace private byte[] fLevels; // line components in logical order private TextLineComponent[] fComponents; // index where components begin private int fComponentStart; // index where components end private int fComponentLimit; private boolean haveLayoutWindow; // used to find valid starting points for line components private BreakIterator fLineBreak = null; private CharArrayIterator charIter = null; int layoutCount = 0; int layoutCharCount = 0; // paragraph, with resolved fonts and styles private StyledParagraph fParagraph; // paragraph data - same across all layouts private boolean fIsDirectionLTR; private byte fBaseline; private float[] fBaselineOffsets; private float fJustifyRatio = 1; /** * Constructs a <code>TextMeasurer</code> from the source text. * The source text should be a single entire paragraph. * @param text the source paragraph. Cannot be null. * @param frc the information about a graphics device which is needed * to measure the text correctly. Cannot be null. */ public TextMeasurer(AttributedCharacterIterator text, FontRenderContext frc) { fFrc = frc; initAll(text); } protected Object clone() { TextMeasurer other; try { other = (TextMeasurer) super.clone(); } catch(CloneNotSupportedException e) { throw new Error(); } if (fComponents != null) { other.fComponents = (TextLineComponent[]) fComponents.clone(); } return other; } private void invalidateComponents() { fComponentStart = fComponentLimit = fChars.length; fComponents = null; haveLayoutWindow = false; } /** * Initialize state, including fChars array, direction, and * fBidi. */ private void initAll(AttributedCharacterIterator text) { fStart = text.getBeginIndex(); // extract chars fChars = new char[text.getEndIndex() - fStart]; int n = 0; for (char c = text.first(); c != text.DONE; c = text.next()) { fChars[n++] = c; } text.first(); fBidi = new Bidi(text); if (fBidi.isLeftToRight()) { fBidi = null; } text.first(); Map paragraphAttrs = text.getAttributes(); if (paragraphAttrs != null) { try { NumericShaper shaper = (NumericShaper)paragraphAttrs.get(TextAttribute.NUMERIC_SHAPING); if (shaper != null) { shaper.shape(fChars, 0, fChars.length); } } catch (ClassCastException e) { } } fParagraph = new StyledParagraph(text, fChars); // set paragraph attributes { // If there's an embedded graphic at the start of the // paragraph, look for the first non-graphic character // and use it and its font to initialize the paragraph. // If not, use the first graphic to initialize. fJustifyRatio = TextLine.getJustifyRatio(paragraphAttrs); boolean haveFont = TextLine.advanceToFirstFont(text); if (haveFont) { Font defaultFont = TextLine.getFontAtCurrentPos(text); int charsStart = text.getIndex() - text.getBeginIndex(); LineMetrics lm = defaultFont.getLineMetrics(fChars, charsStart, charsStart+1, fFrc); fBaseline = (byte) lm.getBaselineIndex(); fBaselineOffsets = lm.getBaselineOffsets(); } else { // hmmm what to do here? Just try to supply reasonable // values I guess. GraphicAttribute graphic = (GraphicAttribute) paragraphAttrs.get(TextAttribute.CHAR_REPLACEMENT); fBaseline = TextLayout.getBaselineFromGraphic(graphic); Font dummyFont = new Font(new Hashtable(5, (float)0.9)); LineMetrics lm = dummyFont.getLineMetrics(" ", 0, 1, fFrc); fBaselineOffsets = lm.getBaselineOffsets(); } fBaselineOffsets = TextLine.getNormalizedOffsets(fBaselineOffsets, fBaseline); } invalidateComponents(); } /** * Generate components for the paragraph. fChars, fBidi should have been * initialized already. */ private void generateComponents(int startingAt, int endingAt) { if (collectStats) { formattedChars += (endingAt-startingAt); } int layoutFlags = 0; // no extra info yet, bidi determines run and line direction TextLabelFactory factory = new TextLabelFactory(fFrc, fChars, fBidi, layoutFlags); int[] charsLtoV = null; if (fBidi != null) { fLevels = BidiUtils.getLevels(fBidi); int[] charsVtoL = BidiUtils.createVisualToLogicalMap(fLevels); charsLtoV = BidiUtils.createInverseMap(charsVtoL); fIsDirectionLTR = fBidi.baseIsLeftToRight(); } else { fLevels = null; fIsDirectionLTR = true; } try { fComponents = TextLine.getComponents( fParagraph, fChars, startingAt, endingAt, charsLtoV, fLevels, factory); } catch(IllegalArgumentException e) { System.out.println("startingAt="+startingAt+"; endingAt="+endingAt); System.out.println("fComponentLimit="+fComponentLimit); throw e; } fComponentStart = startingAt; fComponentLimit = endingAt; //debugFormatCount += (endingAt-startingAt); } private int calcLineBreak(final int pos, final float maxAdvance) { // either of these statements removes the bug: //generateComponents(0, fChars.length); //generateComponents(pos, fChars.length); int startPos = pos; float width = maxAdvance; int tlcIndex; int tlcStart = fComponentStart; for (tlcIndex = 0; tlcIndex < fComponents.length; tlcIndex++) { int gaLimit = tlcStart + fComponents[tlcIndex].getNumCharacters(); if (gaLimit > startPos) { break; } else { tlcStart = gaLimit; } } // tlcStart is now the start of the tlc at tlcIndex for (; tlcIndex < fComponents.length; tlcIndex++) { TextLineComponent tlc = fComponents[tlcIndex]; int numCharsInGa = tlc.getNumCharacters(); int lineBreak = tlc.getLineBreakIndex(startPos - tlcStart, width); if (lineBreak == numCharsInGa && tlcIndex < fComponents.length) { width -= tlc.getAdvanceBetween(startPos - tlcStart, lineBreak); tlcStart += numCharsInGa; startPos = tlcStart; } else { return tlcStart + lineBreak; } } if (fComponentLimit < fChars.length) { // format more text and try again //if (haveLayoutWindow) { // outOfWindow++; //} generateComponents(pos, fChars.length); return calcLineBreak(pos, maxAdvance); } return fChars.length; } /** * According to the Unicode Bidirectional Behavior specification * (Unicode Standard 2.0, section 3.11), whitespace at the ends * of lines which would naturally flow against the base direction * must be made to flow with the line direction, and moved to the * end of the line. This method returns the start of the sequence * of trailing whitespace characters to move to the end of a * line taken from the given range. */ private int trailingCdWhitespaceStart(int startPos, int limitPos) { if (fLevels != null) { // Back up over counterdirectional whitespace final byte baseLevel = (byte) (fIsDirectionLTR? 0 : 1); for (int cdWsStart = limitPos; --cdWsStart >= startPos;) { if ((fLevels[cdWsStart] % 2) == baseLevel || Character.getDirectionality(fChars[cdWsStart]) != Character.DIRECTIONALITY_WHITESPACE) { return ++cdWsStart; } } } return startPos; } private TextLineComponent[] makeComponentsOnRange(int startPos, int limitPos) { // sigh I really hate to do this here since it's part of the // bidi algorithm. // cdWsStart is the start of the trailing counterdirectional // whitespace final int cdWsStart = trailingCdWhitespaceStart(startPos, limitPos); int tlcIndex; int tlcStart = fComponentStart; for (tlcIndex = 0; tlcIndex < fComponents.length; tlcIndex++) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -