Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/font/FontDesignMetrics.java
38829 views
/*1* Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.font;2627import java.lang.ref.ReferenceQueue;28import java.lang.ref.SoftReference;2930import java.awt.FontMetrics;31import java.awt.Font;32import java.awt.GraphicsEnvironment;33import java.awt.geom.AffineTransform;34import java.awt.geom.NoninvertibleTransformException;35import java.awt.font.FontRenderContext;36import java.awt.font.TextLayout;3738import java.io.IOException;39import java.io.ObjectInputStream;40import java.io.ObjectOutputStream;4142import java.util.concurrent.ConcurrentHashMap;4344import sun.java2d.Disposer;45import sun.java2d.DisposerRecord;4647/*48* This class provides a summary of the glyph measurements for a Font49* and a set of hints that guide their display. It provides more metrics50* information for the Font than the java.awt.FontMetrics class. There51* is also some redundancy with that class.52* <p>53* The design metrics for a Font are obtained from Font.getDesignMetrics().54* The FontDesignMetrics object returned will be independent of the55* point size of the Font.56* Most users are familiar with the idea of using <i>point size</i> to57* specify the size of glyphs in a font. This point size defines a58* measurement between the baseline of one line to the baseline of the59* following line in a single spaced text document. The point size is60* based on <i>typographic points</i>, approximately 1/72 of an inch.61* <p>62* The Java2D API adopts the convention that one point is equivalent63* to one unit in user coordinates. When using a normalized transform64* for converting user space coordinates to device space coordinates (see65* GraphicsConfiguration.getDefaultTransform() and66* GraphicsConfiguration.getNormalizingTransform()), 72 user space units67* equal 1 inch in device space. In this case one point is 1/72 of an inch.68* <p>69* The FontDesignMetrics class expresses font metrics in terms of arbitrary70* <i>typographic units</i> (not points) chosen by the font supplier71* and used in the underlying platform font representations. These units are72* defined by dividing the em-square into a grid. The em-sqaure is the73* theoretical square whose dimensions are the full body height of the74* font. A typographic unit is the smallest measurable unit in the75* em-square. The number of units-per-em is determined by the font76* designer. The greater the units-per-em, the greater the precision77* in metrics. For example, Type 1 fonts divide the em-square into a78* 1000 x 1000 grid, while TrueType fonts typically use a 2048 x 204879* grid. The scale of these units can be obtained by calling80* getUnitsPerEm().81* <p>82* Typographic units are relative -- their absolute size changes as the83* size of the of the em-square changes. An em-square is 9 points high84* in a 9-point font. Because typographic units are relative to the85* em-square, a given location on a glyph will have the same coordinates86* in typographic units regardless of the point size.87* <p>88* Converting typographic units to pixels requires computing pixels-per-em89* (ppem). This can be computed as:90* <pre>91ppem = device_resolution * (inches-per-point) * pointSize92* </pre>93* where device resolution could be measured in pixels/inch and the point94* size of a font is effectively points/em. Using a normalized transform95* from user space to device space (see above), results in 1/72 inch/point.96* In this case, ppem is equal to the point size on a 72 dpi monitor, so97* that an N point font displays N pixels high. In general,98* <pre>99pixel_units = typographic_units * (ppem / units_per_em)100* </pre>101* @see java.awt.Font102* @see java.awt.GraphicsConfiguration#getDefaultTransform103* @see java.awt.GraphicsConfiguration#getNormalizingTransform104*/105106public final class FontDesignMetrics extends FontMetrics {107108static final long serialVersionUID = 4480069578560887773L;109110private static final float UNKNOWN_WIDTH = -1;111private static final int CURRENT_VERSION = 1;112113// height, ascent, descent, leading are reported to the client114// as an integer this value is added to the true fp value to115// obtain a value which is usually going to result in a round up116// to the next integer except for very marginal cases.117private static float roundingUpValue = 0.95f;118119// These fields are all part of the old serialization representation120private Font font;121private float ascent;122private float descent;123private float leading;124private float maxAdvance;125private double[] matrix;126private int[] cache; // now unused, still here only for serialization127// End legacy serialization fields128129private int serVersion = 0; // If 1 in readObject, these fields are on the input stream:130private boolean isAntiAliased;131private boolean usesFractionalMetrics;132private AffineTransform frcTx;133134private transient float[] advCache; // transient since values could change across runtimes135private transient int height = -1;136137private transient FontRenderContext frc;138139private transient double[] devmatrix = null;140141private transient FontStrike fontStrike;142143private static FontRenderContext DEFAULT_FRC = null;144145private static FontRenderContext getDefaultFrc() {146147if (DEFAULT_FRC == null) {148AffineTransform tx;149if (GraphicsEnvironment.isHeadless()) {150tx = new AffineTransform();151} else {152tx = GraphicsEnvironment153.getLocalGraphicsEnvironment()154.getDefaultScreenDevice()155.getDefaultConfiguration()156.getDefaultTransform();157}158DEFAULT_FRC = new FontRenderContext(tx, false, false);159}160return DEFAULT_FRC;161}162163/* Strongly cache up to 5 most recently requested FontMetrics objects,164* and softly cache as many as GC allows. In practice this means we165* should keep references around until memory gets low.166* We key the cache either by a Font or a combination of the Font and167* and FRC. A lot of callers use only the font so although there's code168* duplication, we allow just a font to be a key implying a default FRC.169* Also we put the references on a queue so that if they do get nulled170* out we can clear the keys from the table.171*/172private static class KeyReference extends SoftReference173implements DisposerRecord, Disposer.PollDisposable {174175static ReferenceQueue queue = Disposer.getQueue();176177Object key;178179KeyReference(Object key, Object value) {180super(value, queue);181this.key = key;182Disposer.addReference(this, this);183}184185/* It is possible that since this reference object has been186* enqueued, that a new metrics has been put into the table187* for the same key value. So we'll test to see if the table maps188* to THIS reference. If its a new one, we'll leave it alone.189* It is possible that a new entry comes in after our test, but190* it is unlikely and if this were a problem we would need to191* synchronize all 'put' and 'remove' accesses to the cache which192* I would prefer not to do.193*/194public void dispose() {195if (metricsCache.get(key) == this) {196metricsCache.remove(key);197}198}199}200201private static class MetricsKey {202Font font;203FontRenderContext frc;204int hash;205206MetricsKey() {207}208209MetricsKey(Font font, FontRenderContext frc) {210init(font, frc);211}212213void init(Font font, FontRenderContext frc) {214this.font = font;215this.frc = frc;216this.hash = font.hashCode() + frc.hashCode();217}218219public boolean equals(Object key) {220if (!(key instanceof MetricsKey)) {221return false;222}223return224font.equals(((MetricsKey)key).font) &&225frc.equals(((MetricsKey)key).frc);226}227228public int hashCode() {229return hash;230}231232/* Synchronize access to this on the class */233static final MetricsKey key = new MetricsKey();234}235236/* All accesses to a CHM do not in general need to be synchronized,237* as incomplete operations on another thread would just lead to238* harmless cache misses.239*/240private static final ConcurrentHashMap<Object, KeyReference>241metricsCache = new ConcurrentHashMap<Object, KeyReference>();242243private static final int MAXRECENT = 5;244private static final FontDesignMetrics[]245recentMetrics = new FontDesignMetrics[MAXRECENT];246private static int recentIndex = 0;247248public static FontDesignMetrics getMetrics(Font font) {249return getMetrics(font, getDefaultFrc());250}251252public static FontDesignMetrics getMetrics(Font font,253FontRenderContext frc) {254255256/* When using alternate composites, can't cache based just on257* the java.awt.Font. Since this is rarely used and we can still258* cache the physical fonts, its not a problem to just return a259* new instance in this case.260* Note that currently Swing native L&F composites are not handled261* by this code as they use the metrics of the physical anyway.262*/263SunFontManager fm = SunFontManager.getInstance();264if (fm.maybeUsingAlternateCompositeFonts() &&265FontUtilities.getFont2D(font) instanceof CompositeFont) {266return new FontDesignMetrics(font, frc);267}268269FontDesignMetrics m = null;270KeyReference r;271272/* There are 2 possible keys used to perform lookups in metricsCache.273* If the FRC is set to all defaults, we just use the font as the key.274* If the FRC is non-default in any way, we construct a hybrid key275* that combines the font and FRC.276*/277boolean usefontkey = frc.equals(getDefaultFrc());278279if (usefontkey) {280r = metricsCache.get(font);281} else /* use hybrid key */ {282// NB synchronization is not needed here because of updates to283// the metrics cache but is needed for the shared key.284synchronized (MetricsKey.class) {285MetricsKey.key.init(font, frc);286r = metricsCache.get(MetricsKey.key);287}288}289290if (r != null) {291m = (FontDesignMetrics)r.get();292}293294if (m == null) {295/* either there was no reference, or it was cleared. Need a new296* metrics instance. The key to use in the map is a new297* MetricsKey instance when we've determined the FRC is298* non-default. Its constructed from local vars so we are299* thread-safe - no need to worry about the shared key changing.300*/301m = new FontDesignMetrics(font, frc);302if (usefontkey) {303metricsCache.put(font, new KeyReference(font, m));304} else /* use hybrid key */ {305MetricsKey newKey = new MetricsKey(font, frc);306metricsCache.put(newKey, new KeyReference(newKey, m));307}308}309310/* Here's where we keep the recent metrics */311for (int i=0; i<recentMetrics.length; i++) {312if (recentMetrics[i]==m) {313return m;314}315}316317synchronized (recentMetrics) {318recentMetrics[recentIndex++] = m;319if (recentIndex == MAXRECENT) {320recentIndex = 0;321}322}323return m;324}325326/*327* Constructs a new FontDesignMetrics object for the given Font.328* Its private to enable caching - call getMetrics() instead.329* @param font a Font object.330*/331332private FontDesignMetrics(Font font) {333334this(font, getDefaultFrc());335}336337/* private to enable caching - call getMetrics() instead. */338private FontDesignMetrics(Font font, FontRenderContext frc) {339super(font);340this.font = font;341this.frc = frc;342343this.isAntiAliased = frc.isAntiAliased();344this.usesFractionalMetrics = frc.usesFractionalMetrics();345346frcTx = frc.getTransform();347348matrix = new double[4];349initMatrixAndMetrics();350351initAdvCache();352}353354private void initMatrixAndMetrics() {355356Font2D font2D = FontUtilities.getFont2D(font);357fontStrike = font2D.getStrike(font, frc);358StrikeMetrics metrics = fontStrike.getFontMetrics();359this.ascent = metrics.getAscent();360this.descent = metrics.getDescent();361this.leading = metrics.getLeading();362this.maxAdvance = metrics.getMaxAdvance();363364devmatrix = new double[4];365frcTx.getMatrix(devmatrix);366}367368private void initAdvCache() {369advCache = new float[256];370// 0 is a valid metric so force it to -1371for (int i = 0; i < 256; i++) {372advCache[i] = UNKNOWN_WIDTH;373}374}375376private void readObject(ObjectInputStream in) throws IOException,377ClassNotFoundException {378379in.defaultReadObject();380if (serVersion != CURRENT_VERSION) {381frc = getDefaultFrc();382isAntiAliased = frc.isAntiAliased();383usesFractionalMetrics = frc.usesFractionalMetrics();384frcTx = frc.getTransform();385}386else {387frc = new FontRenderContext(frcTx, isAntiAliased, usesFractionalMetrics);388}389390// when deserialized, members are set to their default values for their type--391// not to the values assigned during initialization before the constructor392// body!393height = -1;394395cache = null;396397initMatrixAndMetrics();398initAdvCache();399}400401private void writeObject(ObjectOutputStream out) throws IOException {402403cache = new int[256];404for (int i=0; i < 256; i++) {405cache[i] = -1;406}407serVersion = CURRENT_VERSION;408409out.defaultWriteObject();410411cache = null;412}413414private float handleCharWidth(int ch) {415return fontStrike.getCodePointAdvance(ch); // x-component of result only416}417418// Uses advCache to get character width419// It is incorrect to call this method for ch > 255420private float getLatinCharWidth(char ch) {421422float w = advCache[ch];423if (w == UNKNOWN_WIDTH) {424w = handleCharWidth(ch);425advCache[ch] = w;426}427return w;428}429430431/* Override of FontMetrics.getFontRenderContext() */432public FontRenderContext getFontRenderContext() {433return frc;434}435436public int charWidth(char ch) {437// default metrics for compatibility with legacy code438float w;439if (ch < 0x100) {440w = getLatinCharWidth(ch);441}442else {443w = handleCharWidth(ch);444}445return (int)(0.5 + w);446}447448public int charWidth(int ch) {449if (!Character.isValidCodePoint(ch)) {450ch = 0xffff;451}452453float w = handleCharWidth(ch);454455return (int)(0.5 + w);456}457458public int stringWidth(String str) {459460float width = 0;461if (font.hasLayoutAttributes()) {462/* TextLayout throws IAE for null, so throw NPE explicitly */463if (str == null) {464throw new NullPointerException("str is null");465}466if (str.length() == 0) {467return 0;468}469width = new TextLayout(str, font, frc).getAdvance();470} else {471int length = str.length();472for (int i=0; i < length; i++) {473char ch = str.charAt(i);474if (ch < 0x100) {475width += getLatinCharWidth(ch);476} else if (FontUtilities.isNonSimpleChar(ch)) {477width = new TextLayout(str, font, frc).getAdvance();478break;479} else {480width += handleCharWidth(ch);481}482}483}484485return (int) (0.5 + width);486}487488public int charsWidth(char data[], int off, int len) {489490float width = 0;491if (font.hasLayoutAttributes()) {492if (len == 0) {493return 0;494}495String str = new String(data, off, len);496width = new TextLayout(str, font, frc).getAdvance();497} else {498/* Explicit test needed to satisfy superclass spec */499if (len < 0) {500throw new IndexOutOfBoundsException("len="+len);501}502int limit = off + len;503for (int i=off; i < limit; i++) {504char ch = data[i];505if (ch < 0x100) {506width += getLatinCharWidth(ch);507} else if (FontUtilities.isNonSimpleChar(ch)) {508String str = new String(data, off, len);509width = new TextLayout(str, font, frc).getAdvance();510break;511} else {512width += handleCharWidth(ch);513}514}515}516517return (int) (0.5 + width);518}519520/**521* Gets the advance widths of the first 256 characters in the522* <code>Font</code>. The advance is the523* distance from the leftmost point to the rightmost point on the524* character's baseline. Note that the advance of a525* <code>String</code> is not necessarily the sum of the advances526* of its characters.527* @return an array storing the advance widths of the528* characters in the <code>Font</code>529* described by this <code>FontMetrics</code> object.530*/531// More efficient than base class implementation - reuses existing cache532public int[] getWidths() {533int[] widths = new int[256];534for (char ch = 0 ; ch < 256 ; ch++) {535float w = advCache[ch];536if (w == UNKNOWN_WIDTH) {537w = advCache[ch] = handleCharWidth(ch);538}539widths[ch] = (int) (0.5 + w);540}541return widths;542}543544public int getMaxAdvance() {545return (int)(0.99f + this.maxAdvance);546}547548/*549* Returns the typographic ascent of the font. This is the maximum distance550* glyphs in this font extend above the base line (measured in typographic551* units).552*/553public int getAscent() {554return (int)(roundingUpValue + this.ascent);555}556557/*558* Returns the typographic descent of the font. This is the maximum distance559* glyphs in this font extend below the base line.560*/561public int getDescent() {562return (int)(roundingUpValue + this.descent);563}564565public int getLeading() {566// nb this ensures the sum of the results of the public methods567// for leading, ascent & descent sum to height.568// if the calculations in any other methods change this needs569// to be changed too.570// the 0.95 value used here and in the other methods allows some571// tiny fraction of leeway before rouding up. A higher value (0.99)572// caused some excessive rounding up.573return574(int)(roundingUpValue + descent + leading) -575(int)(roundingUpValue + descent);576}577578// height is calculated as the sum of two separately rounded up values579// because typically clients use ascent to determine the y location to580// pass to drawString etc and we need to ensure that the height has enough581// space below the baseline to fully contain any descender.582public int getHeight() {583584if (height < 0) {585height = getAscent() + (int)(roundingUpValue + descent + leading);586}587return height;588}589}590591592