| /* | |
| * Copyright (c) 1995, 2024, 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. | |
| */ | |
| package java.awt; | |
| import java.awt.font.FontRenderContext; | |
| import java.awt.font.GlyphVector; | |
| import java.awt.font.LineMetrics; | |
| import java.awt.font.TextAttribute; | |
| import java.awt.font.TextLayout; | |
| import java.awt.geom.AffineTransform; | |
| import java.awt.geom.Point2D; | |
| import java.awt.geom.Rectangle2D; | |
| import java.awt.peer.FontPeer; | |
| import java.io.File; | |
| import java.io.FileOutputStream; | |
| import java.io.IOException; | |
| import java.io.InputStream; | |
| import java.io.ObjectOutputStream; | |
| import java.io.OutputStream; | |
| import java.io.Serial; | |
| import java.lang.ref.SoftReference; | |
| import java.nio.file.Files; | |
| import java.text.AttributedCharacterIterator.Attribute; | |
| import java.text.CharacterIterator; | |
| import java.util.EventListener; | |
| import java.util.Hashtable; | |
| import java.util.Locale; | |
| import java.util.Map; | |
| import sun.awt.ComponentFactory; | |
| import sun.font.AttributeMap; | |
| import sun.font.AttributeValues; | |
| import sun.font.CompositeFont; | |
| import sun.font.CoreMetrics; | |
| import sun.font.Font2D; | |
| import sun.font.Font2DHandle; | |
| import sun.font.FontAccess; | |
| import sun.font.FontDesignMetrics; | |
| import sun.font.FontLineMetrics; | |
| import sun.font.FontManager; | |
| import sun.font.FontManagerFactory; | |
| import sun.font.FontUtilities; | |
| import sun.font.GlyphLayout; | |
| import sun.font.StandardGlyphVector; | |
| import static sun.font.EAttribute.EBACKGROUND; | |
| import static sun.font.EAttribute.EBIDI_EMBEDDING; | |
| import static sun.font.EAttribute.ECHAR_REPLACEMENT; | |
| import static sun.font.EAttribute.EFAMILY; | |
| import static sun.font.EAttribute.EFONT; | |
| import static sun.font.EAttribute.EFOREGROUND; | |
| import static sun.font.EAttribute.EINPUT_METHOD_HIGHLIGHT; | |
| import static sun.font.EAttribute.EINPUT_METHOD_UNDERLINE; | |
| import static sun.font.EAttribute.EJUSTIFICATION; | |
| import static sun.font.EAttribute.EKERNING; | |
| import static sun.font.EAttribute.ELIGATURES; | |
| import static sun.font.EAttribute.ENUMERIC_SHAPING; | |
| import static sun.font.EAttribute.EPOSTURE; | |
| import static sun.font.EAttribute.ERUN_DIRECTION; | |
| import static sun.font.EAttribute.ESIZE; | |
| import static sun.font.EAttribute.ESTRIKETHROUGH; | |
| import static sun.font.EAttribute.ESUPERSCRIPT; | |
| import static sun.font.EAttribute.ESWAP_COLORS; | |
| import static sun.font.EAttribute.ETRACKING; | |
| import static sun.font.EAttribute.ETRANSFORM; | |
| import static sun.font.EAttribute.EUNDERLINE; | |
| import static sun.font.EAttribute.EWEIGHT; | |
| import static sun.font.EAttribute.EWIDTH; | |
| /** | |
| * The {@code Font} class represents fonts, which are used to | |
| * render text in a visible way. | |
| * A font provides the information needed to map sequences of | |
| * <em>characters</em> to sequences of <em>glyphs</em> | |
| * and to render sequences of glyphs on {@code Graphics} and | |
| * {@code Component} objects. | |
| * | |
| * <h2>Characters and Glyphs</h2> | |
| * | |
| * A <em>character</em> is a symbol that represents an item such as a letter, | |
| * a digit, or punctuation in an abstract way. For example, {@code 'g'}, | |
| * LATIN SMALL LETTER G, is a character. | |
| * <p> | |
| * A <em>glyph</em> is a shape used to render a character or a sequence of | |
| * characters. In simple writing systems, such as Latin, typically one glyph | |
| * represents one character. In general, however, characters and glyphs do not | |
| * have one-to-one correspondence. For example, the character 'á' | |
| * LATIN SMALL LETTER A WITH ACUTE, can be represented by | |
| * two glyphs: one for 'a' and one for '´'. On the other hand, the | |
| * two-character string "fi" can be represented by a single glyph, an | |
| * "fi" ligature. In complex writing systems, such as Arabic or the South | |
| * and South-East Asian writing systems, the relationship between characters | |
| * and glyphs can be more complicated and involve context-dependent selection | |
| * of glyphs as well as glyph reordering. | |
| * | |
| * A font encapsulates the collection of glyphs needed to render a selected set | |
| * of characters as well as the tables needed to map sequences of characters to | |
| * corresponding sequences of glyphs. | |
| * | |
| * <h2>Physical and Logical Fonts</h2> | |
| * | |
| * The Java Platform distinguishes between two kinds of fonts: | |
| * <em>physical</em> fonts and <em>logical</em> fonts. | |
| * <p> | |
| * <em>Physical</em> fonts are the actual font libraries containing glyph data | |
| * and tables to map from character sequences to glyph sequences, using a font | |
| * technology such as TrueType or PostScript Type 1. | |
| * All implementations of the Java Platform must support TrueType fonts; | |
| * support for other font technologies is implementation dependent. | |
| * Physical fonts may use names such as Helvetica, Palatino, HonMincho, or | |
| * any number of other font names. | |
| * Typically, each physical font supports only a limited set of writing | |
| * systems, for example, only Latin characters or only Japanese and Basic | |
| * Latin. | |
| * The set of available physical fonts varies between configurations. | |
| * Applications that require specific fonts can bundle them and instantiate | |
| * them using the {@link #createFont createFont} method. | |
| * <p> | |
| * <em>Logical</em> fonts are the five font families defined by the Java | |
| * platform which must be supported by any Java runtime environment: | |
| * Serif, SansSerif, Monospaced, Dialog, and DialogInput. | |
| * These logical fonts are not actual font libraries. Instead, the logical | |
| * font names are mapped to physical fonts by the Java runtime environment. | |
| * The mapping is implementation and usually locale dependent, so the look | |
| * and the metrics provided by them vary. | |
| * Typically, each logical font name maps to several physical fonts in order to | |
| * cover a large range of characters. | |
| * <p> | |
| * Peered AWT components, such as {@link Label Label} and | |
| * {@link TextField TextField}, can only use logical fonts. | |
| * <p> | |
| * For a discussion of the relative advantages and disadvantages of using | |
| * physical or logical fonts, see the | |
| * <a href="https://docs.oracle.com/javase/tutorial/2d/text/fonts.html#advantages-and-disadvantages"> | |
| * Physical and Logical Fonts</a> | |
| * in <a href="https://docs.oracle.com/javase/tutorial/index.html">The Java Tutorials</a> | |
| * document. | |
| * | |
| * <h2>Font Faces and Names</h2> | |
| * | |
| * A {@code Font} | |
| * can have many faces, such as heavy, medium, oblique, gothic and | |
| * regular. All of these faces have similar typographic design. | |
| * <p> | |
| * There are three different names that you can get from a | |
| * {@code Font} object. The <em>logical font name</em> is simply the | |
| * name that was used to construct the font. | |
| * The <em>font face name</em>, or just <em>font name</em> for | |
| * short, is the name of a particular font face, like Helvetica Bold. The | |
| * <em>family name</em> is the name of the font family that determines the | |
| * typographic design across several faces, like Helvetica. | |
| * <p> | |
| * The {@code Font} class represents an instance of a font face from | |
| * a collection of font faces that are present in the system resources | |
| * of the host system. As examples, Arial Bold and Courier Bold Italic | |
| * are font faces. There can be several {@code Font} objects | |
| * associated with a font face, each differing in size, style, transform | |
| * and font features. | |
| * <p> | |
| * Glyphs may not always be rendered with the requested properties (e.g, font | |
| * and style) due to platform limitations such as the absence of suitable | |
| * platform fonts to implement a logical font. | |
| * <p> | |
| * The {@link GraphicsEnvironment#getAllFonts() getAllFonts} method | |
| * of the {@code GraphicsEnvironment} class returns an | |
| * array of all font faces available in the system. These font faces are | |
| * returned as {@code Font} objects with a size of 1, identity | |
| * transform and default font features. These | |
| * base fonts can then be used to derive new {@code Font} objects | |
| * with varying sizes, styles, transforms and font features via the | |
| * {@code deriveFont} methods in this class. | |
| * | |
| * <h2>Font and TextAttribute</h2> | |
| * | |
| * <p>{@code Font} supports most | |
| * {@code TextAttribute}s. This makes some operations, such as | |
| * rendering underlined text, convenient since it is not | |
| * necessary to explicitly construct a {@code TextLayout} object. | |
| * Attributes can be set on a Font by constructing or deriving it | |
| * using a {@code Map} of {@code TextAttribute} values. | |
| * | |
| * <p>The values of some {@code TextAttributes} are not | |
| * serializable, and therefore attempting to serialize an instance of | |
| * {@code Font} that has such values will not serialize them. | |
| * This means a Font deserialized from such a stream will not compare | |
| * equal to the original Font that contained the non-serializable | |
| * attributes. This should very rarely pose a problem | |
| * since these attributes are typically used only in special | |
| * circumstances and are unlikely to be serialized. | |
| * | |
| * <ul> | |
| * <li>{@code FOREGROUND} and {@code BACKGROUND} use | |
| * {@code Paint} values. The subclass {@code Color} is | |
| * serializable, while {@code GradientPaint} and | |
| * {@code TexturePaint} are not.</li> | |
| * <li>{@code CHAR_REPLACEMENT} uses | |
| * {@code GraphicAttribute} values. The subclasses | |
| * {@code ShapeGraphicAttribute} and | |
| * {@code ImageGraphicAttribute} are not serializable.</li> | |
| * <li>{@code INPUT_METHOD_HIGHLIGHT} uses | |
| * {@code InputMethodHighlight} values, which are | |
| * not serializable. See {@link java.awt.im.InputMethodHighlight}.</li> | |
| * </ul> | |
| * | |
| * <p>Clients who create custom subclasses of {@code Paint} and | |
| * {@code GraphicAttribute} can make them serializable and | |
| * avoid this problem. Clients who use input method highlights can | |
| * convert these to the platform-specific attributes for that | |
| * highlight on the current platform and set them on the Font as | |
| * a workaround. | |
| * | |
| * <p>The {@code Map}-based constructor and | |
| * {@code deriveFont} APIs ignore the FONT attribute, and it is | |
| * not retained by the Font; the static {@link #getFont} method should | |
| * be used if the FONT attribute might be present. See {@link | |
| * java.awt.font.TextAttribute#FONT} for more information.</p> | |
| * | |
| * <p>Several attributes will cause additional rendering overhead | |
| * and potentially invoke layout. If a {@code Font} has such | |
| * attributes, the <code>{@link #hasLayoutAttributes()}</code> method | |
| * will return true.</p> | |
| * | |
| * <p>Note: Font rotations can cause text baselines to be rotated. In | |
| * order to account for this (rare) possibility, font APIs are | |
| * specified to return metrics and take parameters 'in | |
| * baseline-relative coordinates'. This maps the 'x' coordinate to | |
| * the advance 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' (positive y is 90 degrees | |
| * clockwise from the baseline vector). APIs for which this is | |
| * especially important are called out as having 'baseline-relative | |
| * coordinates.' | |
| */ | |
| public class Font implements java.io.Serializable | |
| { | |
| private static class FontAccessImpl extends FontAccess { | |
| public Font2D getFont2D(Font font) { | |
| return font.getFont2D(); | |
| } | |
| public void setFont2D(Font font, Font2DHandle handle) { | |
| font.font2DHandle = handle; | |
| } | |
| public void setCreatedFont(Font font) { | |
| font.createdFont = true; | |
| } | |
| public boolean isCreatedFont(Font font) { | |
| return font.createdFont; | |
| } | |
| @Override | |
| public FontPeer getFontPeer(final Font font) { | |
| return font.getFontPeer(); | |
| } | |
| } | |
| static { | |
| /* ensure that the necessary native libraries are loaded */ | |
| Toolkit.loadLibraries(); | |
| initIDs(); | |
| FontAccess.setFontAccess(new FontAccessImpl()); | |
| } | |
| /** | |
| * This is now only used during serialization. Typically | |
| * it is null. | |
| * | |
| * @serial | |
| * @see #getAttributes() | |
| */ | |
| private Hashtable<Object, Object> fRequestedAttributes; | |
| /* | |
| * Constants to be used for logical font family names. | |
| */ | |
| /** | |
| * A String constant for the canonical family name of the | |
| * logical font "Dialog". It is useful in Font construction | |
| * to provide compile-time verification of the name. | |
| * @since 1.6 | |
| */ | |
| public static final String DIALOG = "Dialog"; | |
| /** | |
| * A String constant for the canonical family name of the | |
| * logical font "DialogInput". It is useful in Font construction | |
| * to provide compile-time verification of the name. | |
| * @since 1.6 | |
| */ | |
| public static final String DIALOG_INPUT = "DialogInput"; | |
| /** | |
| * A String constant for the canonical family name of the | |
| * logical font "SansSerif". It is useful in Font construction | |
| * to provide compile-time verification of the name. | |
| * @since 1.6 | |
| */ | |
| public static final String SANS_SERIF = "SansSerif"; | |
| /** | |
| * A String constant for the canonical family name of the | |
| * logical font "Serif". It is useful in Font construction | |
| * to provide compile-time verification of the name. | |
| * @since 1.6 | |
| */ | |
| public static final String SERIF = "Serif"; | |
| /** | |
| * A String constant for the canonical family name of the | |
| * logical font "Monospaced". It is useful in Font construction | |
| * to provide compile-time verification of the name. | |
| * @since 1.6 | |
| */ | |
| public static final String MONOSPACED = "Monospaced"; | |
| /* | |
| * Constants to be used for styles. Can be combined to mix | |
| * styles. | |
| */ | |
| /** | |
| * The plain style constant. | |
| */ | |
| public static final int PLAIN = 0; | |
| /** | |
| * The bold style constant. This can be combined with the other style | |
| * constants (except PLAIN) for mixed styles. | |
| */ | |
| public static final int BOLD = 1; | |
| /** | |
| * The italicized style constant. This can be combined with the other | |
| * style constants (except PLAIN) for mixed styles. | |
| */ | |
| public static final int ITALIC = 2; | |
| /** | |
| * The baseline used in most Roman scripts when laying out text. | |
| */ | |
| public static final int ROMAN_BASELINE = 0; | |
| /** | |
| * The baseline used in ideographic scripts like Chinese, Japanese, | |
| * and Korean when laying out text. | |
| */ | |
| public static final int CENTER_BASELINE = 1; | |
| /** | |
| * The baseline used in Devanagari and similar scripts when laying | |
| * out text. | |
| */ | |
| public static final int HANGING_BASELINE = 2; | |
| /** | |
| * Identify a font resource of type TRUETYPE. | |
| * Used to specify a TrueType font resource to the | |
| * {@link #createFont} method. | |
| * The TrueType format was extended to become the OpenType | |
| * format, which adds support for fonts with Postscript outlines, | |
| * this tag therefore references these fonts, as well as those | |
| * with TrueType outlines. | |
| * @since 1.3 | |
| */ | |
| public static final int TRUETYPE_FONT = 0; | |
| /** | |
| * Identify a font resource of type TYPE1. | |
| * Used to specify a Type1 font resource to the | |
| * {@link #createFont} method. | |
| * @since 1.5 | |
| */ | |
| public static final int TYPE1_FONT = 1; | |
| /** | |
| * The logical name of this {@code Font}, as passed to the | |
| * constructor. | |
| * @since 1.0 | |
| * | |
| * @serial | |
| * @see #getName | |
| */ | |
| protected String name; | |
| /** | |
| * The style of this {@code Font}, as passed to the constructor. | |
| * This style can be PLAIN, BOLD, ITALIC, or BOLD+ITALIC. | |
| * @since 1.0 | |
| * | |
| * @serial | |
| * @see #getStyle() | |
| */ | |
| protected int style; | |
| /** | |
| * The point size of this {@code Font}, rounded to integer. | |
| * @since 1.0 | |
| * | |
| * @serial | |
| * @see #getSize() | |
| */ | |
| protected int size; | |
| /** | |
| * The point size of this {@code Font} in {@code float}. | |
| * | |
| * @serial | |
| * @see #getSize() | |
| * @see #getSize2D() | |
| */ | |
| protected float pointSize; | |
| /** | |
| * The platform specific font information. | |
| */ | |
| private transient FontPeer peer; | |
| private transient long pData; // native JDK1.1 font pointer | |
| private transient Font2DHandle font2DHandle; | |
| private transient AttributeValues values; | |
| private transient boolean hasLayoutAttributes; | |
| /* | |
| * If the origin of a Font is a created font then this attribute | |
| * must be set on all derived fonts too. | |
| */ | |
| private transient boolean createdFont = false; | |
| /* | |
| * This is true if the font transform is not identity. It | |
| * is used to avoid unnecessary instantiation of an AffineTransform. | |
| */ | |
| private transient boolean nonIdentityTx; | |
| /* | |
| * A cached value used when a transform is required for internal | |
| * use. This must not be exposed to callers since AffineTransform | |
| * is mutable. | |
| */ | |
| private static final AffineTransform identityTx = new AffineTransform(); | |
| /** | |
| * Use serialVersionUID from JDK 1.1 for interoperability. | |
| */ | |
| @Serial | |
| private static final long serialVersionUID = -4206021311591459213L; | |
| /** | |
| * Gets the peer of this {@code Font}. | |
| * | |
| * @return the peer of the {@code Font}. | |
| */ | |
| private FontPeer getFontPeer() { | |
| if(peer == null) { | |
| Toolkit tk = Toolkit.getDefaultToolkit(); | |
| if (tk instanceof ComponentFactory) { | |
| peer = ((ComponentFactory) tk).getFontPeer(name, style); | |
| } | |
| } | |
| return peer; | |
| } | |
| /** | |
| * Return the AttributeValues object associated with this | |
| * font. Most of the time, the internal object is null. | |
| * If required, it will be created from the 'standard' | |
| * state on the font. Only non-default values will be | |
| * set in the AttributeValues object. | |
| * | |
| * <p>Since the AttributeValues object is mutable, and it | |
| * is cached in the font, care must be taken to ensure that | |
| * it is not mutated. | |
| */ | |
| private AttributeValues getAttributeValues() { | |
| if (values == null) { | |
| AttributeValues valuesTmp = new AttributeValues(); | |
| valuesTmp.setFamily(name); | |
| valuesTmp.setSize(pointSize); // expects the float value. | |
| if ((style & BOLD) != 0) { | |
| valuesTmp.setWeight(2); // WEIGHT_BOLD | |
| } | |
| if ((style & ITALIC) != 0) { | |
| valuesTmp.setPosture(.2f); // POSTURE_OBLIQUE | |
| } | |
| valuesTmp.defineAll(PRIMARY_MASK); // for streaming compatibility | |
| values = valuesTmp; | |
| } | |
| return values; | |
| } | |
| private Font2D getFont2D() { | |
| FontManager fm = FontManagerFactory.getInstance(); | |
| if (font2DHandle == null) { | |
| font2DHandle = | |
| fm.findFont2D(name, style, | |
| FontManager.LOGICAL_FALLBACK).handle; | |
| } | |
| /* Do not cache the de-referenced font2D. It must be explicitly | |
| * de-referenced to pick up a valid font in the event that the | |
| * original one is marked invalid | |
| */ | |
| return font2DHandle.font2D; | |
| } | |
| /** | |
| * Creates a new {@code Font} from the specified name, style and | |
| * point size. | |
| * <p> | |
| * The font name can be a font face name or a font family name. | |
| * It is used together with the style to find an appropriate font face. | |
| * When a font family name is specified, the style argument is used to | |
| * select the most appropriate face from the family. When a font face | |
| * name is specified, the face's style and the style argument are | |
| * merged to locate the best matching font from the same family. | |
| * For example if face name "Arial Bold" is specified with style | |
| * {@code Font.ITALIC}, the font system looks for a face in the | |
| * "Arial" family that is bold and italic, and may associate the font | |
| * instance with the physical font face "Arial Bold Italic". | |
| * The style argument is merged with the specified face's style, not | |
| * added or subtracted. | |
| * This means, specifying a bold face and a bold style does not | |
| * double-embolden the font, and specifying a bold face and a plain | |
| * style does not lighten the font. | |
| * <p> | |
| * If no face for the requested style can be found, the font system | |
| * may apply algorithmic styling to achieve the desired style. | |
| * For example, if {@code ITALIC} is requested, but no italic | |
| * face is available, glyphs from the plain face may be algorithmically | |
| * obliqued (slanted). | |
| * <p> | |
| * Font name lookup is case insensitive, using the case folding | |
| * rules of the US locale. | |
| * <p> | |
| * If the {@code name} parameter represents something other than a | |
| * logical font, i.e. is interpreted as a physical font face or family, and | |
| * this cannot be mapped by the implementation to a physical font or a | |
| * compatible alternative, then the font system will map the Font | |
| * instance to "Dialog", such that for example, the family as reported | |
| * by {@link #getFamily() getFamily} will be "Dialog". | |
| * | |
| * @param name the font name. This can be a font face name or a font | |
| * family name, and may represent either a logical font or a physical | |
| * font found in this {@code GraphicsEnvironment}. | |
| * The family names for logical fonts are: Dialog, DialogInput, | |
| * Monospaced, Serif, or SansSerif. Pre-defined String constants exist | |
| * for all of these names, for example, {@code DIALOG}. If {@code name} is | |
| * {@code null}, the <em>logical font name</em> of the new | |
| * {@code Font} as returned by {@code getName()} is set to | |
| * the name "Default". | |
| * @param style the style constant for the {@code Font} | |
| * The style argument is an integer bitmask that may | |
| * be {@code PLAIN}, or a bitwise union of {@code BOLD} and/or | |
| * {@code ITALIC} (for example, {@code ITALIC} or {@code BOLD|ITALIC}). | |
| * If the style argument does not conform to one of the expected | |
| * integer bitmasks then the style is set to {@code PLAIN}. | |
| * @param size the point size of the {@code Font} | |
| * @see GraphicsEnvironment#getAllFonts | |
| * @see GraphicsEnvironment#getAvailableFontFamilyNames | |
| * @since 1.0 | |
| */ | |
| public Font(String name, int style, int size) { | |
| this.name = (name != null) ? name : "Default"; | |
| this.style = (style & ~0x03) == 0 ? style : 0; | |
| this.size = size; | |
| this.pointSize = size; | |
| } | |
| private Font(String name, int style, float sizePts) { | |
| this.name = (name != null) ? name : "Default"; | |
| this.style = (style & ~0x03) == 0 ? style : 0; | |
| this.size = (int)(sizePts + 0.5); | |
| this.pointSize = sizePts; | |
| } | |
| /* This constructor is used by deriveFont when attributes is null */ | |
| private Font(String name, int style, float sizePts, | |
| boolean created, Font2DHandle handle) { | |
| this(name, style, sizePts); | |
| this.createdFont = created; | |
| /* Fonts created from a stream will use the same font2D instance | |
| * as the parent. | |
| * One exception is that if the derived font is requested to be | |
| * in a different style, then also check if its a CompositeFont | |
| * and if so build a new CompositeFont from components of that style. | |
| * CompositeFonts can only be marked as "created" if they are used | |
| * to add fall backs to a physical font. And non-composites are | |
| * always from "Font.createFont()" and shouldn't get this treatment. | |
| */ | |
| if (created) { | |
| if (handle.font2D instanceof CompositeFont && | |
| handle.font2D.getStyle() != style) { | |
| FontManager fm = FontManagerFactory.getInstance(); | |
| this.font2DHandle = fm.getNewComposite(null, style, handle); | |
| } else { | |
| this.font2DHandle = handle; | |
| } | |
| } | |
| } | |
| /* used to implement Font.createFont */ | |
| private Font(File fontFile, int fontFormat, boolean isCopy) | |
| throws FontFormatException { | |
| this.createdFont = true; | |
| /* Font2D instances created by this method track their font file | |
| * so that when the Font2D is GC'd it can also remove the file. | |
| */ | |
| FontManager fm = FontManagerFactory.getInstance(); | |
| Font2D[] fonts = | |
| fm.createFont2D(fontFile, fontFormat, false, isCopy); | |
| this.font2DHandle = fonts[0].handle; | |
| this.name = this.font2DHandle.font2D.getFontName(Locale.getDefault()); | |
| this.style = Font.PLAIN; | |
| this.size = 1; | |
| this.pointSize = 1f; | |
| } | |
| /* This constructor is used when one font is derived from another. | |
| * Fonts created from a stream will use the same font2D instance as the | |
| * parent. They can be distinguished because the "created" argument | |
| * will be "true". Since there is no way to recreate these fonts they | |
| * need to have the handle to the underlying font2D passed in. | |
| * "created" is also true when a special composite is referenced by the | |
| * handle for essentially the same reasons. | |
| * But when deriving a font in these cases two particular attributes | |
| * need special attention: family/face and style. | |
| * The "composites" in these cases need to be recreated with optimal | |
| * fonts for the new values of family and style. | |
| * For fonts created with createFont() these are treated differently. | |
| * JDK can often synthesise a different style (bold from plain | |
| * for example). For fonts created with "createFont" this is a reasonable | |
| * solution but its also possible (although rare) to derive a font with a | |
| * different family attribute. In this case JDK needs | |
| * to break the tie with the original Font2D and find a new Font. | |
| * The oldName and oldStyle are supplied so they can be compared with | |
| * what the Font2D and the values. To speed things along : | |
| * oldName == null will be interpreted as the name is unchanged. | |
| * oldStyle = -1 will be interpreted as the style is unchanged. | |
| * In these cases there is no need to interrogate "values". | |
| */ | |
| private Font(AttributeValues values, String oldName, int oldStyle, | |
| boolean created, Font2DHandle handle) { | |
| this.createdFont = created; | |
| if (created) { | |
| this.font2DHandle = handle; | |
| String newName = null; | |
| if (oldName != null) { | |
| newName = values.getFamily(); | |
| if (oldName.equals(newName)) newName = null; | |
| } | |
| int newStyle = 0; | |
| if (oldStyle == -1) { | |
| newStyle = -1; | |
| } else { | |
| if (values.getWeight() >= 2f) newStyle = BOLD; | |
| if (values.getPosture() >= .2f) newStyle |= ITALIC; | |
| if (oldStyle == newStyle) newStyle = -1; | |
| } | |
| if (handle.font2D instanceof CompositeFont) { | |
| if (newStyle != -1 || newName != null) { | |
| FontManager fm = FontManagerFactory.getInstance(); | |
| this.font2DHandle = | |
| fm.getNewComposite(newName, newStyle, handle); | |
| } | |
| } else if (newName != null) { | |
| this.createdFont = false; | |
| this.font2DHandle = null; | |
| } | |
| } | |
| initFromValues(values); | |
| } | |
| /** | |
| * Creates a new {@code Font} with the specified attributes. | |
| * Only keys defined in {@link java.awt.font.TextAttribute TextAttribute} | |
| * are recognized. In addition the FONT attribute is | |
| * not recognized by this constructor | |
| * (see {@link #getAvailableAttributes}). Only attributes that have | |
| * values of valid types will affect the new {@code Font}. | |
| * <p> | |
| * If {@code attributes} is {@code null}, a new | |
| * {@code Font} is initialized with default values. | |
| * @see java.awt.font.TextAttribute | |
| * @param attributes the attributes to assign to the new | |
| * {@code Font}, or {@code null} | |
| */ | |
| public Font(Map<? extends Attribute, ?> attributes) { | |
| initFromValues(AttributeValues.fromMap(attributes, RECOGNIZED_MASK)); | |
| } | |
| /** | |
| * Creates a new {@code Font} from the specified {@code font}. | |
| * This constructor is intended for use by subclasses. | |
| * @param font from which to create this {@code Font}. | |
| * @throws NullPointerException if {@code font} is null | |
| * @since 1.6 | |
| */ | |
| protected Font(Font font) { | |
| if (font.values != null) { | |
| initFromValues(font.getAttributeValues().clone()); | |
| } else { | |
| this.name = font.name; | |
| this.style = font.style; | |
| this.size = font.size; | |
| this.pointSize = font.pointSize; | |
| } | |
| this.font2DHandle = font.font2DHandle; | |
| this.createdFont = font.createdFont; | |
| } | |
| /** | |
| * Font recognizes all attributes except FONT. | |
| */ | |
| private static final int RECOGNIZED_MASK = AttributeValues.MASK_ALL | |
| & ~AttributeValues.getMask(EFONT); | |
| /** | |
| * These attributes are considered primary by the FONT attribute. | |
| */ | |
| private static final int PRIMARY_MASK = | |
| AttributeValues.getMask(EFAMILY, EWEIGHT, EWIDTH, EPOSTURE, ESIZE, | |
| ETRANSFORM, ESUPERSCRIPT, ETRACKING); | |
| /** | |
| * These attributes are considered secondary by the FONT attribute. | |
| */ | |
| private static final int SECONDARY_MASK = | |
| RECOGNIZED_MASK & ~PRIMARY_MASK; | |
| /** | |
| * These attributes are handled by layout. | |
| */ | |
| private static final int LAYOUT_MASK = | |
| AttributeValues.getMask(ECHAR_REPLACEMENT, EFOREGROUND, EBACKGROUND, | |
| EUNDERLINE, ESTRIKETHROUGH, ERUN_DIRECTION, | |
| EBIDI_EMBEDDING, EJUSTIFICATION, | |
| EINPUT_METHOD_HIGHLIGHT, EINPUT_METHOD_UNDERLINE, | |
| ESWAP_COLORS, ENUMERIC_SHAPING, EKERNING, | |
| ELIGATURES, ETRACKING, ESUPERSCRIPT); | |
| private static final int EXTRA_MASK = | |
| AttributeValues.getMask(ETRANSFORM, ESUPERSCRIPT, EWIDTH); | |
| /** | |
| * Initialize the standard Font fields from the values object. | |
| */ | |
| private void initFromValues(AttributeValues values) { | |
| this.values = values; | |
| values.defineAll(PRIMARY_MASK); // for 1.5 streaming compatibility | |
| this.name = values.getFamily(); | |
| this.pointSize = values.getSize(); | |
| this.size = (int)(values.getSize() + 0.5); | |
| if (values.getWeight() >= 2f) this.style |= BOLD; // not == 2f | |
| if (values.getPosture() >= .2f) this.style |= ITALIC; // not == .2f | |
| this.nonIdentityTx = values.anyNonDefault(EXTRA_MASK); | |
| this.hasLayoutAttributes = values.anyNonDefault(LAYOUT_MASK); | |
| } | |
| /** | |
| * Returns true if any part of the specified text is from a | |
| * complex script for which the implementation will need to invoke | |
| * layout processing in order to render correctly when using | |
| * {@link Graphics#drawString(String,int,int) drawString(String,int,int)} | |
| * and other text rendering methods. Measurement of the text | |
| * may similarly need the same extra processing. | |
| * The {@code start} and {@code end} indices are provided so that | |
| * the application can request only a subset of the text be considered. | |
| * The last char index examined is at {@code "end-1"}, | |
| * i.e a request to examine the entire array would be | |
| * <pre> | |
| * {@code Font.textRequiresLayout(chars, 0, chars.length);} | |
| * </pre> | |
| * An application may find this information helpful in | |
| * performance sensitive code. | |
| * <p> | |
| * Note that even if this method returns {@code false}, layout processing | |
| * may still be invoked when used with any {@code Font} | |
| * for which {@link #hasLayoutAttributes()} returns {@code true}, | |
| * so that method will need to be consulted for the specific font, | |
| * in order to obtain an answer which accounts for such font attributes. | |
| * | |
| * @param chars the text. | |
| * @param start the index of the first char to examine. | |
| * @param end the ending index, exclusive. | |
| * @return {@code true} if the specified text will need special layout. | |
| * @throws NullPointerException if {@code chars} is null. | |
| * @throws ArrayIndexOutOfBoundsException if {@code start} is negative or | |
| * {@code end} is greater than the length of the {@code chars} array. | |
| * @since 9 | |
| */ | |
| public static boolean textRequiresLayout(char[] chars, | |
| int start, int end) { | |
| if (chars == null) { | |
| throw new NullPointerException("null char array"); | |
| } | |
| if (start < 0 || end > chars.length) { | |
| throw new ArrayIndexOutOfBoundsException("start < 0 or end > len"); | |
| } | |
| return FontUtilities.isComplexScript(chars, start, end); | |
| } | |
| /** | |
| * Returns a {@code Font} appropriate to the attributes. | |
| * If {@code attributes} contains a {@code FONT} attribute | |
| * with a valid {@code Font} as its value, it will be | |
| * merged with any remaining attributes. See | |
| * {@link java.awt.font.TextAttribute#FONT} for more | |
| * information. | |
| * | |
| * @param attributes the attributes to assign to the new | |
| * {@code Font} | |
| * @return a new {@code Font} created with the specified | |
| * attributes | |
| * @throws NullPointerException if {@code attributes} is null. | |
| * @since 1.2 | |
| * @see java.awt.font.TextAttribute | |
| */ | |
| public static Font getFont(Map<? extends Attribute, ?> attributes) { | |
| // optimize for two cases: | |
| // 1) FONT attribute, and nothing else | |
| // 2) attributes, but no FONT | |
| // avoid turning the attributemap into a regular map for no reason | |
| if (attributes instanceof AttributeMap && | |
| ((AttributeMap)attributes).getValues() != null) { | |
| AttributeValues values = ((AttributeMap)attributes).getValues(); | |
| if (values.isNonDefault(EFONT)) { | |
| Font font = values.getFont(); | |
| if (!values.anyDefined(SECONDARY_MASK)) { | |
| return font; | |
| } | |
| // merge | |
| values = font.getAttributeValues().clone(); | |
| values.merge(attributes, SECONDARY_MASK); | |
| return new Font(values, font.name, font.style, | |
| font.createdFont, font.font2DHandle); | |
| } | |
| return new Font(attributes); | |
| } | |
| Font font = (Font)attributes.get(TextAttribute.FONT); | |
| if (font != null) { | |
| if (attributes.size() > 1) { // oh well, check for anything else | |
| AttributeValues values = font.getAttributeValues().clone(); | |
| values.merge(attributes, SECONDARY_MASK); | |
| return new Font(values, font.name, font.style, | |
| font.createdFont, font.font2DHandle); | |
| } | |
| return font; | |
| } | |
| return new Font(attributes); | |
| } | |
| /** | |
| * Returns a new array of {@code Font} decoded from the specified stream. | |
| * The returned {@code Font[]} will have at least one element. | |
| * <p> | |
| * The explicit purpose of this variation on the | |
| * {@code createFont(int, InputStream)} method is to support font | |
| * sources which represent a TrueType/OpenType font collection and | |
| * be able to return all individual fonts in that collection. | |
| * Consequently this method will throw {@code FontFormatException} | |
| * if the data source does not contain at least one TrueType/OpenType | |
| * font. The same exception will also be thrown if any of the fonts in | |
| * the collection does not contain the required font tables. | |
| * <p> | |
| * The condition "at least one", allows for the stream to represent | |
| * a single OpenType/TrueType font. That is, it does not have to be | |
| * a collection. | |
| * Each {@code Font} element of the returned array is | |
| * created with a point size of 1 and style {@link #PLAIN PLAIN}. | |
| * This base font can then be used with the {@code deriveFont} | |
| * methods in this class to derive new {@code Font} objects with | |
| * varying sizes, styles, transforms and font features. | |
| * <p>This method does not close the {@link InputStream}. | |
| * <p> | |
| * To make each {@code Font} available to Font constructors it | |
| * must be registered in the {@code GraphicsEnvironment} by calling | |
| * {@link GraphicsEnvironment#registerFont(Font) registerFont(Font)}. | |
| * @param fontStream an {@code InputStream} object representing the | |
| * input data for the font or font collection. | |
| * @return a new {@code Font[]}. | |
| * @throws FontFormatException if the {@code fontStream} data does | |
| * not contain the required font tables for any of the elements of | |
| * the collection, or if it contains no fonts at all. | |
| * @throws IOException if the {@code fontStream} cannot be completely read. | |
| * @see GraphicsEnvironment#registerFont(Font) | |
| * @since 9 | |
| */ | |
| public static Font[] createFonts(InputStream fontStream) | |
| throws FontFormatException, IOException { | |
| return createFont0(Font.TRUETYPE_FONT, fontStream, true); | |
| } | |
| /* used to implement Font.createFont */ | |
| private Font(Font2D font2D) { | |
| this.createdFont = true; | |
| this.font2DHandle = font2D.handle; | |
| this.name = font2D.getFontName(Locale.getDefault()); | |
| this.style = Font.PLAIN; | |
| this.size = 1; | |
| this.pointSize = 1f; | |
| } | |
| /** | |
| * Returns a new array of {@code Font} decoded from the specified file. | |
| * The returned {@code Font[]} will have at least one element. | |
| * <p> | |
| * The explicit purpose of this variation on the | |
| * {@code createFont(int, File)} method is to support font | |
| * sources which represent a TrueType/OpenType font collection and | |
| * be able to return all individual fonts in that collection. | |
| * Consequently this method will throw {@code FontFormatException} | |
| * if the data source does not contain at least one TrueType/OpenType | |
| * font. The same exception will also be thrown if any of the fonts in | |
| * the collection does not contain the required font tables. | |
| * <p> | |
| * The condition "at least one", allows for the stream to represent | |
| * a single OpenType/TrueType font. That is, it does not have to be | |
| * a collection. | |
| * Each {@code Font} element of the returned array is | |
| * created with a point size of 1 and style {@link #PLAIN PLAIN}. | |
| * This base font can then be used with the {@code deriveFont} | |
| * methods in this class to derive new {@code Font} objects with | |
| * varying sizes, styles, transforms and font features. | |
| * <p> | |
| * To make each {@code Font} available to Font constructors it | |
| * must be registered in the {@code GraphicsEnvironment} by calling | |
| * {@link GraphicsEnvironment#registerFont(Font) registerFont(Font)}. | |
| * @param fontFile a {@code File} object containing the | |
| * input data for the font or font collection. | |
| * @return a new {@code Font[]}. | |
| * @throws FontFormatException if the {@code File} does | |
| * not contain the required font tables for any of the elements of | |
| * the collection, or if it contains no fonts at all. | |
| * @throws IOException if the {@code fontFile} cannot be read. | |
| * @see GraphicsEnvironment#registerFont(Font) | |
| * @since 9 | |
| */ | |
| public static Font[] createFonts(File fontFile) | |
| throws FontFormatException, IOException | |
| { | |
| int fontFormat = Font.TRUETYPE_FONT; | |
| fontFile = checkFontFile(fontFormat, fontFile); | |
| FontManager fm = FontManagerFactory.getInstance(); | |
| Font2D[] font2DArr = | |
| fm.createFont2D(fontFile, fontFormat, true, false); | |
| int num = font2DArr.length; | |
| Font[] fonts = new Font[num]; | |
| for (int i = 0; i < num; i++) { | |
| fonts[i] = new Font(font2DArr[i]); | |
| } | |
| return fonts; | |
| } | |
| /** | |
| * Returns a new {@code Font} using the specified font type | |
| * and input data. The new {@code Font} is | |
| * created with a point size of 1 and style {@link #PLAIN PLAIN}. | |
| * This base font can then be used with the {@code deriveFont} | |
| * methods in this class to derive new {@code Font} objects with | |
| * varying sizes, styles, transforms and font features. This | |
| * method does not close the {@link InputStream}. | |
| * <p> | |
| * To make the {@code Font} available to Font constructors the | |
| * returned {@code Font} must be registered in the | |
| * {@code GraphicsEnvironment} by calling | |
| * {@link GraphicsEnvironment#registerFont(Font) registerFont(Font)}. | |
| * @param fontFormat the type of the {@code Font}, which is | |
| * {@link #TRUETYPE_FONT TRUETYPE_FONT} if a TrueType resource is specified. | |
| * or {@link #TYPE1_FONT TYPE1_FONT} if a Type 1 resource is specified. | |
| * @param fontStream an {@code InputStream} object representing the | |
| * input data for the font. | |
| * @return a new {@code Font} created with the specified font type. | |
| * @throws IllegalArgumentException if {@code fontFormat} is not | |
| * {@code TRUETYPE_FONT} or {@code TYPE1_FONT}. | |
| * @throws FontFormatException if the {@code fontStream} data does | |
| * not contain the required font tables for the specified format. | |
| * @throws IOException if the {@code fontStream} | |
| * cannot be completely read. | |
| * @see GraphicsEnvironment#registerFont(Font) | |
| * @since 1.3 | |
| */ | |
| public static Font createFont(int fontFormat, InputStream fontStream) | |
| throws java.awt.FontFormatException, java.io.IOException { | |
| return createFont0(fontFormat, fontStream, false)[0]; | |
| } | |
| private static Font[] createFont0(int fontFormat, InputStream fontStream, | |
| boolean allFonts) | |
| throws java.awt.FontFormatException, java.io.IOException { | |
| if (fontFormat != Font.TRUETYPE_FONT && | |
| fontFormat != Font.TYPE1_FONT) { | |
| throw new IllegalArgumentException ("font format not recognized"); | |
| } | |
| boolean copiedFontData = false; | |
| try { | |
| final File tFile = Files.createTempFile("+~JF", ".tmp").toFile(); | |
| int totalSize = 0; | |
| try { | |
| final OutputStream outStream = new FileOutputStream(tFile); | |
| try (outStream) { /* don't close the input stream */ | |
| byte[] buf = new byte[8192]; | |
| for (;;) { | |
| int bytesRead = fontStream.read(buf); | |
| if (bytesRead < 0) { | |
| break; | |
| } | |
| outStream.write(buf, 0, bytesRead); | |
| } | |
| } | |
| copiedFontData = true; | |
| FontManager fm = FontManagerFactory.getInstance(); | |
| Font2D[] font2DArr = | |
| fm.createFont2D(tFile, fontFormat, allFonts, true); | |
| int num = font2DArr.length; | |
| Font[] fonts = new Font[num]; | |
| for (int i = 0; i < num; i++) { | |
| fonts[i] = new Font(font2DArr[i]); | |
| } | |
| return fonts; | |
| } finally { | |
| if (!copiedFontData) { | |
| tFile.delete(); | |
| } | |
| } | |
| } catch (Throwable t) { | |
| if (t instanceof FontFormatException) { | |
| throw (FontFormatException)t; | |
| } | |
| if (t instanceof IOException) { | |
| throw (IOException)t; | |
| } | |
| Throwable cause = t.getCause(); | |
| if (cause instanceof FontFormatException) { | |
| throw (FontFormatException)cause; | |
| } | |
| throw new IOException("Problem reading font data."); | |
| } | |
| } | |
| /** | |
| * Returns a new {@code Font} using the specified font type | |
| * and the specified font file. The new {@code Font} is | |
| * created with a point size of 1 and style {@link #PLAIN PLAIN}. | |
| * This base font can then be used with the {@code deriveFont} | |
| * methods in this class to derive new {@code Font} objects with | |
| * varying sizes, styles, transforms and font features. | |
| * @param fontFormat the type of the {@code Font}, which is | |
| * {@link #TRUETYPE_FONT TRUETYPE_FONT} if a TrueType resource is | |
| * specified or {@link #TYPE1_FONT TYPE1_FONT} if a Type 1 resource is | |
| * specified. | |
| * So long as the returned font, or its derived fonts are referenced | |
| * the implementation may continue to access {@code fontFile} | |
| * to retrieve font data. Thus the results are undefined if the file | |
| * is changed, or becomes inaccessible. | |
| * <p> | |
| * To make the {@code Font} available to Font constructors the | |
| * returned {@code Font} must be registered in the | |
| * {@code GraphicsEnvironment} by calling | |
| * {@link GraphicsEnvironment#registerFont(Font) registerFont(Font)}. | |
| * @param fontFile a {@code File} object representing the | |
| * input data for the font. | |
| * @return a new {@code Font} created with the specified font type. | |
| * @throws IllegalArgumentException if {@code fontFormat} is not | |
| * {@code TRUETYPE_FONT} or {@code TYPE1_FONT}. | |
| * @throws NullPointerException if {@code fontFile} is null. | |
| * @throws IOException if the {@code fontFile} cannot be read. | |
| * @throws FontFormatException if {@code fontFile} does | |
| * not contain the required font tables for the specified format. | |
| * @see GraphicsEnvironment#registerFont(Font) | |
| * @since 1.5 | |
| */ | |
| public static Font createFont(int fontFormat, File fontFile) | |
| throws java.awt.FontFormatException, java.io.IOException { | |
| fontFile = checkFontFile(fontFormat, fontFile); | |
| return new Font(fontFile, fontFormat, false); | |
| } | |
| private static File checkFontFile(int fontFormat, File fontFile) | |
| throws FontFormatException, IOException { | |
| fontFile = new File(fontFile.getPath()); | |
| if (fontFormat != Font.TRUETYPE_FONT && | |
| fontFormat != Font.TYPE1_FONT) { | |
| throw new IllegalArgumentException ("font format not recognized"); | |
| } | |
| if (!fontFile.canRead()) { | |
| throw new IOException("Can't read " + fontFile); | |
| } | |
| return fontFile; | |
| } | |
| /** | |
| * Returns a copy of the transform associated with this | |
| * {@code Font}. This transform is not necessarily the one | |
| * used to construct the font. If the font has algorithmic | |
| * superscripting or width adjustment, this will be incorporated | |
| * into the returned {@code AffineTransform}. | |
| * <p> | |
| * Typically, fonts will not be transformed. Clients generally | |
| * should call {@link #isTransformed} first, and only call this | |
| * method if {@code isTransformed} returns true. | |
| * | |
| * @return an {@link AffineTransform} object representing the | |
| * transform attribute of this {@code Font} object. | |
| */ | |
| public AffineTransform getTransform() { | |
| /* The most common case is the identity transform. Most callers | |
| * should call isTransformed() first, to decide if they need to | |
| * get the transform, but some may not. Here we check to see | |
| * if we have a nonidentity transform, and only do the work to | |
| * fetch and/or compute it if so, otherwise we return a new | |
| * identity transform. | |
| * | |
| * Note that the transform is _not_ necessarily the same as | |
| * the transform passed in as an Attribute in a Map, as the | |
| * transform returned will also reflect the effects of WIDTH and | |
| * SUPERSCRIPT attributes. Clients who want the actual transform | |
| * need to call getRequestedAttributes. | |
| */ | |
| if (nonIdentityTx) { | |
| AttributeValues values = getAttributeValues(); | |
| AffineTransform at = values.isNonDefault(ETRANSFORM) | |
| ? new AffineTransform(values.getTransform()) | |
| : new AffineTransform(); | |
| if (values.getSuperscript() != 0) { | |
| // can't get ascent and descent here, recursive call to this fn, | |
| // so use pointsize | |
| // let users combine super- and sub-scripting | |
| int superscript = values.getSuperscript(); | |
| double trans = 0; | |
| int n = 0; | |
| boolean up = superscript > 0; | |
| int sign = up ? -1 : 1; | |
| int ss = up ? superscript : -superscript; | |
| while ((ss & 7) > n) { | |
| int newn = ss & 7; | |
| trans += sign * (ssinfo[newn] - ssinfo[n]); | |
| ss >>= 3; | |
| sign = -sign; | |
| n = newn; | |
| } | |
| trans *= pointSize; | |
| double scale = Math.pow(2./3., n); | |
| at.preConcatenate(AffineTransform.getTranslateInstance(0, trans)); | |
| at.scale(scale, scale); | |
| // note on placement and italics | |
| // We preconcatenate the transform because we don't want to translate along | |
| // the italic angle, but purely perpendicular to the baseline. While this | |
| // looks ok for superscripts, it can lead subscripts to stack on each other | |
| // and bring the following text too close. The way we deal with potential | |
| // collisions that can occur in the case of italics is by adjusting the | |
| // horizontal spacing of the adjacent glyphvectors. Examine the italic | |
| // angle of both vectors, if one is non-zero, compute the minimum ascent | |
| // and descent, and then the x position at each for each vector along its | |
| // italic angle starting from its (offset) baseline. Compute the difference | |
| // between the x positions and use the maximum difference to adjust the | |
| // position of the right gv. | |
| } | |
| if (values.isNonDefault(EWIDTH)) { | |
| at.scale(values.getWidth(), 1f); | |
| } | |
| return at; | |
| } | |
| return new AffineTransform(); | |
| } | |
| // x = r^0 + r^1 + r^2... r^n | |
| // rx = r^1 + r^2 + r^3... r^(n+1) | |
| // x - rx = r^0 - r^(n+1) | |
| // x (1 - r) = r^0 - r^(n+1) | |
| // x = (r^0 - r^(n+1)) / (1 - r) | |
| // x = (1 - r^(n+1)) / (1 - r) | |
| // scale ratio is 2/3 | |
| // trans = 1/2 of ascent * x | |
| // assume ascent is 3/4 of point size | |
| private static final float[] ssinfo = { | |
| 0.0f, | |
| 0.375f, | |
| 0.625f, | |
| 0.7916667f, | |
| 0.9027778f, | |
| 0.9768519f, | |
| 1.0262346f, | |
| 1.0591564f, | |
| }; | |
| /** | |
| * Returns the family name of this {@code Font}. | |
| * | |
| * <p>The family name of a font is font specific. Two fonts such as | |
| * Helvetica Italic and Helvetica Bold have the same family name, | |
| * <i>Helvetica</i>, whereas their font face names are | |
| * <i>Helvetica Bold</i> and <i>Helvetica Italic</i>. The list of | |
| * available family names may be obtained by using the | |
| * {@link GraphicsEnvironment#getAvailableFontFamilyNames()} method. | |
| * | |
| * <p>Use {@code getName} to get the logical name of the font. | |
| * Use {@code getFontName} to get the font face name of the font. | |
| * @return a {@code String} that is the family name of this | |
| * {@code Font}. | |
| * | |
| * @see #getName | |
| * @see #getFontName | |
| * @since 1.1 | |
| */ | |
| public String getFamily() { | |
| return getFamily_NoClientCode(); | |
| } | |
| // NOTE: This method is called by privileged threads. | |
| // We implement this functionality in a package-private | |
| // method to insure that it cannot be overridden by client | |
| // subclasses. | |
| // DO NOT INVOKE CLIENT CODE ON THIS THREAD! | |
| final String getFamily_NoClientCode() { | |
| return getFamily(Locale.getDefault()); | |
| } | |
| /** | |
| * Returns the family name of this {@code Font}, localized for | |
| * the specified locale. | |
| * | |
| * <p>The family name of a font is font specific. Two fonts such as | |
| * Helvetica Italic and Helvetica Bold have the same family name, | |
| * <i>Helvetica</i>, whereas their font face names are | |
| * <i>Helvetica Bold</i> and <i>Helvetica Italic</i>. The list of | |
| * available family names may be obtained by using the | |
| * {@link GraphicsEnvironment#getAvailableFontFamilyNames()} method. | |
| * | |
| * <p>Use {@code getFontName} to get the font face name of the font. | |
| * @param l locale for which to get the family name | |
| * @return a {@code String} representing the family name of the | |
| * font, localized for the specified locale. | |
| * @see #getFontName | |
| * @see java.util.Locale | |
| * @since 1.2 | |
| */ | |
| public String getFamily(Locale l) { | |
| if (l == null) { | |
| throw new NullPointerException("null locale doesn't mean default"); | |
| } | |
| return getFont2D().getFamilyName(l); | |
| } | |
| /** | |
| * Returns the postscript name of this {@code Font}. | |
| * Use {@code getFamily} to get the family name of the font. | |
| * Use {@code getFontName} to get the font face name of the font. | |
| * @return a {@code String} representing the postscript name of | |
| * this {@code Font}. | |
| * @since 1.2 | |
| */ | |
| public String getPSName() { | |
| return getFont2D().getPostscriptName(); | |
| } | |
| /** | |
| * Returns the logical name of this {@code Font}. | |
| * Use {@code getFamily} to get the family name of the font. | |
| * Use {@code getFontName} to get the font face name of the font. | |
| * @return a {@code String} representing the logical name of | |
| * this {@code Font}. | |
| * @see #getFamily | |
| * @see #getFontName | |
| * @since 1.0 | |
| */ | |
| public String getName() { | |
| return name; | |
| } | |
| /** | |
| * Returns the font face name of this {@code Font}. For example, | |
| * Helvetica Bold could be returned as a font face name. | |
| * Use {@code getFamily} to get the family name of the font. | |
| * Use {@code getName} to get the logical name of the font. | |
| * @return a {@code String} representing the font face name of | |
| * this {@code Font}. | |
| * @see #getFamily | |
| * @see #getName | |
| * @since 1.2 | |
| */ | |
| public String getFontName() { | |
| return getFontName(Locale.getDefault()); | |
| } | |
| /** | |
| * Returns the font face name of the {@code Font}, localized | |
| * for the specified locale. For example, Helvetica Fett could be | |
| * returned as the font face name. | |
| * Use {@code getFamily} to get the family name of the font. | |
| * @param l a locale for which to get the font face name | |
| * @return a {@code String} representing the font face name, | |
| * localized for the specified locale. | |
| * @see #getFamily | |
| * @see java.util.Locale | |
| */ | |
| public String getFontName(Locale l) { | |
| if (l == null) { | |
| throw new NullPointerException("null locale doesn't mean default"); | |
| } | |
| return getFont2D().getFontName(l); | |
| } | |
| /** | |
| * Returns the style of this {@code Font}. The style can be | |
| * PLAIN, BOLD, ITALIC, or BOLD+ITALIC. | |
| * @return the style of this {@code Font} | |
| * @see #isPlain | |
| * @see #isBold | |
| * @see #isItalic | |
| * @since 1.0 | |
| */ | |
| public int getStyle() { | |
| return style; | |
| } | |
| /** | |
| * Returns the point size of this {@code Font}, rounded to | |
| * an integer. | |
| * Most users are familiar with the idea of using <i>point size</i> to | |
| * specify the size of glyphs in a font. This point size defines a | |
| * measurement between the baseline of one line to the baseline of the | |
| * following line in a single spaced text document. The point size is | |
| * based on <i>typographic points</i>, approximately 1/72 of an inch. | |
| * <p> | |
| * The Java(tm)2D API adopts the convention that one point is | |
| * equivalent to one unit in user coordinates. When using a | |
| * normalized transform for converting user space coordinates to | |
| * device space coordinates 72 user | |
| * space units equal 1 inch in device space. In this case one point | |
| * is 1/72 of an inch. | |
| * @return the point size of this {@code Font} in 1/72 of an | |
| * inch units. | |
| * @see #getSize2D | |
| * @see GraphicsConfiguration#getDefaultTransform | |
| * @see GraphicsConfiguration#getNormalizingTransform | |
| * @since 1.0 | |
| */ | |
| public int getSize() { | |
| return size; | |
| } | |
| /** | |
| * Returns the point size of this {@code Font} in | |
| * {@code float} value. | |
| * @return the point size of this {@code Font} as a | |
| * {@code float} value. | |
| * @see #getSize | |
| * @since 1.2 | |
| */ | |
| public float getSize2D() { | |
| return pointSize; | |
| } | |
| /** | |
| * Indicates whether or not this {@code Font} object's style is | |
| * PLAIN. | |
| * @return {@code true} if this {@code Font} has a | |
| * PLAIN style; | |
| * {@code false} otherwise. | |
| * @see java.awt.Font#getStyle | |
| * @since 1.0 | |
| */ | |
| public boolean isPlain() { | |
| return style == 0; | |
| } | |
| /** | |
| * Indicates whether or not this {@code Font} object's style is | |
| * BOLD. | |
| * @return {@code true} if this {@code Font} object's | |
| * style is BOLD; | |
| * {@code false} otherwise. | |
| * @see java.awt.Font#getStyle | |
| * @since 1.0 | |
| */ | |
| public boolean isBold() { | |
| return (style & BOLD) != 0; | |
| } | |
| /** | |
| * Indicates whether or not this {@code Font} object's style is | |
| * ITALIC. | |
| * @return {@code true} if this {@code Font} object's | |
| * style is ITALIC; | |
| * {@code false} otherwise. | |
| * @see java.awt.Font#getStyle | |
| * @since 1.0 | |
| */ | |
| public boolean isItalic() { | |
| return (style & ITALIC) != 0; | |
| } | |
| /** | |
| * Indicates whether or not this {@code Font} object has a | |
| * transform that affects its size in addition to the Size | |
| * attribute. | |
| * @return {@code true} if this {@code Font} object | |
| * has a non-identity AffineTransform attribute. | |
| * {@code false} otherwise. | |
| * @see java.awt.Font#getTransform | |
| * @since 1.4 | |
| */ | |
| public boolean isTransformed() { | |
| return nonIdentityTx; | |
| } | |
| /** | |
| * Return true if this Font contains attributes that require extra | |
| * layout processing. | |
| * @return true if the font has layout attributes | |
| * @since 1.6 | |
| */ | |
| public boolean hasLayoutAttributes() { | |
| return hasLayoutAttributes; | |
| } | |
| /** | |
| * Returns a {@code Font} object from the system properties list. | |
| * {@code nm} is treated as the name of a system property to be | |
| * obtained. The {@code String} value of this property is then | |
| * interpreted as a {@code Font} object according to the | |
| * specification of {@code Font.decode(String)} | |
| * If the specified property is not found, null is returned instead. | |
| * | |
| * @param nm the property name | |
| * @return a {@code Font} object that the property name | |
| * describes, or null if no such property exists. | |
| * @throws NullPointerException if nm is null. | |
| * @since 1.2 | |
| * @see #decode(String) | |
| */ | |
| public static Font getFont(String nm) { | |
| return getFont(nm, null); | |
| } | |
| /** | |
| * Returns the {@code Font} that the {@code str} | |
| * argument describes. | |
| * To ensure that this method returns the desired Font, | |
| * format the {@code str} parameter in | |
| * one of these ways | |
| * | |
| * <ul> | |
| * <li><em>fontname-style-pointsize</em> | |
| * <li><em>fontname-pointsize</em> | |
| * <li><em>fontname-style</em> | |
| * <li><em>fontname</em> | |
| * <li><em>fontname style pointsize</em> | |
| * <li><em>fontname pointsize</em> | |
| * <li><em>fontname style</em> | |
| * <li><em>fontname</em> | |
| * </ul> | |
| * in which <i>style</i> is one of the four | |
| * case-insensitive strings: | |
| * {@code "PLAIN"}, {@code "BOLD"}, {@code "BOLDITALIC"}, or | |
| * {@code "ITALIC"}, and pointsize is a positive decimal integer | |
| * representation of the point size. | |
| * For example, if you want a font that is Arial, bold, with | |
| * a point size of 18, you would call this method with: | |
| * "Arial-BOLD-18". | |
| * This is equivalent to calling the Font constructor : | |
| * {@code new Font("Arial", Font.BOLD, 18);} | |
| * and the values are interpreted as specified by that constructor. | |
| * <p> | |
| * A valid trailing decimal field is always interpreted as the pointsize. | |
| * Therefore a fontname containing a trailing decimal value should not | |
| * be used in the fontname only form. | |
| * <p> | |
| * If a style name field is not one of the valid style strings, it is | |
| * interpreted as part of the font name, and the default style is used. | |
| * <p> | |
| * Only one of ' ' or '-' may be used to separate fields in the input. | |
| * The identified separator is the one closest to the end of the string | |
| * which separates a valid pointsize, or a valid style name from | |
| * the rest of the string. | |
| * Null (empty) pointsize and style fields are treated | |
| * as valid fields with the default value for that field. | |
| *<p> | |
| * Some font names may include the separator characters ' ' or '-'. | |
| * If {@code str} is not formed with 3 components, e.g. such that | |
| * {@code style} or {@code pointsize} fields are not present in | |
| * {@code str}, and {@code fontname} also contains a | |
| * character determined to be the separator character | |
| * then these characters where they appear as intended to be part of | |
| * {@code fontname} may instead be interpreted as separators | |
| * so the font name may not be properly recognised. | |
| * | |
| * <p> | |
| * The default size is 12 and the default style is PLAIN. | |
| * If {@code str} does not specify a valid size, the returned | |
| * {@code Font} has a size of 12. If {@code str} does not | |
| * specify a valid style, the returned Font has a style of PLAIN. | |
| * If you do not specify a valid font name in | |
| * the {@code str} argument, this method will return | |
| * a font with the family name "Dialog". | |
| * To determine what font family names are available on | |
| * your system, use the | |
| * {@link GraphicsEnvironment#getAvailableFontFamilyNames()} method. | |
| * If {@code str} is {@code null}, a new {@code Font} | |
| * is returned with the family name "Dialog", a size of 12 and a | |
| * PLAIN style. | |
| * @param str the name of the font, or {@code null} | |
| * @return the {@code Font} object that {@code str} | |
| * describes, or a new default {@code Font} if | |
| * {@code str} is {@code null}. | |
| * @see #getFamily | |
| * @since 1.1 | |
| */ | |
| public static Font decode(String str) { | |
| String fontName = str; | |
| String styleName = ""; | |
| int fontSize = 12; | |
| int fontStyle = Font.PLAIN; | |
| if (str == null) { | |
| return new Font(DIALOG, fontStyle, fontSize); | |
| } | |
| int lastHyphen = str.lastIndexOf('-'); | |
| int lastSpace = str.lastIndexOf(' '); | |
| char sepChar = (lastHyphen > lastSpace) ? '-' : ' '; | |
| int sizeIndex = str.lastIndexOf(sepChar); | |
| int styleIndex = str.lastIndexOf(sepChar, sizeIndex-1); | |
| int strlen = str.length(); | |
| if (sizeIndex > 0 && sizeIndex+1 < strlen) { | |
| try { | |
| fontSize = Integer.parseInt(str.substring(sizeIndex+1)); | |
| if (fontSize <= 0) { | |
| fontSize = 12; | |
| } | |
| } catch (NumberFormatException e) { | |
| /* It wasn't a valid size, if we didn't also find the | |
| * start of the style string perhaps this is the style */ | |
| styleIndex = sizeIndex; | |
| sizeIndex = strlen; | |
| if (str.charAt(sizeIndex-1) == sepChar) { | |
| sizeIndex--; | |
| } | |
| } | |
| } | |
| if (styleIndex >= 0 && styleIndex+1 < strlen) { | |
| styleName = str.substring(styleIndex+1, sizeIndex); | |
| styleName = styleName.toLowerCase(Locale.ENGLISH); | |
| if (styleName.equals("bolditalic")) { | |
| fontStyle = Font.BOLD | Font.ITALIC; | |
| } else if (styleName.equals("italic")) { | |
| fontStyle = Font.ITALIC; | |
| } else if (styleName.equals("bold")) { | |
| fontStyle = Font.BOLD; | |
| } else if (styleName.equals("plain")) { | |
| fontStyle = Font.PLAIN; | |
| } else { | |
| /* this string isn't any of the expected styles, so | |
| * assume its part of the font name | |
| */ | |
| styleIndex = sizeIndex; | |
| if (str.charAt(styleIndex-1) == sepChar) { | |
| styleIndex--; | |
| } | |
| } | |
| fontName = str.substring(0, styleIndex); | |
| } else { | |
| int fontEnd = strlen; | |
| if (styleIndex > 0) { | |
| fontEnd = styleIndex; | |
| } else if (sizeIndex > 0) { | |
| fontEnd = sizeIndex; | |
| } | |
| if (fontEnd > 0 && str.charAt(fontEnd-1) == sepChar) { | |
| fontEnd--; | |
| } | |
| fontName = str.substring(0, fontEnd); | |
| } | |
| return new Font(fontName, fontStyle, fontSize); | |
| } | |
| /** | |
| * Gets the specified {@code Font} from the system properties | |
| * list. As in the {@code getProperty} method of | |
| * {@code System}, the first | |
| * argument is treated as the name of a system property to be | |
| * obtained. The {@code String} value of this property is then | |
| * interpreted as a {@code Font} object. | |
| * <p> | |
| * The property value should be one of the forms accepted by | |
| * {@code Font.decode(String)} | |
| * If the specified property is not found, the {@code font} | |
| * argument is returned instead. | |
| * @param nm the case-insensitive property name | |
| * @param font a default {@code Font} to return if property | |
| * {@code nm} is not defined | |
| * @return the {@code Font} value of the property. | |
| * @throws NullPointerException if nm is null. | |
| * @see #decode(String) | |
| */ | |
| public static Font getFont(String nm, Font font) { | |
| String str = System.getProperty(nm); | |
| if (str == null) { | |
| return font; | |
| } | |
| return decode ( str ); | |
| } | |
| transient int hash; | |
| /** | |
| * Returns a hashcode for this {@code Font}. | |
| * @return a hashcode value for this {@code Font}. | |
| * @since 1.0 | |
| */ | |
| public int hashCode() { | |
| if (hash == 0) { | |
| hash = name.hashCode() ^ style ^ size; | |
| /* It is possible many fonts differ only in transform. | |
| * So include the transform in the hash calculation. | |
| * nonIdentityTx is set whenever there is a transform in | |
| * 'values'. The tests for null are required because it can | |
| * also be set for other reasons. | |
| */ | |
| if (nonIdentityTx && | |
| values != null && values.getTransform() != null) { | |
| hash ^= values.getTransform().hashCode(); | |
| } | |
| } | |
| return hash; | |
| } | |
| /** | |
| * Compares this {@code Font} object to the specified | |
| * {@code Object}. | |
| * @param obj the {@code Object} to compare | |
| * @return {@code true} if the objects are the same | |
| * or if the argument is a {@code Font} object | |
| * describing the same font as this object; | |
| * {@code false} otherwise. | |
| * @since 1.0 | |
| */ | |
| public boolean equals(Object obj) { | |
| if (obj == this) { | |
| return true; | |
| } | |
| if (obj instanceof Font) { | |
| Font font = (Font)obj; | |
| if (size == font.size && | |
| style == font.style && | |
| nonIdentityTx == font.nonIdentityTx && | |
| hasLayoutAttributes == font.hasLayoutAttributes && | |
| pointSize == font.pointSize && | |
| name.equals(font.name)) { | |
| /* 'values' is usually initialized lazily, except when | |
| * the font is constructed from a Map, or derived using | |
| * a Map or other values. So if only one font has | |
| * the field initialized we need to initialize it in | |
| * the other instance and compare. | |
| */ | |
| if (values == null) { | |
| if (font.values == null) { | |
| return true; | |
| } else { | |
| return getAttributeValues().equals(font.values); | |
| } | |
| } else { | |
| return values.equals(font.getAttributeValues()); | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| /** | |
| * Converts this {@code Font} object to a {@code String} | |
| * representation. | |
| * @return a {@code String} representation of this | |
| * {@code Font} object. | |
| * @since 1.0 | |
| */ | |
| // NOTE: This method may be called by privileged threads. | |
| // DO NOT INVOKE CLIENT CODE ON THIS THREAD! | |
| public String toString() { | |
| String strStyle; | |
| if (isBold()) { | |
| strStyle = isItalic() ? "bolditalic" : "bold"; | |
| } else { | |
| strStyle = isItalic() ? "italic" : "plain"; | |
| } | |
| return getClass().getName() + "[family=" + getFamily() + ",name=" + name + ",style=" + | |
| strStyle + ",size=" + size + "]"; | |
| } // toString() | |
| /** Serialization support. A {@code readObject} | |
| * method is necessary because the constructor creates | |
| * the font's peer, and we can't serialize the peer. | |
| * Similarly the computed font "family" may be different | |
| * at {@code readObject} time than at | |
| * {@code writeObject} time. An integer version is | |
| * written so that future versions of this class will be | |
| * able to recognize serialized output from this one. | |
| */ | |
| /** | |
| * The {@code Font} Serializable Data Form. | |
| * | |
| * @serial | |
| */ | |
| private int fontSerializedDataVersion = 1; | |
| /** | |
| * Writes default serializable fields to a stream. | |
| * | |
| * @param s the {@code ObjectOutputStream} to write | |
| * @throws IOException if an I/O error occurs | |
| * @see AWTEventMulticaster#save(ObjectOutputStream, String, EventListener) | |
| * @see #readObject(java.io.ObjectInputStream) | |
| */ | |
| @Serial | |
| private void writeObject(java.io.ObjectOutputStream s) | |
| throws java.io.IOException | |
| { | |
| if (values != null) { | |
| synchronized(values) { | |
| // transient | |
| fRequestedAttributes = values.toSerializableHashtable(); | |
| s.defaultWriteObject(); | |
| fRequestedAttributes = null; | |
| } | |
| } else { | |
| s.defaultWriteObject(); | |
| } | |
| } | |
| /** | |
| * Reads the {@code ObjectInputStream}. | |
| * Unrecognized keys or values will be ignored. | |
| * | |
| * @param s the {@code ObjectInputStream} to read | |
| * @throws ClassNotFoundException if the class of a serialized object could | |
| * not be found | |
| * @throws IOException if an I/O error occurs | |
| * | |
| * @see #writeObject(java.io.ObjectOutputStream) | |
| */ | |
| @Serial | |
| private void readObject(java.io.ObjectInputStream s) | |
| throws java.lang.ClassNotFoundException, | |
| java.io.IOException | |
| { | |
| s.defaultReadObject(); | |
| if (pointSize == 0) { | |
| pointSize = (float)size; | |
| } | |
| // Handle fRequestedAttributes. | |
| // in 1.5, we always streamed out the font values plus | |
| // TRANSFORM, SUPERSCRIPT, and WIDTH, regardless of whether the | |
| // values were default or not. In 1.6 we only stream out | |
| // defined values. So, 1.6 streams in from a 1.5 stream, | |
| // it check each of these values and 'undefines' it if the | |
| // value is the default. | |
| if (fRequestedAttributes != null) { | |
| try { | |
| values = getAttributeValues(); // init | |
| AttributeValues extras = | |
| AttributeValues.fromSerializableHashtable(fRequestedAttributes); | |
| if (!AttributeValues.is16Hashtable(fRequestedAttributes)) { | |
| extras.unsetDefault(); // if legacy stream, undefine these | |
| } | |
| values = getAttributeValues().merge(extras); | |
| this.nonIdentityTx = values.anyNonDefault(EXTRA_MASK); | |
| this.hasLayoutAttributes = values.anyNonDefault(LAYOUT_MASK); | |
| } catch (Throwable t) { | |
| throw new IOException(t); | |
| } finally { | |
| fRequestedAttributes = null; // don't need it any more | |
| } | |
| } | |
| } | |
| /** | |
| * Returns the number of glyphs in this {@code Font}. Glyph codes | |
| * for this {@code Font} range from 0 to | |
| * {@code getNumGlyphs()} - 1. | |
| * @return the number of glyphs in this {@code Font}. | |
| * @since 1.2 | |
| */ | |
| public int getNumGlyphs() { | |
| return getFont2D().getNumGlyphs(); | |
| } | |
| /** | |
| * Returns the glyphCode which is used when this {@code Font} | |
| * does not have a glyph for a specified unicode code point. | |
| * @return the glyphCode of this {@code Font}. | |
| * @since 1.2 | |
| */ | |
| public int getMissingGlyphCode() { | |
| return getFont2D().getMissingGlyphCode(); | |
| } | |
| /** | |
| * Returns the baseline appropriate for displaying this character. | |
| * <p> | |
| * Large fonts can support different writing systems, and each system can | |
| * use a different baseline. | |
| * The character argument determines the writing system to use. Clients | |
| * should not assume all characters use the same baseline. | |
| * | |
| * @param c a character used to identify the writing system | |
| * @return the baseline appropriate for the specified character. | |
| * @see LineMetrics#getBaselineOffsets | |
| * @see #ROMAN_BASELINE | |
| * @see #CENTER_BASELINE | |
| * @see #HANGING_BASELINE | |
| * @since 1.2 | |
| */ | |
| public byte getBaselineFor(char c) { | |
| return getFont2D().getBaselineFor(c); | |
| } | |
| /** | |
| * Returns a map of font attributes available in this | |
| * {@code Font}. Attributes include things like ligatures and | |
| * glyph substitution. | |
| * @return the attributes map of this {@code Font}. | |
| */ | |
| public Map<TextAttribute,?> getAttributes(){ | |
| return new AttributeMap(getAttributeValues()); | |
| } | |
| /** | |
| * Returns the keys of all the attributes supported by this | |
| * {@code Font}. These attributes can be used to derive other | |
| * fonts. | |
| * @return an array containing the keys of all the attributes | |
| * supported by this {@code Font}. | |
| * @since 1.2 | |
| */ | |
| public Attribute[] getAvailableAttributes() { | |
| // FONT is not supported by Font | |
| Attribute[] attributes = { | |
| TextAttribute.FAMILY, | |
| TextAttribute.WEIGHT, | |
| TextAttribute.WIDTH, | |
| TextAttribute.POSTURE, | |
| TextAttribute.SIZE, | |
| TextAttribute.TRANSFORM, | |
| TextAttribute.SUPERSCRIPT, | |
| TextAttribute.CHAR_REPLACEMENT, | |
| TextAttribute.FOREGROUND, | |
| TextAttribute.BACKGROUND, | |
| TextAttribute.UNDERLINE, | |
| TextAttribute.STRIKETHROUGH, | |
| TextAttribute.RUN_DIRECTION, | |
| TextAttribute.BIDI_EMBEDDING, | |
| TextAttribute.JUSTIFICATION, | |
| TextAttribute.INPUT_METHOD_HIGHLIGHT, | |
| TextAttribute.INPUT_METHOD_UNDERLINE, | |
| TextAttribute.SWAP_COLORS, | |
| TextAttribute.NUMERIC_SHAPING, | |
| TextAttribute.KERNING, | |
| TextAttribute.LIGATURES, | |
| TextAttribute.TRACKING, | |
| }; | |
| return attributes; | |
| } | |
| /** | |
| * Creates a new {@code Font} object by replicating this | |
| * {@code Font} object and applying a new style and size. | |
| * @param style the style for the new {@code Font} | |
| * @param size the size for the new {@code Font} | |
| * @return a new {@code Font} object. | |
| * @since 1.2 | |
| */ | |
| public Font deriveFont(int style, float size){ | |
| if (values == null) { | |
| return new Font(name, style, size, createdFont, font2DHandle); | |
| } | |
| AttributeValues newValues = getAttributeValues().clone(); | |
| int oldStyle = (this.style != style) ? this.style : -1; | |
| applyStyle(style, newValues); | |
| newValues.setSize(size); | |
| return new Font(newValues, null, oldStyle, createdFont, font2DHandle); | |
| } | |
| /** | |
| * Creates a new {@code Font} object by replicating this | |
| * {@code Font} object and applying a new style and transform. | |
| * @param style the style for the new {@code Font} | |
| * @param trans the {@code AffineTransform} associated with the | |
| * new {@code Font} | |
| * @return a new {@code Font} object. | |
| * @throws IllegalArgumentException if {@code trans} is | |
| * {@code null} | |
| * @since 1.2 | |
| */ | |
| public Font deriveFont(int style, AffineTransform trans){ | |
| AttributeValues newValues = getAttributeValues().clone(); | |
| int oldStyle = (this.style != style) ? this.style : -1; | |
| applyStyle(style, newValues); | |
| applyTransform(trans, newValues); | |
| return new Font(newValues, null, oldStyle, createdFont, font2DHandle); | |
| } | |
| /** | |
| * Creates a new {@code Font} object by replicating the current | |
| * {@code Font} object and applying a new size to it. | |
| * @param size the size for the new {@code Font}. | |
| * @return a new {@code Font} object. | |
| * @since 1.2 | |
| */ | |
| public Font deriveFont(float size){ | |
| if (values == null) { | |
| return new Font(name, style, size, createdFont, font2DHandle); | |
| } | |
| AttributeValues newValues = getAttributeValues().clone(); | |
| newValues.setSize(size); | |
| return new Font(newValues, null, -1, createdFont, font2DHandle); | |
| } | |
| /** | |
| * Creates a new {@code Font} object by replicating the current | |
| * {@code Font} object and applying a new transform to it. | |
| * @param trans the {@code AffineTransform} associated with the | |
| * new {@code Font} | |
| * @return a new {@code Font} object. | |
| * @throws IllegalArgumentException if {@code trans} is | |
| * {@code null} | |
| * @since 1.2 | |
| */ | |
| public Font deriveFont(AffineTransform trans){ | |
| AttributeValues newValues = getAttributeValues().clone(); | |
| applyTransform(trans, newValues); | |
| return new Font(newValues, null, -1, createdFont, font2DHandle); | |
| } | |
| /** | |
| * Creates a new {@code Font} object by replicating the current | |
| * {@code Font} object and applying a new style to it. | |
| * @param style the style for the new {@code Font} | |
| * @return a new {@code Font} object. | |
| * @since 1.2 | |
| */ | |
| public Font deriveFont(int style){ | |
| if (values == null) { | |
| return new Font(name, style, size, createdFont, font2DHandle); | |
| } | |
| AttributeValues newValues = getAttributeValues().clone(); | |
| int oldStyle = (this.style != style) ? this.style : -1; | |
| applyStyle(style, newValues); | |
| return new Font(newValues, null, oldStyle, createdFont, font2DHandle); | |
| } | |
| /** | |
| * Creates a new {@code Font} object by replicating the current | |
| * {@code Font} object and applying a new set of font attributes | |
| * to it. | |
| * | |
| * @param attributes a map of attributes enabled for the new | |
| * {@code Font} | |
| * @return a new {@code Font} object. | |
| * @since 1.2 | |
| */ | |
| public Font deriveFont(Map<? extends Attribute, ?> attributes) { | |
| if (attributes == null) { | |
| return this; | |
| } | |
| AttributeValues newValues = getAttributeValues().clone(); | |
| newValues.merge(attributes, RECOGNIZED_MASK); | |
| return new Font(newValues, name, style, createdFont, font2DHandle); | |
| } | |
| /** | |
| * Checks if this {@code Font} has a glyph for the specified | |
| * character. | |
| * | |
| * <p> <b>Note:</b> This method cannot handle | |
| * <a href="../../../java.base/java/lang/Character.html#supplementary"> | |
| * supplementary characters</a>. | |
| * To support all Unicode characters, including | |
| * supplementary characters, use the {@link #canDisplay(int)} | |
| * method or {@code canDisplayUpTo} methods. | |
| * | |
| * @param c the character for which a glyph is needed | |
| * @return {@code true} if this {@code Font} has a glyph for this | |
| * character; {@code false} otherwise. | |
| * @since 1.2 | |
| */ | |
| public boolean canDisplay(char c){ | |
| return getFont2D().canDisplay(c); | |
| } | |
| /** | |
| * Checks if this {@code Font} has a glyph for the specified | |
| * character. | |
| * | |
| * @param codePoint the character (Unicode code point) for which a glyph | |
| * is needed. | |
| * @return {@code true} if this {@code Font} has a glyph for the | |
| * character; {@code false} otherwise. | |
| * @throws IllegalArgumentException if the code point is not a valid Unicode | |
| * code point. | |
| * @see Character#isValidCodePoint(int) | |
| * @since 1.5 | |
| */ | |
| public boolean canDisplay(int codePoint) { | |
| if (!Character.isValidCodePoint(codePoint)) { | |
| throw new IllegalArgumentException("invalid code point: " + | |
| Integer.toHexString(codePoint)); | |
| } | |
| return getFont2D().canDisplay(codePoint); | |
| } | |
| /** | |
| * Indicates whether or not this {@code Font} can display a | |
| * specified {@code String}. For strings with Unicode encoding, | |
| * it is important to know if a particular font can display the | |
| * string. This method returns an offset into the {@code String} | |
| * {@code str} which is the first character this | |
| * {@code Font} cannot display without using the missing glyph | |
| * code. If the {@code Font} can display all characters, -1 is | |
| * returned. | |
| * @param str a {@code String} object | |
| * @return an offset into {@code str} that points | |
| * to the first character in {@code str} that this | |
| * {@code Font} cannot display; or {@code -1} if | |
| * this {@code Font} can display all characters in | |
| * {@code str}. | |
| * @since 1.2 | |
| */ | |
| public int canDisplayUpTo(String str) { | |
| Font2D font2d = getFont2D(); | |
| int len = str.length(); | |
| for (int i = 0; i < len; i++) { | |
| char c = str.charAt(i); | |
| if (font2d.canDisplay(c)) { | |
| continue; | |
| } | |
| if (!Character.isHighSurrogate(c)) { | |
| return i; | |
| } | |
| if (!font2d.canDisplay(str.codePointAt(i))) { | |
| return i; | |
| } | |
| i++; | |
| } | |
| return -1; | |
| } | |
| /** | |
| * Indicates whether or not this {@code Font} can display | |
| * the characters in the specified {@code text} | |
| * starting at {@code start} and ending at | |
| * {@code limit}. This method is a convenience overload. | |
| * @param text the specified array of {@code char} values | |
| * @param start the specified starting offset (in | |
| * {@code char}s) into the specified array of | |
| * {@code char} values | |
| * @param limit the specified ending offset (in | |
| * {@code char}s) into the specified array of | |
| * {@code char} values | |
| * @return an offset into {@code text} that points | |
| * to the first character in {@code text} that this | |
| * {@code Font} cannot display; or {@code -1} if | |
| * this {@code Font} can display all characters in | |
| * {@code text}. | |
| * @since 1.2 | |
| */ | |
| public int canDisplayUpTo(char[] text, int start, int limit) { | |
| Font2D font2d = getFont2D(); | |
| for (int i = start; i < limit; i++) { | |
| char c = text[i]; | |
| if (font2d.canDisplay(c)) { | |
| continue; | |
| } | |
| if (!Character.isHighSurrogate(c)) { | |
| return i; | |
| } | |
| if (!font2d.canDisplay(Character.codePointAt(text, i, limit))) { | |
| return i; | |
| } | |
| i++; | |
| } | |
| return -1; | |
| } | |
| /** | |
| * Indicates whether or not this {@code Font} can display the | |
| * text specified by the {@code iter} starting at | |
| * {@code start} and ending at {@code limit}. | |
| * | |
| * @param iter a {@link CharacterIterator} object | |
| * @param start the specified starting offset into the specified | |
| * {@code CharacterIterator}. | |
| * @param limit the specified ending offset into the specified | |
| * {@code CharacterIterator}. | |
| * @return an offset into {@code iter} that points | |
| * to the first character in {@code iter} that this | |
| * {@code Font} cannot display; or {@code -1} if | |
| * this {@code Font} can display all characters in | |
| * {@code iter}. | |
| * @since 1.2 | |
| */ | |
| public int canDisplayUpTo(CharacterIterator iter, int start, int limit) { | |
| Font2D font2d = getFont2D(); | |
| char c = iter.setIndex(start); | |
| for (int i = start; i < limit; i++, c = iter.next()) { | |
| if (font2d.canDisplay(c)) { | |
| continue; | |
| } | |
| if (!Character.isHighSurrogate(c)) { | |
| return i; | |
| } | |
| char c2 = iter.next(); | |
| // c2 could be CharacterIterator.DONE which is not a low surrogate. | |
| if (!Character.isLowSurrogate(c2)) { | |
| return i; | |
| } | |
| if (!font2d.canDisplay(Character.toCodePoint(c, c2))) { | |
| return i; | |
| } | |
| i++; | |
| } | |
| return -1; | |
| } | |
| /** | |
| * Returns the italic angle of this {@code Font}. The italic angle | |
| * is the inverse slope of the caret which best matches the posture of this | |
| * {@code Font}. | |
| * @see TextAttribute#POSTURE | |
| * @return the angle of the ITALIC style of this {@code Font}. | |
| */ | |
| public float getItalicAngle() { | |
| return getItalicAngle(null); | |
| } | |
| /* The FRC hints don't affect the value of the italic angle but | |
| * we need to pass them in to look up a strike. | |
| * If we can pass in ones already being used it can prevent an extra | |
| * strike from being allocated. Note that since italic angle is | |
| * a property of the font, the font transform is needed not the | |
| * device transform. Finally, this is private but the only caller of this | |
| * in the JDK - and the only likely caller - is in this same class. | |
| */ | |
| private float getItalicAngle(FontRenderContext frc) { | |
| Object aa, fm; | |
| if (frc == null) { | |
| aa = RenderingHints.VALUE_TEXT_ANTIALIAS_OFF; | |
| fm = RenderingHints.VALUE_FRACTIONALMETRICS_OFF; | |
| } else { | |
| aa = frc.getAntiAliasingHint(); | |
| fm = frc.getFractionalMetricsHint(); | |
| } | |
| return getFont2D().getItalicAngle(this, identityTx, aa, fm); | |
| } | |
| /** | |
| * Checks whether or not this {@code Font} has uniform | |
| * line metrics. A logical {@code Font} might be a | |
| * composite font, which means that it is composed of different | |
| * physical fonts to cover different code ranges. Each of these | |
| * fonts might have different {@code LineMetrics}. If the | |
| * logical {@code Font} is a single | |
| * font then the metrics would be uniform. | |
| * @return {@code true} if this {@code Font} has | |
| * uniform line metrics; {@code false} otherwise. | |
| */ | |
| public boolean hasUniformLineMetrics() { | |
| return false; // REMIND always safe, but prevents caller optimize | |
| } | |
| private transient SoftReference<FontLineMetrics> flmref; | |
| private FontLineMetrics defaultLineMetrics(FontRenderContext frc) { | |
| FontLineMetrics flm = null; | |
| if (flmref == null | |
| || (flm = flmref.get()) == null | |
| || !flm.frc.equals(frc)) { | |
| /* The device transform in the frc is not used in obtaining line | |
| * metrics, although it probably should be: REMIND find why not? | |
| * The font transform is used but its applied in getFontMetrics, so | |
| * just pass identity here | |
| */ | |
| float [] metrics = new float[8]; | |
| getFont2D().getFontMetrics(this, identityTx, | |
| frc.getAntiAliasingHint(), | |
| frc.getFractionalMetricsHint(), | |
| metrics); | |
| float ascent = metrics[0]; | |
| float descent = metrics[1]; | |
| float leading = metrics[2]; | |
| float ssOffset = 0; | |
| if (values != null && values.getSuperscript() != 0) { | |
| ssOffset = (float)getTransform().getTranslateY(); | |
| ascent -= ssOffset; | |
| descent += ssOffset; | |
| } | |
| float height = ascent + descent + leading; | |
| int baselineIndex = 0; // need real index, assumes roman for everything | |
| // need real baselines eventually | |
| float[] baselineOffsets = { 0, (descent/2f - ascent) / 2f, -ascent }; | |
| float strikethroughOffset = metrics[4]; | |
| float strikethroughThickness = metrics[5]; | |
| float underlineOffset = metrics[6]; | |
| float underlineThickness = metrics[7]; | |
| float italicAngle = getItalicAngle(frc); | |
| if (isTransformed()) { | |
| AffineTransform ctx = values.getCharTransform(); // extract rotation | |
| if (ctx != null) { | |
| Point2D.Float pt = new Point2D.Float(); | |
| pt.setLocation(0, strikethroughOffset); | |
| ctx.deltaTransform(pt, pt); | |
| strikethroughOffset = pt.y; | |
| pt.setLocation(0, strikethroughThickness); | |
| ctx.deltaTransform(pt, pt); | |
| strikethroughThickness = pt.y; | |
| pt.setLocation(0, underlineOffset); | |
| ctx.deltaTransform(pt, pt); | |
| underlineOffset = pt.y; | |
| pt.setLocation(0, underlineThickness); | |
| ctx.deltaTransform(pt, pt); | |
| underlineThickness = pt.y; | |
| } | |
| } | |
| strikethroughOffset += ssOffset; | |
| underlineOffset += ssOffset; | |
| CoreMetrics cm = new CoreMetrics(ascent, descent, leading, height, | |
| baselineIndex, baselineOffsets, | |
| strikethroughOffset, strikethroughThickness, | |
| underlineOffset, underlineThickness, | |
| ssOffset, italicAngle); | |
| flm = new FontLineMetrics(0, cm, frc); | |
| flmref = new SoftReference<FontLineMetrics>(flm); | |
| } | |
| return (FontLineMetrics)flm.clone(); | |
| } | |
| /** | |
| * Returns a {@link LineMetrics} object created with the specified | |
| * {@code String} and {@link FontRenderContext}. | |
| * @param str the specified {@code String} | |
| * @param frc the specified {@code FontRenderContext} | |
| * @return a {@code LineMetrics} object created with the | |
| * specified {@code String} and {@link FontRenderContext}. | |
| */ | |
| public LineMetrics getLineMetrics( String str, FontRenderContext frc) { | |
| FontLineMetrics flm = defaultLineMetrics(frc); | |
| flm.numchars = str.length(); | |
| return flm; | |
| } | |
| /** | |
| * Returns a {@code LineMetrics} object created with the | |
| * specified arguments. | |
| * @param str the specified {@code String} | |
| * @param beginIndex the initial offset of {@code str} | |
| * @param limit the end offset of {@code str} | |
| * @param frc the specified {@code FontRenderContext} | |
| * @return a {@code LineMetrics} object created with the | |
| * specified arguments. | |
| */ | |
| public LineMetrics getLineMetrics( String str, | |
| int beginIndex, int limit, | |
| FontRenderContext frc) { | |
| FontLineMetrics flm = defaultLineMetrics(frc); | |
| int numChars = limit - beginIndex; | |
| flm.numchars = (numChars < 0)? 0: numChars; | |
| return flm; | |
| } | |
| /** | |
| * Returns a {@code LineMetrics} object created with the | |
| * specified arguments. | |
| * @param chars an array of characters | |
| * @param beginIndex the initial offset of {@code chars} | |
| * @param limit the end offset of {@code chars} | |
| * @param frc the specified {@code FontRenderContext} | |
| * @return a {@code LineMetrics} object created with the | |
| * specified arguments. | |
| */ | |
| public LineMetrics getLineMetrics(char [] chars, | |
| int beginIndex, int limit, | |
| FontRenderContext frc) { | |
| FontLineMetrics flm = defaultLineMetrics(frc); | |
| int numChars = limit - beginIndex; | |
| flm.numchars = (numChars < 0)? 0: numChars; | |
| return flm; | |
| } | |
| /** | |
| * Returns a {@code LineMetrics} object created with the | |
| * specified arguments. | |
| * @param ci the specified {@code CharacterIterator} | |
| * @param beginIndex the initial offset in {@code ci} | |
| * @param limit the end offset of {@code ci} | |
| * @param frc the specified {@code FontRenderContext} | |
| * @return a {@code LineMetrics} object created with the | |
| * specified arguments. | |
| */ | |
| public LineMetrics getLineMetrics(CharacterIterator ci, | |
| int beginIndex, int limit, | |
| FontRenderContext frc) { | |
| FontLineMetrics flm = defaultLineMetrics(frc); | |
| int numChars = limit - beginIndex; | |
| flm.numchars = (numChars < 0)? 0: numChars; | |
| return flm; | |
| } | |
| /** | |
| * Returns the logical bounds of the specified {@code String} in | |
| * the specified {@code FontRenderContext}. The logical bounds | |
| * contains the origin, ascent, advance, and height, which includes | |
| * the leading. The logical bounds does not always enclose all the | |
| * text. For example, in some languages and in some fonts, accent | |
| * marks can be positioned above the ascent or below the descent. | |
| * To obtain a visual bounding box, which encloses all the text, | |
| * use the {@link TextLayout#getBounds() getBounds} method of | |
| * {@code TextLayout}. | |
| * <p>Note: The returned bounds is in baseline-relative coordinates | |
| * (see {@link java.awt.Font class notes}). | |
| * @param str the specified {@code String} | |
| * @param frc the specified {@code FontRenderContext} | |
| * @return a {@link Rectangle2D} that is the bounding box of the | |
| * specified {@code String} in the specified | |
| * {@code FontRenderContext}. | |
| * @see FontRenderContext | |
| * @see Font#createGlyphVector | |
| * @since 1.2 | |
| */ | |
| public Rectangle2D getStringBounds( String str, FontRenderContext frc) { | |
| char[] array = str.toCharArray(); | |
| return getStringBounds(array, 0, array.length, frc); | |
| } | |
| /** | |
| * Returns the logical bounds of the specified {@code String} in | |
| * the specified {@code FontRenderContext}. The logical bounds | |
| * contains the origin, ascent, advance, and height, which includes | |
| * the leading. The logical bounds does not always enclose all the | |
| * text. For example, in some languages and in some fonts, accent | |
| * marks can be positioned above the ascent or below the descent. | |
| * To obtain a visual bounding box, which encloses all the text, | |
| * use the {@link TextLayout#getBounds() getBounds} method of | |
| * {@code TextLayout}. | |
| * <p>Note: The returned bounds is in baseline-relative coordinates | |
| * (see {@link java.awt.Font class notes}). | |
| * @param str the specified {@code String} | |
| * @param beginIndex the initial offset of {@code str} | |
| * @param limit the end offset of {@code str} | |
| * @param frc the specified {@code FontRenderContext} | |
| * @return a {@code Rectangle2D} that is the bounding box of the | |
| * specified {@code String} in the specified | |
| * {@code FontRenderContext}. | |
| * @throws IndexOutOfBoundsException if {@code beginIndex} is | |
| * less than zero, or {@code limit} is greater than the | |
| * length of {@code str}, or {@code beginIndex} | |
| * is greater than {@code limit}. | |
| * @see FontRenderContext | |
| * @see Font#createGlyphVector | |
| * @since 1.2 | |
| */ | |
| public Rectangle2D getStringBounds( String str, | |
| int beginIndex, int limit, | |
| FontRenderContext frc) { | |
| String substr = str.substring(beginIndex, limit); | |
| return getStringBounds(substr, frc); | |
| } | |
| /** | |
| * Returns the logical bounds of the specified array of characters | |
| * in the specified {@code FontRenderContext}. The logical | |
| * bounds contains the origin, ascent, advance, and height, which | |
| * includes the leading. The logical bounds does not always enclose | |
| * all the text. For example, in some languages and in some fonts, | |
| * accent marks can be positioned above the ascent or below the | |
| * descent. To obtain a visual bounding box, which encloses all the | |
| * text, use the {@link TextLayout#getBounds() getBounds} method of | |
| * {@code TextLayout}. | |
| * <p>Note: The returned bounds is in baseline-relative coordinates | |
| * (see {@link java.awt.Font class notes}). | |
| * @param chars an array of characters | |
| * @param beginIndex the initial offset in the array of | |
| * characters | |
| * @param limit the end offset in the array of characters | |
| * @param frc the specified {@code FontRenderContext} | |
| * @return a {@code Rectangle2D} that is the bounding box of the | |
| * specified array of characters in the specified | |
| * {@code FontRenderContext}. | |
| * @throws IndexOutOfBoundsException if {@code beginIndex} is | |
| * less than zero, or {@code limit} is greater than the | |
| * length of {@code chars}, or {@code beginIndex} | |
| * is greater than {@code limit}. | |
| * @see FontRenderContext | |
| * @see Font#createGlyphVector | |
| * @since 1.2 | |
| */ | |
| public Rectangle2D getStringBounds(char [] chars, | |
| int beginIndex, int limit, | |
| FontRenderContext frc) { | |
| if (beginIndex < 0) { | |
| throw new IndexOutOfBoundsException("beginIndex: " + beginIndex); | |
| } | |
| if (limit > chars.length) { | |
| throw new IndexOutOfBoundsException("limit: " + limit); | |
| } | |
| if (beginIndex > limit) { | |
| throw new IndexOutOfBoundsException("range length: " + | |
| (limit - beginIndex)); | |
| } | |
| // this code should be in textlayout | |
| // quick check for simple text, assume GV ok to use if simple | |
| boolean simple = values == null || | |
| (values.getKerning() == 0 | |
| && values.getLigatures() == 0 | |
| && values.getTracking() == 0 | |
| && values.getBaselineTransform() == null); | |
| if (simple) { | |
| simple = ! FontUtilities.isComplexText(chars, beginIndex, limit); | |
| } | |
| if (simple || ((limit - beginIndex) == 0)) { | |
| FontDesignMetrics metrics = FontDesignMetrics.getMetrics(this, frc); | |
| return metrics.getSimpleBounds(chars, beginIndex, limit-beginIndex); | |
| } else { | |
| // need char array constructor on textlayout | |
| String str = new String(chars, beginIndex, limit - beginIndex); | |
| TextLayout tl = new TextLayout(str, this, frc); | |
| return new Rectangle2D.Float(0, -tl.getAscent(), tl.getAdvance(), | |
| tl.getAscent() + tl.getDescent() + | |
| tl.getLeading()); | |
| } | |
| } | |
| /** | |
| * Returns the logical bounds of the characters indexed in the | |
| * specified {@link CharacterIterator} in the | |
| * specified {@code FontRenderContext}. The logical bounds | |
| * contains the origin, ascent, advance, and height, which includes | |
| * the leading. The logical bounds does not always enclose all the | |
| * text. For example, in some languages and in some fonts, accent | |
| * marks can be positioned above the ascent or below the descent. | |
| * To obtain a visual bounding box, which encloses all the text, | |
| * use the {@link TextLayout#getBounds() getBounds} method of | |
| * {@code TextLayout}. | |
| * <p>Note: The returned bounds is in baseline-relative coordinates | |
| * (see {@link java.awt.Font class notes}). | |
| * @param ci the specified {@code CharacterIterator} | |
| * @param beginIndex the initial offset in {@code ci} | |
| * @param limit the end offset in {@code ci} | |
| * @param frc the specified {@code FontRenderContext} | |
| * @return a {@code Rectangle2D} that is the bounding box of the | |
| * characters indexed in the specified {@code CharacterIterator} | |
| * in the specified {@code FontRenderContext}. | |
| * @see FontRenderContext | |
| * @see Font#createGlyphVector | |
| * @since 1.2 | |
| * @throws IndexOutOfBoundsException if {@code beginIndex} is | |
| * less than the start index of {@code ci}, or | |
| * {@code limit} is greater than the end index of | |
| * {@code ci}, or {@code beginIndex} is greater | |
| * than {@code limit} | |
| */ | |
| public Rectangle2D getStringBounds(CharacterIterator ci, | |
| int beginIndex, int limit, | |
| FontRenderContext frc) { | |
| int start = ci.getBeginIndex(); | |
| int end = ci.getEndIndex(); | |
| if (beginIndex < start) { | |
| throw new IndexOutOfBoundsException("beginIndex: " + beginIndex); | |
| } | |
| if (limit > end) { | |
| throw new IndexOutOfBoundsException("limit: " + limit); | |
| } | |
| if (beginIndex > limit) { | |
| throw new IndexOutOfBoundsException("range length: " + | |
| (limit - beginIndex)); | |
| } | |
| char[] arr = new char[limit - beginIndex]; | |
| ci.setIndex(beginIndex); | |
| for(int idx = 0; idx < arr.length; idx++) { | |
| arr[idx] = ci.current(); | |
| ci.next(); | |
| } | |
| return getStringBounds(arr,0,arr.length,frc); | |
| } | |
| /** | |
| * Returns the bounds for the character with the maximum | |
| * bounds as defined in the specified {@code FontRenderContext}. | |
| * <p>Note: The returned bounds is in baseline-relative coordinates | |
| * (see {@link java.awt.Font class notes}). | |
| * @param frc the specified {@code FontRenderContext} | |
| * @return a {@code Rectangle2D} that is the bounding box | |
| * for the character with the maximum bounds. | |
| */ | |
| public Rectangle2D getMaxCharBounds(FontRenderContext frc) { | |
| float [] metrics = new float[4]; | |
| getFont2D().getFontMetrics(this, frc, metrics); | |
| return new Rectangle2D.Float(0, -metrics[0], | |
| metrics[3], | |
| metrics[0] + metrics[1] + metrics[2]); | |
| } | |
| /** | |
| * Creates a {@link java.awt.font.GlyphVector GlyphVector} by | |
| * mapping characters to glyphs one-to-one based on the | |
| * Unicode cmap in this {@code Font}. This method does no other | |
| * processing besides the mapping of glyphs to characters. This | |
| * means that this method is not useful for some scripts, such | |
| * as Arabic, Hebrew, Thai, and Indic, that require reordering, | |
| * shaping, or ligature substitution. | |
| * @param frc the specified {@code FontRenderContext} | |
| * @param str the specified {@code String} | |
| * @return a new {@code GlyphVector} created with the | |
| * specified {@code String} and the specified | |
| * {@code FontRenderContext}. | |
| */ | |
| public GlyphVector createGlyphVector(FontRenderContext frc, String str) | |
| { | |
| return (GlyphVector)new StandardGlyphVector(this, str, frc); | |
| } | |
| /** | |
| * Creates a {@link java.awt.font.GlyphVector GlyphVector} by | |
| * mapping characters to glyphs one-to-one based on the | |
| * Unicode cmap in this {@code Font}. This method does no other | |
| * processing besides the mapping of glyphs to characters. This | |
| * means that this method is not useful for some scripts, such | |
| * as Arabic, Hebrew, Thai, and Indic, that require reordering, | |
| * shaping, or ligature substitution. | |
| * @param frc the specified {@code FontRenderContext} | |
| * @param chars the specified array of characters | |
| * @return a new {@code GlyphVector} created with the | |
| * specified array of characters and the specified | |
| * {@code FontRenderContext}. | |
| */ | |
| public GlyphVector createGlyphVector(FontRenderContext frc, char[] chars) | |
| { | |
| return (GlyphVector)new StandardGlyphVector(this, chars, frc); | |
| } | |
| /** | |
| * Creates a {@link java.awt.font.GlyphVector GlyphVector} by | |
| * mapping the specified characters to glyphs one-to-one based on the | |
| * Unicode cmap in this {@code Font}. This method does no other | |
| * processing besides the mapping of glyphs to characters. This | |
| * means that this method is not useful for some scripts, such | |
| * as Arabic, Hebrew, Thai, and Indic, that require reordering, | |
| * shaping, or ligature substitution. | |
| * @param frc the specified {@code FontRenderContext} | |
| * @param ci the specified {@code CharacterIterator} | |
| * @return a new {@code GlyphVector} created with the | |
| * specified {@code CharacterIterator} and the specified | |
| * {@code FontRenderContext}. | |
| */ | |
| public GlyphVector createGlyphVector( FontRenderContext frc, | |
| CharacterIterator ci) | |
| { | |
| return (GlyphVector)new StandardGlyphVector(this, ci, frc); | |
| } | |
| /** | |
| * Creates a {@link java.awt.font.GlyphVector GlyphVector} by | |
| * mapping characters to glyphs one-to-one based on the | |
| * Unicode cmap in this {@code Font}. This method does no other | |
| * processing besides the mapping of glyphs to characters. This | |
| * means that this method is not useful for some scripts, such | |
| * as Arabic, Hebrew, Thai, and Indic, that require reordering, | |
| * shaping, or ligature substitution. | |
| * @param frc the specified {@code FontRenderContext} | |
| * @param glyphCodes the specified integer array | |
| * @return a new {@code GlyphVector} created with the | |
| * specified integer array and the specified | |
| * {@code FontRenderContext}. | |
| */ | |
| public GlyphVector createGlyphVector( FontRenderContext frc, | |
| int [] glyphCodes) | |
| { | |
| return (GlyphVector)new StandardGlyphVector(this, glyphCodes, frc); | |
| } | |
| /** | |
| * Returns a new {@code GlyphVector} object, performing full | |
| * layout of the text if possible. Full layout is required for | |
| * complex text, such as Arabic or Hindi. Support for different | |
| * scripts depends on the font and implementation. | |
| * <p> | |
| * Layout requires bidi analysis, as performed by | |
| * {@code Bidi}, and should only be performed on text that | |
| * has a uniform direction. The direction is indicated in the | |
| * flags parameter,by using LAYOUT_RIGHT_TO_LEFT to indicate a | |
| * right-to-left (Arabic and Hebrew) run direction, or | |
| * LAYOUT_LEFT_TO_RIGHT to indicate a left-to-right (English) | |
| * run direction. | |
| * <p> | |
| * In addition, some operations, such as Arabic shaping, require | |
| * context, so that the characters at the start and limit can have | |
| * the proper shapes. Sometimes the data in the buffer outside | |
| * the provided range does not have valid data. The values | |
| * LAYOUT_NO_START_CONTEXT and LAYOUT_NO_LIMIT_CONTEXT can be | |
| * added to the flags parameter to indicate that the text before | |
| * start, or after limit, respectively, should not be examined | |
| * for context. | |
| * <p> | |
| * All other values for the flags parameter are reserved. | |
| * | |
| * @param frc the specified {@code FontRenderContext} | |
| * @param text the text to layout | |
| * @param start the start of the text to use for the {@code GlyphVector} | |
| * @param limit the limit of the text to use for the {@code GlyphVector} | |
| * @param flags control flags as described above | |
| * @return a new {@code GlyphVector} representing the text between | |
| * start and limit, with glyphs chosen and positioned so as to best represent | |
| * the text | |
| * @throws ArrayIndexOutOfBoundsException if start or limit is | |
| * out of bounds | |
| * @see java.text.Bidi | |
| * @see #LAYOUT_LEFT_TO_RIGHT | |
| * @see #LAYOUT_RIGHT_TO_LEFT | |
| * @see #LAYOUT_NO_START_CONTEXT | |
| * @see #LAYOUT_NO_LIMIT_CONTEXT | |
| * @since 1.4 | |
| */ | |
| public GlyphVector layoutGlyphVector(FontRenderContext frc, | |
| char[] text, | |
| int start, | |
| int limit, | |
| int flags) { | |
| GlyphLayout gl = GlyphLayout.get(null); // !!! no custom layout engines | |
| StandardGlyphVector gv = gl.layout(this, frc, text, | |
| start, limit-start, flags, null); | |
| GlyphLayout.done(gl); | |
| return gv; | |
| } | |
| /** | |
| * A flag to layoutGlyphVector indicating that text is left-to-right as | |
| * determined by Bidi analysis. | |
| */ | |
| public static final int LAYOUT_LEFT_TO_RIGHT = 0; | |
| /** | |
| * A flag to layoutGlyphVector indicating that text is right-to-left as | |
| * determined by Bidi analysis. | |
| */ | |
| public static final int LAYOUT_RIGHT_TO_LEFT = 1; | |
| /** | |
| * A flag to layoutGlyphVector indicating that text in the char array | |
| * before the indicated start should not be examined. | |
| */ | |
| public static final int LAYOUT_NO_START_CONTEXT = 2; | |
| /** | |
| * A flag to layoutGlyphVector indicating that text in the char array | |
| * after the indicated limit should not be examined. | |
| */ | |
| public static final int LAYOUT_NO_LIMIT_CONTEXT = 4; | |
| private static void applyTransform(AffineTransform trans, AttributeValues values) { | |
| if (trans == null) { | |
| throw new IllegalArgumentException("transform must not be null"); | |
| } | |
| values.setTransform(trans); | |
| } | |
| private static void applyStyle(int style, AttributeValues values) { | |
| // WEIGHT_BOLD, WEIGHT_REGULAR | |
| values.setWeight((style & BOLD) != 0 ? 2f : 1f); | |
| // POSTURE_OBLIQUE, POSTURE_REGULAR | |
| values.setPosture((style & ITALIC) != 0 ? .2f : 0f); | |
| } | |
| /* | |
| * Initialize JNI field and method IDs | |
| */ | |
| private static native void initIDs(); | |
| } |