2753 lines · 103.1 KB · Java
RawDownload
/* * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * (C) Copyright Taligent, Inc. 1996 - 1997, All Rights Reserved * (C) Copyright IBM Corp. 1996-2003, 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.Rectangle;import java.awt.Shape;import java.awt.font.NumericShaper;import java.awt.font.TextLine.TextLineMetrics;import java.awt.geom.AffineTransform;import java.awt.geom.GeneralPath;import java.awt.geom.NoninvertibleTransformException;import java.awt.geom.Point2D;import java.awt.geom.Rectangle2D;import java.text.AttributedString;import java.text.AttributedCharacterIterator;import java.text.AttributedCharacterIterator.Attribute;import java.util.Map;import java.util.HashMap;import java.util.Hashtable;import sun.font.AttributeValues;import sun.font.CoreMetrics;import sun.font.Decoration;import sun.font.FontLineMetrics;import sun.font.FontResolver;import sun.font.GraphicComponent;import sun.font.LayoutPathImpl;import sun.text.CodePointIterator; /** * * <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> * Note: Font rotations can cause text baselines to be rotated, and * multiple runs with different rotations can cause the baseline to * bend or zig-zag.  In order to account for this (rare) possibility, * some APIs are specified to return metrics and take parameters 'in * baseline-relative coordinates' (e.g. ascent, advance), and others * are in 'in standard coordinates' (e.g. getBounds).  Values in * baseline-relative coordinates map the 'x' coordinate to the * distance along the baseline, (positive x is forward along the * baseline), and the 'y' coordinate to a distance along the * perpendicular to the baseline at 'x' (postitive y is 90 degrees * clockwise from the baseline vector).  Values in standard * coordinates are measured along the x and y axes, with 0,0 at the * origin of the TextLayout.  Documentation for each relevant API * indicates what values are in what coordinate system.  In general, * measurement-related APIs are in baseline-relative coordinates, * while display-related APIs are in standard coordinates. * * @see LineBreakMeasurer * @see TextAttribute * @see TextHitInfo * @see LayoutPath */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;     /*     * 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    // justifyRatio of the justified layout is set to this value.    private static final float ALREADY_JUSTIFIED = -53.9f;     // dx and dy specify the distance between the TextLayout's origin    // and the origin of the leftmost GlyphSet (TextLayoutComponent,    // actually).  They were used for hanging punctuation support,    // which is no longer implemented.  Currently they are both always 0,    // and TextLayout is not guaranteed to work with non-zero dx, dy    // values right now.  They were left in as an aide and reminder to    // anyone who implements hanging punctuation or other similar stuff.    // They are static now so they don't take up space in TextLayout    // instances.    private static float dx;    private static float dy;     /*     * Natural bounds is used internally.  It is built on demand in     * getNaturalBounds.     */    private Rectangle2D naturalBounds = null;     /*     * boundsRect encloses all of the bits this TextLayout can draw.  It     * is build on demand in getBounds.     */    private Rectangle2D boundsRect = null;     /*     * flag to supress/allow carets inside of ligatures when hit testing or     * arrow-keying     */    private boolean caretsInLigaturesAreAllowed = false;     /**     * Defines a policy for determining the strong caret location.     * This class contains one method, <code>getStrongCaret</code>, which     * is used to specify the policy that determines the strong caret in     * dual-caret text.  The strong caret is used to move the caret to the     * left or right. Instances of this class can be passed to     * <code>getCaretShapes</code>, <code>getNextLeftHit</code> and     * <code>getNextRightHit</code> to customize strong caret     * selection.     * <p>     * To specify alternate caret policies, subclass <code>CaretPolicy</code>     * and override <code>getStrongCaret</code>.  <code>getStrongCaret</code>     * should inspect the two <code>TextHitInfo</code> arguments and choose     * one of them as the strong caret.     * <p>     * Most clients do not need to use this class.     */    public static class CaretPolicy {         /**         * Constructs a <code>CaretPolicy</code>.         */         public CaretPolicy() {         }         /**         * Chooses one of the specified <code>TextHitInfo</code> instances as         * a strong caret in the specified <code>TextLayout</code>.         * @param hit1 a valid hit in <code>layout</code>         * @param hit2 a valid hit in <code>layout</code>         * @param layout the <code>TextLayout</code> in which         *        <code>hit1</code> and <code>hit2</code> are used         * @return <code>hit1</code> or <code>hit2</code>         *        (or an equivalent <code>TextHitInfo</code>), indicating the         *        strong caret.         */        public TextHitInfo getStrongCaret(TextHitInfo hit1,                                          TextHitInfo hit2,                                          TextLayout layout) {             // default implmentation just calls private method on layout            return layout.getStrongHit(hit1, hit2);        }    }     /**     * This <code>CaretPolicy</code> is used when a policy is not specified     * by the client.  With this policy, a hit on a character whose direction     * is the same as the line direction is stronger than a hit on a     * counterdirectional character.  If the characters' directions are     * the same, a hit on the leading edge of a character is stronger     * than a hit on the trailing edge of a character.     */    public static final CaretPolicy DEFAULT_CARET_POLICY = new CaretPolicy();     /**     * Constructs a <code>TextLayout</code> from a <code>String</code>     * and a {@link Font}.  All the text is styled using the specified     * <code>Font</code>.     * <p>     * The <code>String</code> must specify a single paragraph of text,     * because an entire paragraph is required for the bidirectional     * algorithm.     * @param string the text to display     * @param font a <code>Font</code> used to style the text     * @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>TextLayout</code> and user space.     */    public TextLayout(String string, Font font, FontRenderContext frc) {         if (font == null) {            throw new IllegalArgumentException("Null font passed to TextLayout constructor.");        }         if (string == null) {            throw new IllegalArgumentException("Null string passed to TextLayout constructor.");        }         if (string.length() == 0) {            throw new IllegalArgumentException("Zero length string passed to TextLayout constructor.");        }         Map attributes = null;        if (font.hasLayoutAttributes()) {            attributes = font.getAttributes();        }         char[] text = string.toCharArray();        if (sameBaselineUpTo(font, text, 0, text.length) == text.length) {            fastInit(text, font, attributes, frc);        } else {            AttributedString as = attributes == null                ? new AttributedString(string)                : new AttributedString(string, attributes);            as.addAttribute(TextAttribute.FONT, font);            standardInit(as.getIterator(), text, frc);        }    }     /**     * Constructs a <code>TextLayout</code> from a <code>String</code>     * and an attribute set.     * <p>     * All the text is styled using the provided attributes.     * <p>     * <code>string</code> must specify a single paragraph of text because an     * entire paragraph is required for the bidirectional algorithm.     * @param string the text to display     * @param attributes the attributes used to style the text     * @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>TextLayout</code> and user space.     */    public TextLayout(String string, Map<? extends Attribute,?> attributes,                      FontRenderContext frc)    {        if (string == null) {            throw new IllegalArgumentException("Null string passed to TextLayout constructor.");        }         if (attributes == null) {            throw new IllegalArgumentException("Null map passed to TextLayout constructor.");        }         if (string.length() == 0) {            throw new IllegalArgumentException("Zero length string passed to TextLayout constructor.");        }         char[] text = string.toCharArray();        Font font = singleFont(text, 0, text.length, attributes);        if (font != null) {            fastInit(text, font, attributes, frc);        } else {            AttributedString as = new AttributedString(string, attributes);            standardInit(as.getIterator(), text, frc);        }    }     /*     * Determines a font for the attributes, and if a single font can render     * all the text on one baseline, return it, otherwise null.  If the     * attributes specify a font, assume it can display all the text without     * checking.     * If the AttributeSet contains an embedded graphic, return null.     */    private static Font singleFont(char[] text,                                   int start,                                   int limit,                                   Map attributes) {         if (attributes.get(TextAttribute.CHAR_REPLACEMENT) != null) {            return null;        }         Font font = null;        try {            font = (Font)attributes.get(TextAttribute.FONT);        }        catch (ClassCastException e) {        }        if (font == null) {            if (attributes.get(TextAttribute.FAMILY) != null) {                font = Font.getFont(attributes);                if (font.canDisplayUpTo(text, start, limit) != -1) {                    return null;                }            } else {                FontResolver resolver = FontResolver.getInstance();                CodePointIterator iter = CodePointIterator.create(text, start, limit);                int fontIndex = resolver.nextFontRunIndex(iter);                if (iter.charIndex() == limit) {                    font = resolver.getFont(fontIndex, attributes);                }            }        }         if (sameBaselineUpTo(font, text, start, limit) != limit) {            return null;        }         return font;    }     /**     * Constructs a <code>TextLayout</code> from an iterator over styled text.     * <p>     * The iterator must specify a single paragraph of text because an     * entire paragraph is required for the bidirectional     * algorithm.     * @param text the styled text to display     * @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

Lines 1–500 of 2753.