📄 linebreakmeasurer.java
字号:
/* * @(#)LineBreakMeasurer.java 1.22 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.text.BreakIterator;import java.text.CharacterIterator;import java.text.AttributedCharacterIterator;import java.awt.font.FontRenderContext;/** * The <code>LineBreakMeasurer</code> class allows styled text to be * broken into lines (or segments) that fit within a particular visual * advance. This is useful for clients who wish to display a paragraph of * text that fits within a specific width, called the <b>wrapping * width</b>. * <p> * <code>LineBreakMeasurer</code> is constructed with an iterator over * styled text. The iterator's range should be a single paragraph in the * text. * <code>LineBreakMeasurer</code> maintains a position in the text for the * start of the next text segment. Initially, this position is the * start of text. Paragraphs are assigned an overall direction (either * left-to-right or right-to-left) according to the bidirectional * formatting rules. All segments obtained from a paragraph have the * same direction as the paragraph. * <p> * Segments of text are obtained by calling the method * <code>nextLayout</code>, which returns a {@link TextLayout} * representing the text that fits within the wrapping width. * The <code>nextLayout</code> method moves the current position * to the end of the layout returned from <code>nextLayout</code>. * <p> * <code>LineBreakMeasurer</code> implements the most commonly used * line-breaking policy: Every word that fits within the wrapping * width is placed on the line. If the first word does not fit, then all * of the characters that fit within the wrapping width are placed on the * line. At least one character is placed on each line. * <p> * The <code>TextLayout</code> instances returned by * <code>LineBreakMeasurer</code> treat tabs like 0-width spaces. Clients * who wish to obtain tab-delimited segments for positioning should use * the overload of <code>nextLayout</code> which takes a limiting offset * in the text. * The limiting offset should be the first character after the tab. * The <code>TextLayout</code> objects returned from this method end * at the limit provided (or before, if the text between the current * position and the limit won't fit entirely within the wrapping * width). * <p> * Clients who are laying out tab-delimited text need a slightly * different line-breaking policy after the first segment has been * placed on a line. Instead of fitting partial words in the * remaining space, they should place words which don't fit in the * remaining space entirely on the next line. This change of policy * can be requested in the overload of <code>nextLayout</code> which * takes a <code>boolean</code> parameter. If this parameter is * <code>true</code>, <code>nextLayout</code> returns * <code>null</code> if the first word won't fit in * the given space. See the tab sample below. * <p> * In general, if the text used to construct the * <code>LineBreakMeasurer</code> changes, a new * <code>LineBreakMeasurer</code> must be constructed to reflect * the change. (The old <code>LineBreakMeasurer</code> continues to * function properly, but it won't be aware of the text change.) * Nevertheless, if the text change is the insertion or deletion of a * single character, an existing <code>LineBreakMeasurer</code> can be * 'updated' by calling <code>insertChar</code> or * <code>deleteChar</code>. Updating an existing * <code>LineBreakMeasurer</code> is much faster than creating a new one. * Clients who modify text based on user typing should take advantage * of these methods. * <p> * <strong>Examples</strong>:<p> * Rendering a paragraph in a component * <blockquote> * <pre> * public void paint(Graphics graphics) { * * Point2D pen = new Point2D(10, 20); * Graphics2D g2d = (Graphics2D)graphics; * FontRenderContext frc = g2d.getFontRenderContext(); * * // let styledText be an AttributedCharacterIterator containing at least * // one character * * LineBreakMeasurer measurer = new LineBreakMeasurer(styledText, frc); * float wrappingWidth = getSize().width - 15; * * while (measurer.getPosition() < fStyledText.length()) { * * TextLayout layout = measurer.nextLayout(wrappingWidth); * * pen.y += (layout.getAscent()); * float dx = layout.isLeftToRight() ? * 0 : (wrappingWidth - layout.getAdvance()); * * layout.draw(graphics, pen.x + dx, pen.y); * pen.y += layout.getDescent() + layout.getLeading(); * } * } * </pre> * </blockquote> * <p> * Rendering text with tabs. For simplicity, the overall text * direction is assumed to be left-to-right * <blockquote> * <pre> * public void paint(Graphics graphics) { * * float leftMargin = 10, rightMargin = 310; * float[] tabStops = { 100, 250 }; * * // assume styledText is an AttributedCharacterIterator, and the number * // of tabs in styledText is tabCount * * int[] tabLocations = new int[tabCount+1]; * * int i = 0; * for (char c = styledText.first(); c != styledText.DONE; c = styledText.next()) { * if (c == '\t') { * tabLocations[i++] = styledText.getIndex(); * } * } * tabLocations[tabCount] = styledText.getEndIndex() - 1; * * // Now tabLocations has an entry for every tab's offset in * // the text. For convenience, the last entry is tabLocations * // is the offset of the last character in the text. * * LineBreakMeasurer measurer = new LineBreakMeasurer(styledText); * int currentTab = 0; * float verticalPos = 20; * * while (measurer.getPosition() < styledText.getEndIndex()) { * * // Lay out and draw each line. All segments on a line * // must be computed before any drawing can occur, since * // we must know the largest ascent on the line. * // TextLayouts are computed and stored in a Vector; * // their horizontal positions are stored in a parallel * // Vector. * * // lineContainsText is true after first segment is drawn * boolean lineContainsText = false; * boolean lineComplete = false; * float maxAscent = 0, maxDescent = 0; * float horizontalPos = leftMargin; * Vector layouts = new Vector(1); * Vector penPositions = new Vector(1); * * while (!lineComplete) { * float wrappingWidth = rightMargin - horizontalPos; * TextLayout layout = * measurer.nextLayout(wrappingWidth, * tabLocations[currentTab]+1, * lineContainsText); * * // layout can be null if lineContainsText is true * if (layout != null) { * layouts.addElement(layout); * penPositions.addElement(new Float(horizontalPos)); * horizontalPos += layout.getAdvance(); * maxAscent = Math.max(maxAscent, layout.getAscent()); * maxDescent = Math.max(maxDescent, * layout.getDescent() + layout.getLeading()); * } else { * lineComplete = true; * } * * lineContainsText = true; * * if (measurer.getPosition() == tabLocations[currentTab]+1) { * currentTab++; * } * * if (measurer.getPosition() == styledText.getEndIndex()) * lineComplete = true; * else if (horizontalPos >= tabStops[tabStops.length-1]) * lineComplete = true; * * if (!lineComplete) { * // move to next tab stop * int j; * for (j=0; horizontalPos >= tabStops[j]; j++) {} * horizontalPos = tabStops[j]; * } * } * * verticalPos += maxAscent; * * Enumeration layoutEnum = layouts.elements(); * Enumeration positionEnum = penPositions.elements(); * * // now iterate through layouts and draw them * while (layoutEnum.hasMoreElements()) { * TextLayout nextLayout = (TextLayout) layoutEnum.nextElement(); * Float nextPosition = (Float) positionEnum.nextElement(); * nextLayout.draw(graphics, nextPosition.floatValue(), verticalPos); * } * * verticalPos += maxDescent; * } * } * </pre> * </blockquote> * @see TextLayout */public final class LineBreakMeasurer { private BreakIterator breakIter; private int start; private int pos; private int limit; private TextMeasurer measurer; private CharArrayIterator charIter; /** * Constructs a <code>LineBreakMeasurer</code> for the specified text. * * @param text the text for which this <code>LineBreakMeasurer</code> * produces <code>TextLayout</code> objects; the text must contain * at least one character; if the text available through * <code>iter</code> changes, further calls to this * <code>LineBreakMeasurer</code> instance are undefined (except, * in some cases, when <code>insertChar</code> or * <code>deleteChar</code> are invoked afterward - see below) * @param frc contains information about a graphics device which is * needed to measure the text correctly; * text measurements can vary slightly depending on the * device resolution, and attributes such as antialiasing; this * parameter does not specify a translation between the * <code>LineBreakMeasurer</code> and user space * @see LineBreakMeasurer#insertChar * @see LineBreakMeasurer#deleteChar */ public LineBreakMeasurer(AttributedCharacterIterator text, FontRenderContext frc) { this(text, BreakIterator.getLineInstance(), frc);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -