📄 textlayout.java
字号:
/* * @(#)TextLayout.java 1.88 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.Color;import java.awt.Font;import java.awt.Graphics2D;import java.awt.Shape;import java.awt.font.NumericShaper;import java.awt.geom.AffineTransform;import java.awt.geom.GeneralPath;import java.awt.geom.Point2D;import java.awt.geom.Rectangle2D;import java.text.AttributedString;import java.text.AttributedCharacterIterator;import java.util.Map;import java.util.HashMap;import java.util.Hashtable;import sun.awt.font.AdvanceCache;import sun.awt.font.Decoration;import sun.awt.font.FontResolver;import sun.awt.font.NativeFontWrapper;import sun.java2d.SunGraphicsEnvironment;/** * * <code>TextLayout</code> is an immutable graphical representation of styled * character data. * <p> * It provides the following capabilities: * <ul> * <li>implicit bidirectional analysis and reordering, * <li>cursor positioning and movement, including split cursors for * mixed directional text, * <li>highlighting, including both logical and visual highlighting * for mixed directional text, * <li>multiple baselines (roman, hanging, and centered), * <li>hit testing, * <li>justification, * <li>default font substitution, * <li>metric information such as ascent, descent, and advance, and * <li>rendering * </ul> * <p> * A <code>TextLayout</code> object can be rendered using * its <code>draw</code> method. * <p> * <code>TextLayout</code> can be constructed either directly or through * the use of a {@link LineBreakMeasurer}. When constructed directly, the * source text represents a single paragraph. <code>LineBreakMeasurer</code> * allows styled text to be broken into lines that fit within a particular * width. See the <code>LineBreakMeasurer</code> documentation for more * information. * <p> * <code>TextLayout</code> construction logically proceeds as follows: * <ul> * <li>paragraph attributes are extracted and examined, * <li>text is analyzed for bidirectional reordering, and reordering * information is computed if needed, * <li>text is segmented into style runs * <li>fonts are chosen for style runs, first by using a font if the * attribute {@link TextAttribute#FONT} is present, otherwise by computing * a default font using the attributes that have been defined * <li>if text is on multiple baselines, the runs or subruns are further * broken into subruns sharing a common baseline, * <li>glyphvectors are generated for each run using the chosen font, * <li>final bidirectional reordering is performed on the glyphvectors * </ul> * <p> * All graphical information returned from a <code>TextLayout</code> * object's methods is relative to the origin of the * <code>TextLayout</code>, which is the intersection of the * <code>TextLayout</code> object's baseline with its left edge. Also, * coordinates passed into a <code>TextLayout</code> object's methods * are assumed to be relative to the <code>TextLayout</code> object's * origin. Clients usually need to translate between a * <code>TextLayout</code> object's coordinate system and the coordinate * system in another object (such as a * {@link java.awt.Graphics Graphics} object). * <p> * <code>TextLayout</code> objects are constructed from styled text, * but they do not retain a reference to their source text. Thus, * changes in the text previously used to generate a <code>TextLayout</code> * do not affect the <code>TextLayout</code>. * <p> * Three methods on a <code>TextLayout</code> object * (<code>getNextRightHit</code>, <code>getNextLeftHit</code>, and * <code>hitTestChar</code>) return instances of {@link TextHitInfo}. * The offsets contained in these <code>TextHitInfo</code> objects * are relative to the start of the <code>TextLayout</code>, <b>not</b> * to the text used to create the <code>TextLayout</code>. Similarly, * <code>TextLayout</code> methods that accept <code>TextHitInfo</code> * instances as parameters expect the <code>TextHitInfo</code> object's * offsets to be relative to the <code>TextLayout</code>, not to any * underlying text storage model. * <p> * <strong>Examples</strong>:<p> * Constructing and drawing a <code>TextLayout</code> and its bounding * rectangle: * <blockquote><pre> * Graphics2D g = ...; * Point2D loc = ...; * Font font = Font.getFont("Helvetica-bold-italic"); * FontRenderContext frc = g.getFontRenderContext(); * TextLayout layout = new TextLayout("This is a string", font, frc); * layout.draw(g, (float)loc.getX(), (float)loc.getY()); * * Rectangle2D bounds = layout.getBounds(); * bounds.setRect(bounds.getX()+loc.getX(), * bounds.getY()+loc.getY(), * bounds.getWidth(), * bounds.getHeight()); * g.draw(bounds); * </pre> * </blockquote> * <p> * Hit-testing a <code>TextLayout</code> (determining which character is at * a particular graphical location): * <blockquote><pre> * Point2D click = ...; * TextHitInfo hit = layout.hitTestChar( * (float) (click.getX() - loc.getX()), * (float) (click.getY() - loc.getY())); * </pre> * </blockquote> * <p> * Responding to a right-arrow key press: * <blockquote><pre> * int insertionIndex = ...; * TextHitInfo next = layout.getNextRightHit(insertionIndex); * if (next != null) { * // translate graphics to origin of layout on screen * g.translate(loc.getX(), loc.getY()); * Shape[] carets = layout.getCaretShapes(next.getInsertionIndex()); * g.draw(carets[0]); * if (carets[1] != null) { * g.draw(carets[1]); * } * } * </pre></blockquote> * <p> * Drawing a selection range corresponding to a substring in the source text. * The selected area may not be visually contiguous: * <blockquote><pre> * // selStart, selLimit should be relative to the layout, * // not to the source text * * int selStart = ..., selLimit = ...; * Color selectionColor = ...; * Shape selection = layout.getLogicalHighlightShape(selStart, selLimit); * // selection may consist of disjoint areas * // graphics is assumed to be tranlated to origin of layout * g.setColor(selectionColor); * g.fill(selection); * </pre></blockquote> * <p> * Drawing a visually contiguous selection range. The selection range may * correspond to more than one substring in the source text. The ranges of * the corresponding source text substrings can be obtained with * <code>getLogicalRangesForVisualSelection()</code>: * <blockquote><pre> * TextHitInfo selStart = ..., selLimit = ...; * Shape selection = layout.getVisualHighlightShape(selStart, selLimit); * g.setColor(selectionColor); * g.fill(selection); * int[] ranges = getLogicalRangesForVisualSelection(selStart, selLimit); * // ranges[0], ranges[1] is the first selection range, * // ranges[2], ranges[3] is the second selection range, etc. * </pre></blockquote> * <p> * @see LineBreakMeasurer * @see TextAttribute * @see TextHitInfo */public final class TextLayout implements Cloneable { private int characterCount; private boolean isVerticalLine = false; private byte baseline; private float[] baselineOffsets; // why have these ? private TextLine textLine; // cached values computed from GlyphSets and set info: // all are recomputed from scratch in buildCache() private TextLine.TextLineMetrics lineMetrics = null; private float visibleAdvance; private int hashCodeCache; /** * temporary optimization */ static class OptInfo implements Decoration.Label { private static final float MAGIC_ADVANCE = -12345.67f; // cache of required information for TextLine construction private FontRenderContext frc; private char[] chars; private Font font; private LineMetrics metrics; private Map attrs; // deferred initialization private float advance; private Rectangle2D vb; private Decoration decoration; private String str; private OptInfo(FontRenderContext frc, char[] chars, Font font, LineMetrics metrics, Map attrs) { this.frc = frc; this.chars = chars; this.font = font; this.metrics = metrics; this.attrs = attrs; if (attrs != null) { this.attrs = new HashMap(attrs); // sigh, need to clone since might change... } this.advance = MAGIC_ADVANCE; } TextLine createTextLine() { return TextLine.fastCreateTextLine(frc, chars, font, metrics, attrs); } float getAdvance() { if (advance == MAGIC_ADVANCE) { AdvanceCache adv = AdvanceCache.get(font, frc); advance = adv.getAdvance(chars, 0, chars.length); // we pretested the chars array so no exception here } return advance; } // Decoration.Label reqd. public LineMetrics getLineMetrics() { return metrics; } // Decoration.Label reqd. public Rectangle2D getLogicalBounds() { return new Rectangle2D.Float(0, -metrics.getAscent(), getAdvance(), metrics.getHeight()); } // Decoration.Label reqd. public void handleDraw(Graphics2D g2d, float x, float y) { if (str == null) { str = new String(chars, 0, chars.length); } g2d.drawString(str, x , y); } // Decoration.Label reqd. public Rectangle2D handleGetCharVisualBounds(int index) { // not used throw new InternalError(); } // Decoration.Label reqd. public Rectangle2D handleGetVisualBounds() { AdvanceCache adv = AdvanceCache.get(font, frc); return adv.getVisualBounds(chars, 0, chars.length); } // Decoration.Label reqd. public Shape handleGetOutline(float x, float y) { // not used throw new InternalError(); } // if we could successfully draw, then return true boolean draw(Graphics2D g2d, float x, float y) { // If the frc differs from the graphics frc, we punt to TextLayout because the // metrics might be different... if (g2d.getFontRenderContext().equals(frc)) { Font oldFont = g2d.getFont(); g2d.setFont(font); getDecoration().drawTextAndDecorations(this, g2d, x, y); g2d.setFont(oldFont); return true; } return false; } Rectangle2D getVisualBounds() { if (vb == null) { vb = getDecoration().getVisualBounds(this); } return (Rectangle2D)vb.clone(); } Decoration getDecoration() { if (decoration == null) { if (attrs == null) { decoration = Decoration.getDecoration(null); } else { decoration = Decoration.getDecoration(StyledParagraph.addInputMethodAttrs(attrs)); } } return decoration; } static OptInfo create(FontRenderContext frc, char[] chars, Font font, LineMetrics metrics, Map attrs) { // Preflight text to make sure advance cache supports it, otherwise it would throw an exception. // We also need to preflight to make sure we don't require layout. If we limit optimizations to // latin-1 we handle both cases. We could add an additional check for Japanese since currently // it doesn't require layout and the advance cache would be simple, but right now we don't. if (!font.isTransformed() && AdvanceCache.supportsText(chars)) { if (attrs == null || attrs.get(TextAttribute.CHAR_REPLACEMENT) == null) { return new OptInfo(frc, chars, font, metrics, attrs); } } return null; } } private OptInfo optInfo; /* * TextLayouts are supposedly immutable. If you mutate a TextLayout under * the covers (like the justification code does) you'll need to set this * back to false. Could be replaced with textLine != null <--> cacheIsValid. */ private boolean cacheIsValid = false; // This value is obtained from an attribute, and constrained to the // interval [0,1]. If 0, the layout cannot be justified. private float justifyRatio; // If a layout is produced by justification, then that layout // cannot be justified. To enforce this constraint the
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -