Home » openjdk-7 » sun » font » [javadoc | source]

    1   /*
    2    * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   
   26   package sun.font;
   27   
   28   import java.lang.ref.ReferenceQueue;
   29   import java.lang.ref.SoftReference;
   30   
   31   import java.awt.FontMetrics;
   32   import java.awt.Font;
   33   import java.awt.GraphicsEnvironment;
   34   import java.awt.geom.AffineTransform;
   35   import java.awt.geom.NoninvertibleTransformException;
   36   import java.awt.font.FontRenderContext;
   37   import java.awt.font.TextLayout;
   38   
   39   import java.io.IOException;
   40   import java.io.ObjectInputStream;
   41   import java.io.ObjectOutputStream;
   42   
   43   import java.util.concurrent.ConcurrentHashMap;
   44   
   45   import sun.java2d.Disposer;
   46   import sun.java2d.DisposerRecord;
   47   
   48   /*
   49    * This class provides a summary of the glyph measurements  for a Font
   50    * and a set of hints that guide their display.  It provides more metrics
   51    * information for the Font than the java.awt.FontMetrics class. There
   52    * is also some redundancy with that class.
   53    * <p>
   54    * The design metrics for a Font are obtained from Font.getDesignMetrics().
   55    * The FontDesignMetrics object returned will be independent of the
   56    * point size of the Font.
   57    * Most users are familiar with the idea of using <i>point size</i> to
   58    * specify the size of glyphs in a font. This point size defines a
   59    * measurement between the baseline of one line to the baseline of the
   60    * following line in a single spaced text document. The point size is
   61    * based on <i>typographic points</i>, approximately 1/72 of an inch.
   62    * <p>
   63    * The Java2D API adopts the convention that one point is equivalent
   64    * to one unit in user coordinates.  When using a normalized transform
   65    * for converting user space coordinates to device space coordinates (see
   66    * GraphicsConfiguration.getDefaultTransform() and
   67    * GraphicsConfiguration.getNormalizingTransform()), 72 user space units
   68    * equal 1 inch in device space.  In this case one point is 1/72 of an inch.
   69    * <p>
   70    * The FontDesignMetrics class expresses font metrics in terms of arbitrary
   71    * <i>typographic units</i> (not points) chosen by the font supplier
   72    * and used in the underlying platform font representations.  These units are
   73    * defined by dividing the em-square into a grid.  The em-sqaure is the
   74    * theoretical square whose dimensions are the full body height of the
   75    * font.  A typographic unit is the smallest measurable unit in the
   76    * em-square.  The number of units-per-em is determined by the font
   77    * designer.  The greater the units-per-em, the greater the precision
   78    * in metrics.  For example, Type 1 fonts divide the em-square into a
   79    * 1000 x 1000 grid, while TrueType fonts typically use a 2048 x 2048
   80    * grid.  The scale of these units can be obtained by calling
   81    * getUnitsPerEm().
   82    * <p>
   83    * Typographic units are relative -- their absolute size changes as the
   84    * size of the of the em-square changes.  An em-square is 9 points high
   85    * in a 9-point font.  Because typographic units are relative to the
   86    * em-square, a given location on a glyph will have the same coordinates
   87    * in typographic units regardless of the point size.
   88    * <p>
   89    * Converting typographic units to pixels requires computing pixels-per-em
   90    * (ppem).  This can be computed as:
   91    * <pre>
   92            ppem = device_resolution * (inches-per-point) * pointSize
   93    * </pre>
   94    * where device resolution could be measured in pixels/inch and the point
   95    * size of a font is effectively points/em.  Using a normalized transform
   96    * from user space to device space (see above), results in 1/72 inch/point.
   97    * In this case, ppem is equal to the point size on a 72 dpi monitor, so
   98    * that an N point font displays N pixels high.  In general,
   99    * <pre>
  100           pixel_units = typographic_units * (ppem / units_per_em)
  101    * </pre>
  102    * @see java.awt.Font
  103    * @see java.awt.GraphicsConfiguration#getDefaultTransform
  104    * @see java.awt.GraphicsConfiguration#getNormalizingTransform
  105    */
  106   
  107   public final class FontDesignMetrics extends FontMetrics {
  108   
  109       static final long serialVersionUID = 4480069578560887773L;
  110   
  111       private static final float UNKNOWN_WIDTH = -1;
  112       private static final int CURRENT_VERSION = 1;
  113   
  114       // height, ascent, descent, leading are reported to the client
  115       // as an integer this value is added to the true fp value to
  116       // obtain a value which is usually going to result in a round up
  117       // to the next integer except for very marginal cases.
  118       private static float roundingUpValue = 0.95f;
  119   
  120       // These fields are all part of the old serialization representation
  121       private Font  font;
  122       private float ascent;
  123       private float descent;
  124       private float leading;
  125       private float maxAdvance;
  126       private double[] matrix;
  127       private int[] cache; // now unused, still here only for serialization
  128       // End legacy serialization fields
  129   
  130       private int serVersion = 0;  // If 1 in readObject, these fields are on the input stream:
  131       private boolean isAntiAliased;
  132       private boolean usesFractionalMetrics;
  133       private AffineTransform frcTx;
  134   
  135       private transient float[] advCache; // transient since values could change across runtimes
  136       private transient int height = -1;
  137   
  138       private transient FontRenderContext frc;
  139   
  140       private transient double[] devmatrix = null;
  141   
  142       private transient FontStrike fontStrike;
  143   
  144       private static FontRenderContext DEFAULT_FRC = null;
  145   
  146       private static FontRenderContext getDefaultFrc() {
  147   
  148           if (DEFAULT_FRC == null) {
  149               AffineTransform tx;
  150               if (GraphicsEnvironment.isHeadless()) {
  151                   tx = new AffineTransform();
  152               } else {
  153                   tx =  GraphicsEnvironment
  154                       .getLocalGraphicsEnvironment()
  155                       .getDefaultScreenDevice()
  156                       .getDefaultConfiguration()
  157                       .getDefaultTransform();
  158               }
  159               DEFAULT_FRC = new FontRenderContext(tx, false, false);
  160           }
  161           return DEFAULT_FRC;
  162       }
  163   
  164       /* Strongly cache up to 5 most recently requested FontMetrics objects,
  165        * and softly cache as many as GC allows. In practice this means we
  166        * should keep references around until memory gets low.
  167        * We key the cache either by a Font or a combination of the Font and
  168        * and FRC. A lot of callers use only the font so although there's code
  169        * duplication, we allow just a font to be a key implying a default FRC.
  170        * Also we put the references on a queue so that if they do get nulled
  171        * out we can clear the keys from the table.
  172        */
  173       private static class KeyReference extends SoftReference
  174           implements DisposerRecord, Disposer.PollDisposable {
  175   
  176           static ReferenceQueue queue = Disposer.getQueue();
  177   
  178           Object key;
  179   
  180           KeyReference(Object key, Object value) {
  181               super(value, queue);
  182               this.key = key;
  183               Disposer.addReference(this, this);
  184           }
  185   
  186           /* It is possible that since this reference object has been
  187            * enqueued, that a new metrics has been put into the table
  188            * for the same key value. So we'll test to see if the table maps
  189            * to THIS reference. If its a new one, we'll leave it alone.
  190            * It is possible that a new entry comes in after our test, but
  191            * it is unlikely and if this were a problem we would need to
  192            * synchronize all 'put' and 'remove' accesses to the cache which
  193            * I would prefer not to do.
  194            */
  195           public void dispose() {
  196               if (metricsCache.get(key) == this) {
  197                   metricsCache.remove(key);
  198               }
  199           }
  200       }
  201   
  202       private static class MetricsKey {
  203           Font font;
  204           FontRenderContext frc;
  205           int hash;
  206   
  207           MetricsKey() {
  208           }
  209   
  210           MetricsKey(Font font, FontRenderContext frc) {
  211               init(font, frc);
  212           }
  213   
  214           void init(Font font, FontRenderContext frc) {
  215               this.font = font;
  216               this.frc = frc;
  217               this.hash = font.hashCode() + frc.hashCode();
  218           }
  219   
  220           public boolean equals(Object key) {
  221               if (!(key instanceof MetricsKey)) {
  222                   return false;
  223               }
  224               return
  225                   font.equals(((MetricsKey)key).font) &&
  226                   frc.equals(((MetricsKey)key).frc);
  227           }
  228   
  229           public int hashCode() {
  230               return hash;
  231           }
  232   
  233           /* Synchronize access to this on the class */
  234           static final MetricsKey key = new MetricsKey();
  235       }
  236   
  237       /* All accesses to a CHM do not in general need to be synchronized,
  238        * as incomplete operations on another thread would just lead to
  239        * harmless cache misses.
  240        */
  241       private static final ConcurrentHashMap<Object, KeyReference>
  242           metricsCache = new ConcurrentHashMap<Object, KeyReference>();
  243   
  244       private static final int MAXRECENT = 5;
  245       private static final FontDesignMetrics[]
  246           recentMetrics = new FontDesignMetrics[MAXRECENT];
  247       private static int recentIndex = 0;
  248   
  249       public static FontDesignMetrics getMetrics(Font font) {
  250           return getMetrics(font, getDefaultFrc());
  251        }
  252   
  253       public static FontDesignMetrics getMetrics(Font font,
  254                                                  FontRenderContext frc) {
  255   
  256   
  257           /* When using alternate composites, can't cache based just on
  258            * the java.awt.Font. Since this is rarely used and we can still
  259            * cache the physical fonts, its not a problem to just return a
  260            * new instance in this case.
  261            * Note that currently Swing native L&F composites are not handled
  262            * by this code as they use the metrics of the physical anyway.
  263            */
  264           SunFontManager fm = SunFontManager.getInstance();
  265           if (fm.maybeUsingAlternateCompositeFonts() &&
  266               FontUtilities.getFont2D(font) instanceof CompositeFont) {
  267               return new FontDesignMetrics(font, frc);
  268           }
  269   
  270           FontDesignMetrics m = null;
  271           KeyReference r;
  272   
  273           /* There are 2 possible keys used to perform lookups in metricsCache.
  274            * If the FRC is set to all defaults, we just use the font as the key.
  275            * If the FRC is non-default in any way, we construct a hybrid key
  276            * that combines the font and FRC.
  277            */
  278           boolean usefontkey = frc.equals(getDefaultFrc());
  279   
  280           if (usefontkey) {
  281               r = metricsCache.get(font);
  282           } else /* use hybrid key */ {
  283               // NB synchronization is not needed here because of updates to
  284               // the metrics cache but is needed for the shared key.
  285               synchronized (MetricsKey.class) {
  286                   MetricsKey.key.init(font, frc);
  287                   r = metricsCache.get(MetricsKey.key);
  288               }
  289           }
  290   
  291           if (r != null) {
  292               m = (FontDesignMetrics)r.get();
  293           }
  294   
  295           if (m == null) {
  296               /* either there was no reference, or it was cleared. Need a new
  297                * metrics instance. The key to use in the map is a new
  298                * MetricsKey instance when we've determined the FRC is
  299                * non-default. Its constructed from local vars so we are
  300                * thread-safe - no need to worry about the shared key changing.
  301                */
  302               m = new FontDesignMetrics(font, frc);
  303               if (usefontkey) {
  304                   metricsCache.put(font, new KeyReference(font, m));
  305               } else /* use hybrid key */ {
  306                   MetricsKey newKey = new MetricsKey(font, frc);
  307                   metricsCache.put(newKey, new KeyReference(newKey, m));
  308               }
  309           }
  310   
  311           /* Here's where we keep the recent metrics */
  312           for (int i=0; i<recentMetrics.length; i++) {
  313               if (recentMetrics[i]==m) {
  314                   return m;
  315               }
  316           }
  317   
  318           synchronized (recentMetrics) {
  319               recentMetrics[recentIndex++] = m;
  320               if (recentIndex == MAXRECENT) {
  321                   recentIndex = 0;
  322               }
  323           }
  324           return m;
  325       }
  326   
  327     /*
  328      * Constructs a new FontDesignMetrics object for the given Font.
  329      * Its private to enable caching - call getMetrics() instead.
  330      * @param font a Font object.
  331      */
  332   
  333       private FontDesignMetrics(Font font) {
  334   
  335           this(font, getDefaultFrc());
  336       }
  337   
  338       /* private to enable caching - call getMetrics() instead. */
  339       private FontDesignMetrics(Font font, FontRenderContext frc) {
  340         super(font);
  341         this.font = font;
  342         this.frc = frc;
  343   
  344         this.isAntiAliased = frc.isAntiAliased();
  345         this.usesFractionalMetrics = frc.usesFractionalMetrics();
  346   
  347         frcTx = frc.getTransform();
  348   
  349         matrix = new double[4];
  350         initMatrixAndMetrics();
  351   
  352         initAdvCache();
  353       }
  354   
  355       private void initMatrixAndMetrics() {
  356   
  357           Font2D font2D = FontUtilities.getFont2D(font);
  358           fontStrike = font2D.getStrike(font, frc);
  359           StrikeMetrics metrics = fontStrike.getFontMetrics();
  360           this.ascent = metrics.getAscent();
  361           this.descent = metrics.getDescent();
  362           this.leading = metrics.getLeading();
  363           this.maxAdvance = metrics.getMaxAdvance();
  364   
  365           devmatrix = new double[4];
  366           frcTx.getMatrix(devmatrix);
  367       }
  368   
  369       private void initAdvCache() {
  370           advCache = new float[256];
  371           // 0 is a valid metric so force it to -1
  372           for (int i = 0; i < 256; i++) {
  373               advCache[i] = UNKNOWN_WIDTH;
  374           }
  375       }
  376   
  377       private void readObject(ObjectInputStream in) throws IOException,
  378                                                     ClassNotFoundException {
  379   
  380           in.defaultReadObject();
  381           if (serVersion != CURRENT_VERSION) {
  382               frc = getDefaultFrc();
  383               isAntiAliased = frc.isAntiAliased();
  384               usesFractionalMetrics = frc.usesFractionalMetrics();
  385               frcTx = frc.getTransform();
  386           }
  387           else {
  388               frc = new FontRenderContext(frcTx, isAntiAliased, usesFractionalMetrics);
  389           }
  390   
  391           // when deserialized, members are set to their default values for their type--
  392           // not to the values assigned during initialization before the constructor
  393           // body!
  394           height = -1;
  395   
  396           cache = null;
  397   
  398           initMatrixAndMetrics();
  399           initAdvCache();
  400       }
  401   
  402       private void writeObject(ObjectOutputStream out) throws IOException {
  403   
  404           cache = new int[256];
  405           for (int i=0; i < 256; i++) {
  406               cache[i] = -1;
  407           }
  408           serVersion = CURRENT_VERSION;
  409   
  410           out.defaultWriteObject();
  411   
  412           cache = null;
  413       }
  414   
  415       private float handleCharWidth(int ch) {
  416           return fontStrike.getCodePointAdvance(ch); // x-component of result only
  417       }
  418   
  419       // Uses advCache to get character width
  420       // It is incorrect to call this method for ch > 255
  421       private float getLatinCharWidth(char ch) {
  422   
  423           float w = advCache[ch];
  424           if (w == UNKNOWN_WIDTH) {
  425               w = handleCharWidth(ch);
  426               advCache[ch] = w;
  427           }
  428           return w;
  429       }
  430   
  431   
  432       /* Override of FontMetrics.getFontRenderContext() */
  433       public FontRenderContext getFontRenderContext() {
  434           return frc;
  435       }
  436   
  437       public int charWidth(char ch) {
  438           // default metrics for compatibility with legacy code
  439           float w;
  440           if (ch < 0x100) {
  441               w = getLatinCharWidth(ch);
  442           }
  443           else {
  444               w = handleCharWidth(ch);
  445           }
  446           return (int)(0.5 + w);
  447       }
  448   
  449       public int charWidth(int ch) {
  450           if (!Character.isValidCodePoint(ch)) {
  451               ch = 0xffff;
  452           }
  453   
  454           float w = handleCharWidth(ch);
  455   
  456           return (int)(0.5 + w);
  457       }
  458   
  459       public int stringWidth(String str) {
  460   
  461           float width = 0;
  462           if (font.hasLayoutAttributes()) {
  463               /* TextLayout throws IAE for null, so throw NPE explicitly */
  464               if (str == null) {
  465                   throw new NullPointerException("str is null");
  466               }
  467               if (str.length() == 0) {
  468                   return 0;
  469               }
  470               width = new TextLayout(str, font, frc).getAdvance();
  471           } else {
  472               int length = str.length();
  473               for (int i=0; i < length; i++) {
  474                   char ch = str.charAt(i);
  475                   if (ch < 0x100) {
  476                       width += getLatinCharWidth(ch);
  477                   } else if (FontUtilities.isNonSimpleChar(ch)) {
  478                       width = new TextLayout(str, font, frc).getAdvance();
  479                       break;
  480                   } else {
  481                       width += handleCharWidth(ch);
  482                   }
  483               }
  484           }
  485   
  486           return (int) (0.5 + width);
  487       }
  488   
  489       public int charsWidth(char data[], int off, int len) {
  490   
  491           float width = 0;
  492           if (font.hasLayoutAttributes()) {
  493               if (len == 0) {
  494                   return 0;
  495               }
  496               String str = new String(data, off, len);
  497               width = new TextLayout(str, font, frc).getAdvance();
  498           } else {
  499               /* Explicit test needed to satisfy superclass spec */
  500               if (len < 0) {
  501                   throw new IndexOutOfBoundsException("len="+len);
  502               }
  503               int limit = off + len;
  504               for (int i=off; i < limit; i++) {
  505                   char ch = data[i];
  506                   if (ch < 0x100) {
  507                       width += getLatinCharWidth(ch);
  508                   } else if (FontUtilities.isNonSimpleChar(ch)) {
  509                       String str = new String(data, off, len);
  510                       width = new TextLayout(str, font, frc).getAdvance();
  511                       break;
  512                   } else {
  513                       width += handleCharWidth(ch);
  514                   }
  515               }
  516           }
  517   
  518           return (int) (0.5 + width);
  519       }
  520   
  521       /**
  522        * Gets the advance widths of the first 256 characters in the
  523        * <code>Font</code>.  The advance is the
  524        * distance from the leftmost point to the rightmost point on the
  525        * character's baseline.  Note that the advance of a
  526        * <code>String</code> is not necessarily the sum of the advances
  527        * of its characters.
  528        * @return    an array storing the advance widths of the
  529        *                 characters in the <code>Font</code>
  530        *                 described by this <code>FontMetrics</code> object.
  531        */
  532       // More efficient than base class implementation - reuses existing cache
  533       public int[] getWidths() {
  534           int[] widths = new int[256];
  535           for (char ch = 0 ; ch < 256 ; ch++) {
  536               float w = advCache[ch];
  537               if (w == UNKNOWN_WIDTH) {
  538                   w = advCache[ch] = handleCharWidth(ch);
  539               }
  540               widths[ch] = (int) (0.5 + w);
  541           }
  542           return widths;
  543       }
  544   
  545       public int getMaxAdvance() {
  546           return (int)(0.99f + this.maxAdvance);
  547       }
  548   
  549     /*
  550      * Returns the typographic ascent of the font. This is the maximum distance
  551      * glyphs in this font extend above the base line (measured in typographic
  552      * units).
  553      */
  554       public int getAscent() {
  555           return (int)(roundingUpValue + this.ascent);
  556       }
  557   
  558     /*
  559      * Returns the typographic descent of the font. This is the maximum distance
  560      * glyphs in this font extend below the base line.
  561      */
  562       public int getDescent() {
  563           return (int)(roundingUpValue + this.descent);
  564       }
  565   
  566       public int getLeading() {
  567           // nb this ensures the sum of the results of the public methods
  568           // for leading, ascent & descent sum to height.
  569           // if the calculations in any other methods change this needs
  570           // to be changed too.
  571           // the 0.95 value used here and in the other methods allows some
  572           // tiny fraction of leeway before rouding up. A higher value (0.99)
  573           // caused some excessive rounding up.
  574           return
  575               (int)(roundingUpValue + descent + leading) -
  576               (int)(roundingUpValue + descent);
  577       }
  578   
  579       // height is calculated as the sum of two separately rounded up values
  580       // because typically clients use ascent to determine the y location to
  581       // pass to drawString etc and we need to ensure that the height has enough
  582       // space below the baseline to fully contain any descender.
  583       public int getHeight() {
  584   
  585           if (height < 0) {
  586               height = getAscent() + (int)(roundingUpValue + descent + leading);
  587           }
  588           return height;
  589       }
  590   }

Home » openjdk-7 » sun » font » [javadoc | source]