590 lines · 20.7 KB · Java
RawDownload
/* * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ 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. * <p> * 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 <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 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. * <p> * The FontDesignMetrics class expresses font metrics in terms of arbitrary * <i>typographic units</i> (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(). * <p> * 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. * <p> * Converting typographic units to pixels requires computing pixels-per-em * (ppem).  This can be computed as: * <pre>         ppem = device_resolution * (inches-per-point) * pointSize * </pre> * 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, * <pre>        pixel_units = typographic_units * (ppem / units_per_em) * </pre> * @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, Disposer.PollDisposable {         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<Object, KeyReference>        metricsCache = new ConcurrentHashMap<Object, KeyReference>();     private static final int MAXRECENT = 5;    private static final FontDesignMetrics[]        recentMetrics = new FontDesignMetrics[MAXRECENT];    private static int recentIndex = 0;     public static FontDesignMetrics getMetrics(Font font) {        return getMetrics(font, getDefaultFrc());     }     public static FontDesignMetrics getMetrics(Font font,                                               FontRenderContext frc) {          /* When using alternate composites, can't cache based just on         * the java.awt.Font. Since this is rarely used and we can still         * cache the physical fonts, its not a problem to just return a         * new instance in this case.         * Note that currently Swing native L&F composites are not handled         * by this code as they use the metrics of the physical anyway.         */        SunFontManager fm = SunFontManager.getInstance();        if (fm.maybeUsingAlternateCompositeFonts() &&            FontUtilities.getFont2D(font) instanceof CompositeFont) {            return new FontDesignMetrics(font, frc);        }         FontDesignMetrics m = null;        KeyReference r;         /* There are 2 possible keys used to perform lookups in metricsCache.         * If the FRC is set to all defaults, we just use the font as the key.         * If the FRC is non-default in any way, we construct a hybrid key         * that combines the font and FRC.         */        boolean usefontkey = frc.equals(getDefaultFrc());         if (usefontkey) {            r = metricsCache.get(font);        } else /* use hybrid key */ {            // NB synchronization is not needed here because of updates to            // the metrics cache but is needed for the shared key.            synchronized (MetricsKey.class) {                MetricsKey.key.init(font, frc);                r = metricsCache.get(MetricsKey.key);            }        }         if (r != null) {            m = (FontDesignMetrics)r.get();        }         if (m == null) {            /* either there was no reference, or it was cleared. Need a new             * metrics instance. The key to use in the map is a new             * MetricsKey instance when we've determined the FRC is             * non-default. Its constructed from local vars so we are             * thread-safe - no need to worry about the shared key changing.             */            m = new FontDesignMetrics(font, frc);            if (usefontkey) {                metricsCache.put(font, new KeyReference(font, m));            } else /* use hybrid key */ {                MetricsKey newKey = new MetricsKey(font, frc);                metricsCache.put(newKey, new KeyReference(newKey, m));            }        }         /* Here's where we keep the recent metrics */        for (int i=0; i<recentMetrics.length; i++) {            if (recentMetrics[i]==m) {                return m;            }        }         synchronized (recentMetrics) {            recentMetrics[recentIndex++] = m;            if (recentIndex == MAXRECENT) {                recentIndex = 0;            }        }        return m;    }   /*   * Constructs a new FontDesignMetrics object for the given Font.   * Its private to enable caching - call getMetrics() instead.   * @param font a Font object.   */     private FontDesignMetrics(Font font) {         this(font, getDefaultFrc());    }     /* private to enable caching - call getMetrics() instead. */    private FontDesignMetrics(Font font, FontRenderContext frc) {      super(font);      this.font = font;      this.frc = frc;       this.isAntiAliased = frc.isAntiAliased();      this.usesFractionalMetrics = frc.usesFractionalMetrics();       frcTx = frc.getTransform();       matrix = new double[4];      initMatrixAndMetrics();       initAdvCache();    }     private void initMatrixAndMetrics() {         Font2D font2D = FontUtilities.getFont2D(font);        fontStrike = font2D.getStrike(font, frc);        StrikeMetrics metrics = fontStrike.getFontMetrics();        this.ascent = metrics.getAscent();        this.descent = metrics.getDescent();        this.leading = metrics.getLeading();        this.maxAdvance = metrics.getMaxAdvance();         devmatrix = new double[4];        frcTx.getMatrix(devmatrix);    }     private void initAdvCache() {        advCache = new float[256];        // 0 is a valid metric so force it to -1        for (int i = 0; i < 256; i++) {            advCache[i] = UNKNOWN_WIDTH;        }    }     private void readObject(ObjectInputStream in) throws IOException,                                                  ClassNotFoundException {         in.defaultReadObject();        if (serVersion != CURRENT_VERSION) {            frc = getDefaultFrc();            isAntiAliased = frc.isAntiAliased();            usesFractionalMetrics = frc.usesFractionalMetrics();            frcTx = frc.getTransform();        }        else {            frc = new FontRenderContext(frcTx, isAntiAliased, usesFractionalMetrics);        }         // when deserialized, members are set to their default values for their type--        // not to the values assigned during initialization before the constructor        // body!        height = -1;         cache = null;         initMatrixAndMetrics();        initAdvCache();    }     private void writeObject(ObjectOutputStream out) throws IOException {         cache = new int[256];        for (int i=0; i < 256; i++) {            cache[i] = -1;        }        serVersion = CURRENT_VERSION;         out.defaultWriteObject();         cache = null;    }     private float handleCharWidth(int ch) {        return fontStrike.getCodePointAdvance(ch); // x-component of result only    }     // Uses advCache to get character width    // It is incorrect to call this method for ch > 255    private float getLatinCharWidth(char ch) {         float w = advCache[ch];        if (w == UNKNOWN_WIDTH) {            w = handleCharWidth(ch);            advCache[ch] = w;        }        return w;    }      /* Override of FontMetrics.getFontRenderContext() */    public FontRenderContext getFontRenderContext() {        return frc;    }     public int charWidth(char ch) {        // default metrics for compatibility with legacy code        float w;        if (ch < 0x100) {            w = getLatinCharWidth(ch);        }        else {            w = handleCharWidth(ch);        }        return (int)(0.5 + w);    }     public int charWidth(int ch) {        if (!Character.isValidCodePoint(ch)) {            ch = 0xffff;        }         float w = handleCharWidth(ch);         return (int)(0.5 + w);    }     public int stringWidth(String str) {         float width = 0;        if (font.hasLayoutAttributes()) {            /* TextLayout throws IAE for null, so throw NPE explicitly */            if (str == null) {                throw new NullPointerException("str is null");            }            if (str.length() == 0) {                return 0;            }            width = new TextLayout(str, font, frc).getAdvance();        } else {            int length = str.length();            for (int i=0; i < length; i++) {                char ch = str.charAt(i);                if (ch < 0x100) {                    width += getLatinCharWidth(ch);                } else if (FontUtilities.isNonSimpleChar(ch)) {                    width = new TextLayout(str, font, frc).getAdvance();                    break;                } else {                    width += handleCharWidth(ch);                }            }        }         return (int) (0.5 + width);    }     public int charsWidth(char data[], int off, int len) {         float width = 0;        if (font.hasLayoutAttributes()) {            if (len == 0) {                return 0;            }            String str = new String(data, off, len);            width = new TextLayout(str, font, frc).getAdvance();        } else {            /* Explicit test needed to satisfy superclass spec */            if (len < 0) {

Lines 1–500 of 590.