/* * 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