/* * Copyright 1997-2006 Sun Microsystems, Inc. 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. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ package sun.font; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.awt.FontMetrics; import java.awt.Font; import java.awt.GraphicsEnvironment; import java.awt.geom.AffineTransform; import java.awt.geom.NoninvertibleTransformException; import java.awt.font.FontRenderContext; import java.awt.font.TextLayout; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.concurrent.ConcurrentHashMap; import sun.java2d.Disposer; import sun.java2d.DisposerRecord; /* * This class provides a summary of the glyph measurements for a Font * and a set of hints that guide their display. It provides more metrics * information for the Font than the java.awt.FontMetrics class. There * is also some redundancy with that class. *
* The design metrics for a Font are obtained from Font.getDesignMetrics(). * The FontDesignMetrics object returned will be independent of the * point size of the Font. * Most users are familiar with the idea of using point size 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 typographic points, approximately 1/72 of an inch. *
* The Java2D 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 (see * GraphicsConfiguration.getDefaultTransform() and * GraphicsConfiguration.getNormalizingTransform()), 72 user space units * equal 1 inch in device space. In this case one point is 1/72 of an inch. *
* The FontDesignMetrics class expresses font metrics in terms of arbitrary * typographic units (not points) chosen by the font supplier * and used in the underlying platform font representations. These units are * defined by dividing the em-square into a grid. The em-sqaure is the * theoretical square whose dimensions are the full body height of the * font. A typographic unit is the smallest measurable unit in the * em-square. The number of units-per-em is determined by the font * designer. The greater the units-per-em, the greater the precision * in metrics. For example, Type 1 fonts divide the em-square into a * 1000 x 1000 grid, while TrueType fonts typically use a 2048 x 2048 * grid. The scale of these units can be obtained by calling * getUnitsPerEm(). *
* Typographic units are relative -- their absolute size changes as the * size of the of the em-square changes. An em-square is 9 points high * in a 9-point font. Because typographic units are relative to the * em-square, a given location on a glyph will have the same coordinates * in typographic units regardless of the point size. *
* Converting typographic units to pixels requires computing pixels-per-em * (ppem). This can be computed as: *
ppem = device_resolution * (inches-per-point) * pointSize ** where device resolution could be measured in pixels/inch and the point * size of a font is effectively points/em. Using a normalized transform * from user space to device space (see above), results in 1/72 inch/point. * In this case, ppem is equal to the point size on a 72 dpi monitor, so * that an N point font displays N pixels high. In general, *
pixel_units = typographic_units * (ppem / units_per_em) ** @see java.awt.Font * @see java.awt.GraphicsConfiguration#getDefaultTransform * @see java.awt.GraphicsConfiguration#getNormalizingTransform */ public final class FontDesignMetrics extends FontMetrics { static final long serialVersionUID = 4480069578560887773L; private static final float UNKNOWN_WIDTH = -1; private static final int CURRENT_VERSION = 1; // height, ascent, descent, leading are reported to the client // as an integer this value is added to the true fp value to // obtain a value which is usually going to result in a round up // to the next integer except for very marginal cases. private static float roundingUpValue = 0.95f; // These fields are all part of the old serialization representation private Font font; private float ascent; private float descent; private float leading; private float maxAdvance; private double[] matrix; private int[] cache; // now unused, still here only for serialization // End legacy serialization fields private int serVersion = 0; // If 1 in readObject, these fields are on the input stream: private boolean isAntiAliased; private boolean usesFractionalMetrics; private AffineTransform frcTx; private transient float[] advCache; // transient since values could change across runtimes private transient int height = -1; private transient FontRenderContext frc; private transient double[] devmatrix = null; private transient FontStrike fontStrike; private static FontRenderContext DEFAULT_FRC = null; private static FontRenderContext getDefaultFrc() { if (DEFAULT_FRC == null) { AffineTransform tx; if (GraphicsEnvironment.isHeadless()) { tx = new AffineTransform(); } else { tx = GraphicsEnvironment .getLocalGraphicsEnvironment() .getDefaultScreenDevice() .getDefaultConfiguration() .getDefaultTransform(); } DEFAULT_FRC = new FontRenderContext(tx, false, false); } return DEFAULT_FRC; } /* Strongly cache up to 5 most recently requested FontMetrics objects, * and softly cache as many as GC allows. In practice this means we * should keep references around until memory gets low. * We key the cache either by a Font or a combination of the Font and * and FRC. A lot of callers use only the font so although there's code * duplication, we allow just a font to be a key implying a default FRC. * Also we put the references on a queue so that if they do get nulled * out we can clear the keys from the table. */ private static class KeyReference extends SoftReference implements DisposerRecord { static ReferenceQueue queue = Disposer.getQueue(); Object key; KeyReference(Object key, Object value) { super(value, queue); this.key = key; Disposer.addReference(this, this); } /* It is possible that since this reference object has been * enqueued, that a new metrics has been put into the table * for the same key value. So we'll test to see if the table maps * to THIS reference. If its a new one, we'll leave it alone. * It is possible that a new entry comes in after our test, but * it is unlikely and if this were a problem we would need to * synchronize all 'put' and 'remove' accesses to the cache which * I would prefer not to do. */ public void dispose() { if (metricsCache.get(key) == this) { metricsCache.remove(key); } } } private static class MetricsKey { Font font; FontRenderContext frc; int hash; MetricsKey() { } MetricsKey(Font font, FontRenderContext frc) { init(font, frc); } void init(Font font, FontRenderContext frc) { this.font = font; this.frc = frc; this.hash = font.hashCode() + frc.hashCode(); } public boolean equals(Object key) { if (!(key instanceof MetricsKey)) { return false; } return font.equals(((MetricsKey)key).font) && frc.equals(((MetricsKey)key).frc); } public int hashCode() { return hash; } /* Synchronize access to this on the class */ static final MetricsKey key = new MetricsKey(); } /* All accesses to a CHM do not in general need to be synchronized, * as incomplete operations on another thread would just lead to * harmless cache misses. */ private static final ConcurrentHashMap