0N/A/*
2362N/A * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.font;
0N/A
0N/Aimport java.lang.ref.ReferenceQueue;
0N/Aimport java.lang.ref.SoftReference;
0N/A
0N/Aimport java.awt.FontMetrics;
0N/Aimport java.awt.Font;
0N/Aimport java.awt.GraphicsEnvironment;
0N/Aimport java.awt.geom.AffineTransform;
0N/Aimport java.awt.geom.NoninvertibleTransformException;
0N/Aimport java.awt.font.FontRenderContext;
0N/Aimport java.awt.font.TextLayout;
0N/A
0N/Aimport java.io.IOException;
0N/Aimport java.io.ObjectInputStream;
0N/Aimport java.io.ObjectOutputStream;
0N/A
0N/Aimport java.util.concurrent.ConcurrentHashMap;
0N/A
0N/Aimport sun.java2d.Disposer;
0N/Aimport sun.java2d.DisposerRecord;
0N/A
0N/A/*
0N/A * This class provides a summary of the glyph measurements for a Font
0N/A * and a set of hints that guide their display. It provides more metrics
0N/A * information for the Font than the java.awt.FontMetrics class. There
0N/A * is also some redundancy with that class.
0N/A * <p>
0N/A * The design metrics for a Font are obtained from Font.getDesignMetrics().
0N/A * The FontDesignMetrics object returned will be independent of the
0N/A * point size of the Font.
0N/A * Most users are familiar with the idea of using <i>point size</i> to
0N/A * specify the size of glyphs in a font. This point size defines a
0N/A * measurement between the baseline of one line to the baseline of the
0N/A * following line in a single spaced text document. The point size is
0N/A * based on <i>typographic points</i>, approximately 1/72 of an inch.
0N/A * <p>
0N/A * The Java2D API adopts the convention that one point is equivalent
0N/A * to one unit in user coordinates. When using a normalized transform
0N/A * for converting user space coordinates to device space coordinates (see
0N/A * GraphicsConfiguration.getDefaultTransform() and
0N/A * GraphicsConfiguration.getNormalizingTransform()), 72 user space units
0N/A * equal 1 inch in device space. In this case one point is 1/72 of an inch.
0N/A * <p>
0N/A * The FontDesignMetrics class expresses font metrics in terms of arbitrary
0N/A * <i>typographic units</i> (not points) chosen by the font supplier
0N/A * and used in the underlying platform font representations. These units are
0N/A * defined by dividing the em-square into a grid. The em-sqaure is the
0N/A * theoretical square whose dimensions are the full body height of the
0N/A * font. A typographic unit is the smallest measurable unit in the
0N/A * em-square. The number of units-per-em is determined by the font
0N/A * designer. The greater the units-per-em, the greater the precision
0N/A * in metrics. For example, Type 1 fonts divide the em-square into a
0N/A * 1000 x 1000 grid, while TrueType fonts typically use a 2048 x 2048
0N/A * grid. The scale of these units can be obtained by calling
0N/A * getUnitsPerEm().
0N/A * <p>
0N/A * Typographic units are relative -- their absolute size changes as the
0N/A * size of the of the em-square changes. An em-square is 9 points high
0N/A * in a 9-point font. Because typographic units are relative to the
0N/A * em-square, a given location on a glyph will have the same coordinates
0N/A * in typographic units regardless of the point size.
0N/A * <p>
0N/A * Converting typographic units to pixels requires computing pixels-per-em
0N/A * (ppem). This can be computed as:
0N/A * <pre>
0N/A ppem = device_resolution * (inches-per-point) * pointSize
0N/A * </pre>
0N/A * where device resolution could be measured in pixels/inch and the point
0N/A * size of a font is effectively points/em. Using a normalized transform
0N/A * from user space to device space (see above), results in 1/72 inch/point.
0N/A * In this case, ppem is equal to the point size on a 72 dpi monitor, so
0N/A * that an N point font displays N pixels high. In general,
0N/A * <pre>
0N/A pixel_units = typographic_units * (ppem / units_per_em)
0N/A * </pre>
0N/A * @see java.awt.Font
0N/A * @see java.awt.GraphicsConfiguration#getDefaultTransform
0N/A * @see java.awt.GraphicsConfiguration#getNormalizingTransform
0N/A */
0N/A
0N/Apublic final class FontDesignMetrics extends FontMetrics {
0N/A
0N/A static final long serialVersionUID = 4480069578560887773L;
0N/A
0N/A private static final float UNKNOWN_WIDTH = -1;
0N/A private static final int CURRENT_VERSION = 1;
0N/A
0N/A // height, ascent, descent, leading are reported to the client
0N/A // as an integer this value is added to the true fp value to
0N/A // obtain a value which is usually going to result in a round up
0N/A // to the next integer except for very marginal cases.
0N/A private static float roundingUpValue = 0.95f;
0N/A
0N/A // These fields are all part of the old serialization representation
0N/A private Font font;
0N/A private float ascent;
0N/A private float descent;
0N/A private float leading;
0N/A private float maxAdvance;
0N/A private double[] matrix;
0N/A private int[] cache; // now unused, still here only for serialization
0N/A // End legacy serialization fields
0N/A
0N/A private int serVersion = 0; // If 1 in readObject, these fields are on the input stream:
0N/A private boolean isAntiAliased;
0N/A private boolean usesFractionalMetrics;
0N/A private AffineTransform frcTx;
0N/A
0N/A private transient float[] advCache; // transient since values could change across runtimes
0N/A private transient int height = -1;
0N/A
0N/A private transient FontRenderContext frc;
0N/A
0N/A private transient double[] devmatrix = null;
0N/A
0N/A private transient FontStrike fontStrike;
0N/A
0N/A private static FontRenderContext DEFAULT_FRC = null;
0N/A
0N/A private static FontRenderContext getDefaultFrc() {
0N/A
0N/A if (DEFAULT_FRC == null) {
0N/A AffineTransform tx;
0N/A if (GraphicsEnvironment.isHeadless()) {
0N/A tx = new AffineTransform();
0N/A } else {
0N/A tx = GraphicsEnvironment
0N/A .getLocalGraphicsEnvironment()
0N/A .getDefaultScreenDevice()
0N/A .getDefaultConfiguration()
0N/A .getDefaultTransform();
0N/A }
0N/A DEFAULT_FRC = new FontRenderContext(tx, false, false);
0N/A }
0N/A return DEFAULT_FRC;
0N/A }
0N/A
0N/A /* Strongly cache up to 5 most recently requested FontMetrics objects,
0N/A * and softly cache as many as GC allows. In practice this means we
0N/A * should keep references around until memory gets low.
0N/A * We key the cache either by a Font or a combination of the Font and
0N/A * and FRC. A lot of callers use only the font so although there's code
0N/A * duplication, we allow just a font to be a key implying a default FRC.
0N/A * Also we put the references on a queue so that if they do get nulled
0N/A * out we can clear the keys from the table.
0N/A */
0N/A private static class KeyReference extends SoftReference
1886N/A implements DisposerRecord, Disposer.PollDisposable {
0N/A
0N/A static ReferenceQueue queue = Disposer.getQueue();
0N/A
0N/A Object key;
0N/A
0N/A KeyReference(Object key, Object value) {
0N/A super(value, queue);
0N/A this.key = key;
0N/A Disposer.addReference(this, this);
0N/A }
0N/A
0N/A /* It is possible that since this reference object has been
0N/A * enqueued, that a new metrics has been put into the table
0N/A * for the same key value. So we'll test to see if the table maps
0N/A * to THIS reference. If its a new one, we'll leave it alone.
0N/A * It is possible that a new entry comes in after our test, but
0N/A * it is unlikely and if this were a problem we would need to
0N/A * synchronize all 'put' and 'remove' accesses to the cache which
0N/A * I would prefer not to do.
0N/A */
0N/A public void dispose() {
0N/A if (metricsCache.get(key) == this) {
0N/A metricsCache.remove(key);
0N/A }
0N/A }
0N/A }
0N/A
0N/A private static class MetricsKey {
0N/A Font font;
0N/A FontRenderContext frc;
0N/A int hash;
0N/A
0N/A MetricsKey() {
0N/A }
0N/A
0N/A MetricsKey(Font font, FontRenderContext frc) {
0N/A init(font, frc);
0N/A }
0N/A
0N/A void init(Font font, FontRenderContext frc) {
0N/A this.font = font;
0N/A this.frc = frc;
0N/A this.hash = font.hashCode() + frc.hashCode();
0N/A }
0N/A
0N/A public boolean equals(Object key) {
0N/A if (!(key instanceof MetricsKey)) {
0N/A return false;
0N/A }
0N/A return
0N/A font.equals(((MetricsKey)key).font) &&
0N/A frc.equals(((MetricsKey)key).frc);
0N/A }
0N/A
0N/A public int hashCode() {
0N/A return hash;
0N/A }
0N/A
0N/A /* Synchronize access to this on the class */
0N/A static final MetricsKey key = new MetricsKey();
0N/A }
0N/A
0N/A /* All accesses to a CHM do not in general need to be synchronized,
0N/A * as incomplete operations on another thread would just lead to
0N/A * harmless cache misses.
0N/A */
0N/A private static final ConcurrentHashMap<Object, KeyReference>
0N/A metricsCache = new ConcurrentHashMap<Object, KeyReference>();
0N/A
0N/A private static final int MAXRECENT = 5;
0N/A private static final FontDesignMetrics[]
0N/A recentMetrics = new FontDesignMetrics[MAXRECENT];
0N/A private static int recentIndex = 0;
0N/A
0N/A public static FontDesignMetrics getMetrics(Font font) {
0N/A return getMetrics(font, getDefaultFrc());
0N/A }
0N/A
0N/A public static FontDesignMetrics getMetrics(Font font,
0N/A FontRenderContext frc) {
0N/A
0N/A
0N/A /* When using alternate composites, can't cache based just on
0N/A * the java.awt.Font. Since this is rarely used and we can still
0N/A * cache the physical fonts, its not a problem to just return a
0N/A * new instance in this case.
0N/A * Note that currently Swing native L&F composites are not handled
0N/A * by this code as they use the metrics of the physical anyway.
0N/A */
1686N/A SunFontManager fm = SunFontManager.getInstance();
1686N/A if (fm.maybeUsingAlternateCompositeFonts() &&
1686N/A FontUtilities.getFont2D(font) instanceof CompositeFont) {
0N/A return new FontDesignMetrics(font, frc);
0N/A }
0N/A
0N/A FontDesignMetrics m = null;
0N/A KeyReference r;
0N/A
0N/A /* There are 2 possible keys used to perform lookups in metricsCache.
0N/A * If the FRC is set to all defaults, we just use the font as the key.
0N/A * If the FRC is non-default in any way, we construct a hybrid key
0N/A * that combines the font and FRC.
0N/A */
0N/A boolean usefontkey = frc.equals(getDefaultFrc());
0N/A
0N/A if (usefontkey) {
0N/A r = metricsCache.get(font);
0N/A } else /* use hybrid key */ {
0N/A // NB synchronization is not needed here because of updates to
0N/A // the metrics cache but is needed for the shared key.
0N/A synchronized (MetricsKey.class) {
0N/A MetricsKey.key.init(font, frc);
0N/A r = metricsCache.get(MetricsKey.key);
0N/A }
0N/A }
0N/A
0N/A if (r != null) {
0N/A m = (FontDesignMetrics)r.get();
0N/A }
0N/A
0N/A if (m == null) {
0N/A /* either there was no reference, or it was cleared. Need a new
0N/A * metrics instance. The key to use in the map is a new
0N/A * MetricsKey instance when we've determined the FRC is
0N/A * non-default. Its constructed from local vars so we are
0N/A * thread-safe - no need to worry about the shared key changing.
0N/A */
0N/A m = new FontDesignMetrics(font, frc);
0N/A if (usefontkey) {
0N/A metricsCache.put(font, new KeyReference(font, m));
0N/A } else /* use hybrid key */ {
0N/A MetricsKey newKey = new MetricsKey(font, frc);
0N/A metricsCache.put(newKey, new KeyReference(newKey, m));
0N/A }
0N/A }
0N/A
0N/A /* Here's where we keep the recent metrics */
0N/A for (int i=0; i<recentMetrics.length; i++) {
0N/A if (recentMetrics[i]==m) {
0N/A return m;
0N/A }
0N/A }
0N/A
0N/A synchronized (recentMetrics) {
0N/A recentMetrics[recentIndex++] = m;
0N/A if (recentIndex == MAXRECENT) {
0N/A recentIndex = 0;
0N/A }
0N/A }
0N/A return m;
0N/A }
0N/A
0N/A /*
0N/A * Constructs a new FontDesignMetrics object for the given Font.
0N/A * Its private to enable caching - call getMetrics() instead.
0N/A * @param font a Font object.
0N/A */
0N/A
0N/A private FontDesignMetrics(Font font) {
0N/A
0N/A this(font, getDefaultFrc());
0N/A }
0N/A
0N/A /* private to enable caching - call getMetrics() instead. */
0N/A private FontDesignMetrics(Font font, FontRenderContext frc) {
0N/A super(font);
0N/A this.font = font;
0N/A this.frc = frc;
0N/A
0N/A this.isAntiAliased = frc.isAntiAliased();
0N/A this.usesFractionalMetrics = frc.usesFractionalMetrics();
0N/A
0N/A frcTx = frc.getTransform();
0N/A
0N/A matrix = new double[4];
0N/A initMatrixAndMetrics();
0N/A
0N/A initAdvCache();
0N/A }
0N/A
0N/A private void initMatrixAndMetrics() {
0N/A
1686N/A Font2D font2D = FontUtilities.getFont2D(font);
0N/A fontStrike = font2D.getStrike(font, frc);
0N/A StrikeMetrics metrics = fontStrike.getFontMetrics();
0N/A this.ascent = metrics.getAscent();
0N/A this.descent = metrics.getDescent();
0N/A this.leading = metrics.getLeading();
0N/A this.maxAdvance = metrics.getMaxAdvance();
0N/A
0N/A devmatrix = new double[4];
0N/A frcTx.getMatrix(devmatrix);
0N/A }
0N/A
0N/A private void initAdvCache() {
0N/A advCache = new float[256];
0N/A // 0 is a valid metric so force it to -1
0N/A for (int i = 0; i < 256; i++) {
0N/A advCache[i] = UNKNOWN_WIDTH;
0N/A }
0N/A }
0N/A
0N/A private void readObject(ObjectInputStream in) throws IOException,
0N/A ClassNotFoundException {
0N/A
0N/A in.defaultReadObject();
0N/A if (serVersion != CURRENT_VERSION) {
0N/A frc = getDefaultFrc();
0N/A isAntiAliased = frc.isAntiAliased();
0N/A usesFractionalMetrics = frc.usesFractionalMetrics();
0N/A frcTx = frc.getTransform();
0N/A }
0N/A else {
0N/A frc = new FontRenderContext(frcTx, isAntiAliased, usesFractionalMetrics);
0N/A }
0N/A
0N/A // when deserialized, members are set to their default values for their type--
0N/A // not to the values assigned during initialization before the constructor
0N/A // body!
0N/A height = -1;
0N/A
0N/A cache = null;
0N/A
0N/A initMatrixAndMetrics();
0N/A initAdvCache();
0N/A }
0N/A
0N/A private void writeObject(ObjectOutputStream out) throws IOException {
0N/A
0N/A cache = new int[256];
0N/A for (int i=0; i < 256; i++) {
0N/A cache[i] = -1;
0N/A }
0N/A serVersion = CURRENT_VERSION;
0N/A
0N/A out.defaultWriteObject();
0N/A
0N/A cache = null;
0N/A }
0N/A
0N/A private float handleCharWidth(int ch) {
0N/A return fontStrike.getCodePointAdvance(ch); // x-component of result only
0N/A }
0N/A
0N/A // Uses advCache to get character width
0N/A // It is incorrect to call this method for ch > 255
0N/A private float getLatinCharWidth(char ch) {
0N/A
0N/A float w = advCache[ch];
0N/A if (w == UNKNOWN_WIDTH) {
0N/A w = handleCharWidth(ch);
0N/A advCache[ch] = w;
0N/A }
0N/A return w;
0N/A }
0N/A
0N/A
0N/A /* Override of FontMetrics.getFontRenderContext() */
0N/A public FontRenderContext getFontRenderContext() {
0N/A return frc;
0N/A }
0N/A
0N/A public int charWidth(char ch) {
0N/A // default metrics for compatibility with legacy code
0N/A float w;
0N/A if (ch < 0x100) {
0N/A w = getLatinCharWidth(ch);
0N/A }
0N/A else {
0N/A w = handleCharWidth(ch);
0N/A }
0N/A return (int)(0.5 + w);
0N/A }
0N/A
0N/A public int charWidth(int ch) {
0N/A if (!Character.isValidCodePoint(ch)) {
0N/A ch = 0xffff;
0N/A }
0N/A
0N/A float w = handleCharWidth(ch);
0N/A
0N/A return (int)(0.5 + w);
0N/A }
0N/A
0N/A public int stringWidth(String str) {
0N/A
0N/A float width = 0;
0N/A if (font.hasLayoutAttributes()) {
0N/A /* TextLayout throws IAE for null, so throw NPE explicitly */
0N/A if (str == null) {
0N/A throw new NullPointerException("str is null");
0N/A }
0N/A if (str.length() == 0) {
0N/A return 0;
0N/A }
0N/A width = new TextLayout(str, font, frc).getAdvance();
0N/A } else {
0N/A int length = str.length();
0N/A for (int i=0; i < length; i++) {
0N/A char ch = str.charAt(i);
0N/A if (ch < 0x100) {
0N/A width += getLatinCharWidth(ch);
1686N/A } else if (FontUtilities.isNonSimpleChar(ch)) {
0N/A width = new TextLayout(str, font, frc).getAdvance();
0N/A break;
0N/A } else {
0N/A width += handleCharWidth(ch);
0N/A }
0N/A }
0N/A }
0N/A
0N/A return (int) (0.5 + width);
0N/A }
0N/A
0N/A public int charsWidth(char data[], int off, int len) {
0N/A
0N/A float width = 0;
0N/A if (font.hasLayoutAttributes()) {
0N/A if (len == 0) {
0N/A return 0;
0N/A }
0N/A String str = new String(data, off, len);
0N/A width = new TextLayout(str, font, frc).getAdvance();
0N/A } else {
0N/A /* Explicit test needed to satisfy superclass spec */
0N/A if (len < 0) {
0N/A throw new IndexOutOfBoundsException("len="+len);
0N/A }
0N/A int limit = off + len;
0N/A for (int i=off; i < limit; i++) {
0N/A char ch = data[i];
0N/A if (ch < 0x100) {
0N/A width += getLatinCharWidth(ch);
1686N/A } else if (FontUtilities.isNonSimpleChar(ch)) {
0N/A String str = new String(data, off, len);
0N/A width = new TextLayout(str, font, frc).getAdvance();
0N/A break;
0N/A } else {
0N/A width += handleCharWidth(ch);
0N/A }
0N/A }
0N/A }
0N/A
0N/A return (int) (0.5 + width);
0N/A }
0N/A
0N/A /**
0N/A * Gets the advance widths of the first 256 characters in the
0N/A * <code>Font</code>. The advance is the
0N/A * distance from the leftmost point to the rightmost point on the
0N/A * character's baseline. Note that the advance of a
0N/A * <code>String</code> is not necessarily the sum of the advances
0N/A * of its characters.
0N/A * @return an array storing the advance widths of the
0N/A * characters in the <code>Font</code>
0N/A * described by this <code>FontMetrics</code> object.
0N/A */
0N/A // More efficient than base class implementation - reuses existing cache
0N/A public int[] getWidths() {
0N/A int[] widths = new int[256];
0N/A for (char ch = 0 ; ch < 256 ; ch++) {
0N/A float w = advCache[ch];
0N/A if (w == UNKNOWN_WIDTH) {
0N/A w = advCache[ch] = handleCharWidth(ch);
0N/A }
0N/A widths[ch] = (int) (0.5 + w);
0N/A }
0N/A return widths;
0N/A }
0N/A
0N/A public int getMaxAdvance() {
0N/A return (int)(0.99f + this.maxAdvance);
0N/A }
0N/A
0N/A /*
0N/A * Returns the typographic ascent of the font. This is the maximum distance
0N/A * glyphs in this font extend above the base line (measured in typographic
0N/A * units).
0N/A */
0N/A public int getAscent() {
0N/A return (int)(roundingUpValue + this.ascent);
0N/A }
0N/A
0N/A /*
0N/A * Returns the typographic descent of the font. This is the maximum distance
0N/A * glyphs in this font extend below the base line.
0N/A */
0N/A public int getDescent() {
0N/A return (int)(roundingUpValue + this.descent);
0N/A }
0N/A
0N/A public int getLeading() {
0N/A // nb this ensures the sum of the results of the public methods
0N/A // for leading, ascent & descent sum to height.
0N/A // if the calculations in any other methods change this needs
0N/A // to be changed too.
0N/A // the 0.95 value used here and in the other methods allows some
0N/A // tiny fraction of leeway before rouding up. A higher value (0.99)
0N/A // caused some excessive rounding up.
0N/A return
0N/A (int)(roundingUpValue + descent + leading) -
0N/A (int)(roundingUpValue + descent);
0N/A }
0N/A
0N/A // height is calculated as the sum of two separately rounded up values
0N/A // because typically clients use ascent to determine the y location to
0N/A // pass to drawString etc and we need to ensure that the height has enough
0N/A // space below the baseline to fully contain any descender.
0N/A public int getHeight() {
0N/A
0N/A if (height < 0) {
0N/A height = getAscent() + (int)(roundingUpValue + descent + leading);
0N/A }
0N/A return height;
0N/A }
0N/A}