1 /*
   2  * Copyright (c) 2003, 2014, 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.awt.Font;
  29 import java.awt.font.FontRenderContext;
  30 import java.awt.geom.AffineTransform;
  31 import java.lang.ref.Reference;
  32 import java.lang.ref.SoftReference;
  33 import java.util.concurrent.ConcurrentHashMap;
  34 import java.util.Locale;
  35 
  36 public abstract class Font2D {
  37 
  38     /* Note: JRE and FONT_CONFIG ranks are identical. I don't know of a reason
  39      * to distingish these. Possibly if a user adds fonts to the JRE font
  40      * directory that are the same font as the ones specified in the font
  41      * configuration but that is more likely to be the legitimate intention
  42      * than a problem. One reason why these should be the same is that on
  43      * Linux the JRE fonts ARE the font configuration fonts, and although I
  44      * believe all are assigned FONT_CONFIG rank, it is conceivable that if
  45      * this were not so, that some JRE font would not be allowed to joint the
  46      * family of its siblings which were assigned FONT_CONFIG rank. Giving
  47      * them the same rank is the easy solution for now at least.
  48      */
  49     public static final int FONT_CONFIG_RANK   = 2;
  50     public static final int JRE_RANK     = 2;
  51     public static final int TTF_RANK     = 3;
  52     public static final int TYPE1_RANK   = 4;
  53     public static final int NATIVE_RANK  = 5;
  54     public static final int UNKNOWN_RANK = 6;
  55     public static final int DEFAULT_RANK = 4;
  56 
  57     private static final String[] boldNames = {
  58         "bold", "demibold", "demi-bold", "demi bold", "negreta", "demi", };
  59 
  60     private static final String[] italicNames = {
  61         "italic", "cursiva", "oblique", "inclined", };
  62 
  63     private static final String[] boldItalicNames = {
  64           "bolditalic", "bold-italic", "bold italic",
  65           "boldoblique", "bold-oblique", "bold oblique",
  66           "demibold italic", "negreta cursiva","demi oblique", };
  67 
  68     private static final FontRenderContext DEFAULT_FRC =
  69         new FontRenderContext(null, false, false);
  70 
  71     public Font2DHandle handle;
  72     protected String familyName;           /* Family font name (english) */
  73     protected String fullName;             /* Full font name (english)   */
  74     protected int style = Font.PLAIN;
  75     protected FontFamily family;
  76     protected int fontRank = DEFAULT_RANK;
  77 
  78     /*
  79      * A mapper can be independent of the strike.
  80      * Perhaps the reference to the mapper ought to be held on the
  81      * scaler, as it may be implemented via scaler functionality anyway
  82      * and so the mapper would be useless if its native portion was
  83      * freed when the scaler was GC'd.
  84      */
  85     protected CharToGlyphMapper mapper;
  86 
  87     /*
  88      * The strike cache is maintained per "Font2D" as that is the
  89      * principal object by which you look up fonts.
  90      * It means more Hashmaps, but look ups can be quicker because
  91      * the map will have fewer entries, and there's no need to try to
  92      * make the Font2D part of the key.
  93      */
  94     protected ConcurrentHashMap<FontStrikeDesc, Reference<FontStrike>>
  95         strikeCache = new ConcurrentHashMap<>();
  96 
  97     /* Store the last Strike in a Reference object.
  98      * Similarly to the strike that was stored on a C++ font object,
  99      * this is an optimisation which helps if multiple clients (ie
 100      * typically SunGraphics2D instances) are using the same font, then
 101      * as may be typical of many UIs, they are probably using it in the
 102      * same style, so it can be a win to first quickly check if the last
 103      * strike obtained from this Font2D satifies the needs of the next
 104      * client too.
 105      * This pre-supposes that a FontStrike is a shareable object, which
 106      * it should.
 107      */
 108     protected Reference<FontStrike> lastFontStrike = new SoftReference<>(null);
 109 
 110     /*
 111      * POSSIBLE OPTIMISATION:
 112      * Array of length 1024 elements of 64 bits indicating if a font
 113      * contains these. This kind of information can be shared between
 114      * all point sizes.
 115      * if corresponding bit in knownBitmaskMap is set then canDisplayBitmaskMap
 116      * is valid. This is 16Kbytes of data per composite font style.
 117      * What about UTF-32 and surrogates?
 118      * REMIND: This is too much storage. Probably can only cache this
 119      * information for latin range, although possibly OK to store all
 120      * for just the "logical" fonts.
 121      * Or instead store arrays of subranges of 1024 bits (128 bytes) in
 122      * the range below surrogate pairs.
 123      */
 124 //     protected long[] knownBitmaskMap;
 125 //     protected long[] canDisplayBitmaskMap;
 126 
 127     /* Returns the "real" style of this Font2D. Eg the font face
 128      * Lucida Sans Bold" has a real style of Font.BOLD, even though
 129      * it may be able to used to simulate bold italic
 130      */
 131     public int getStyle() {
 132         return style;
 133     }
 134     protected void setStyle() {
 135 
 136         String fName = fullName.toLowerCase();
 137 
 138         for (int i=0; i < boldItalicNames.length; i++) {
 139             if (fName.indexOf(boldItalicNames[i]) != -1) {
 140                 style = Font.BOLD|Font.ITALIC;
 141                 return;
 142             }
 143         }
 144 
 145         for (int i=0; i < italicNames.length; i++) {
 146             if (fName.indexOf(italicNames[i]) != -1) {
 147                 style = Font.ITALIC;
 148                 return;
 149             }
 150         }
 151 
 152         for (int i=0; i < boldNames.length; i++) {
 153             if (fName.indexOf(boldNames[i]) != -1 ) {
 154                 style = Font.BOLD;
 155                 return;
 156             }
 157         }
 158     }
 159 
 160 
 161     int getRank() {
 162         return fontRank;
 163     }
 164 
 165     void setRank(int rank) {
 166         fontRank = rank;
 167     }
 168 
 169     abstract CharToGlyphMapper getMapper();
 170 
 171 
 172 
 173     /* This isn't very efficient but its infrequently used.
 174      * StandardGlyphVector uses it when the client assigns the glyph codes.
 175      * These may not be valid. This validates them substituting the missing
 176      * glyph elsewhere.
 177      */
 178     protected int getValidatedGlyphCode(int glyphCode) {
 179         if (glyphCode < 0 || glyphCode >= getMapper().getNumGlyphs()) {
 180             glyphCode = getMapper().getMissingGlyphCode();
 181         }
 182         return glyphCode;
 183     }
 184 
 185     /*
 186      * Creates an appropriate strike for the Font2D subclass
 187      */
 188     abstract FontStrike createStrike(FontStrikeDesc desc);
 189 
 190     /* this may be useful for APIs like canDisplay where the answer
 191      * is dependent on the font and its scaler, but not the strike.
 192      * If no strike has ever been returned, then create a one that matches
 193      * this font with the default FRC. It will become the lastStrike and
 194      * there's a good chance that the next call will be to get exactly that
 195      * strike.
 196      */
 197     public FontStrike getStrike(Font font) {
 198         FontStrike strike = lastFontStrike.get();
 199         if (strike != null) {
 200             return strike;
 201         } else {
 202             return getStrike(font, DEFAULT_FRC);
 203         }
 204     }
 205 
 206     /* SunGraphics2D has font, tx, aa and fm. From this info
 207      * can get a Strike object from the cache, creating it if necessary.
 208      * This code is designed for multi-threaded access.
 209      * For that reason it creates a local FontStrikeDesc rather than filling
 210      * in a shared one. Up to two AffineTransforms and one FontStrikeDesc will
 211      * be created by every lookup. This appears to perform more than
 212      * adequately. But it may make sense to expose FontStrikeDesc
 213      * as a parameter so a caller can use its own.
 214      * In such a case if a FontStrikeDesc is stored as a key then
 215      * we would need to use a private copy.
 216      *
 217      * Note that this code doesn't prevent two threads from creating
 218      * two different FontStrike instances and having one of the threads
 219      * overwrite the other in the map. This is likely to be a rare
 220      * occurrence and the only consequence is that these callers will have
 221      * different instances of the strike, and there'd be some duplication of
 222      * population of the strikes. However since users of these strikes are
 223      * transient, then the one that was overwritten would soon be freed.
 224      * If there is any problem then a small synchronized block would be
 225      * required with its attendant consequences for MP scaleability.
 226      */
 227     public FontStrike getStrike(Font font, AffineTransform devTx,
 228                                 int aa, int fm) {
 229 
 230         /* Create the descriptor which is used to identify a strike
 231          * in the strike cache/map. A strike is fully described by
 232          * the attributes of this descriptor.
 233          */
 234         /* REMIND: generating garbage and doing computation here in order
 235          * to include pt size in the tx just for a lookup! Figure out a
 236          * better way.
 237          */
 238         double ptSize = font.getSize2D();
 239         AffineTransform glyphTx = (AffineTransform)devTx.clone();
 240         glyphTx.scale(ptSize, ptSize);
 241         if (font.isTransformed()) {
 242             glyphTx.concatenate(font.getTransform());
 243         }
 244         if (glyphTx.getTranslateX() != 0 || glyphTx.getTranslateY() != 0) {
 245             glyphTx.setTransform(glyphTx.getScaleX(),
 246                                  glyphTx.getShearY(),
 247                                  glyphTx.getShearX(),
 248                                  glyphTx.getScaleY(),
 249                                  0.0, 0.0);
 250         }
 251         FontStrikeDesc desc = new FontStrikeDesc(devTx, glyphTx,
 252                                                  font.getStyle(), aa, fm);
 253         return getStrike(desc, false);
 254     }
 255 
 256     public FontStrike getStrike(Font font, AffineTransform devTx,
 257                                 AffineTransform glyphTx,
 258                                 int aa, int fm) {
 259 
 260         /* Create the descriptor which is used to identify a strike
 261          * in the strike cache/map. A strike is fully described by
 262          * the attributes of this descriptor.
 263          */
 264         FontStrikeDesc desc = new FontStrikeDesc(devTx, glyphTx,
 265                                                  font.getStyle(), aa, fm);
 266         return getStrike(desc, false);
 267     }
 268 
 269     public FontStrike getStrike(Font font, FontRenderContext frc) {
 270 
 271         AffineTransform at = frc.getTransform();
 272         double ptSize = font.getSize2D();
 273         at.scale(ptSize, ptSize);
 274         if (font.isTransformed()) {
 275             at.concatenate(font.getTransform());
 276             if (at.getTranslateX() != 0 || at.getTranslateY() != 0) {
 277                 at.setTransform(at.getScaleX(),
 278                                 at.getShearY(),
 279                                 at.getShearX(),
 280                                 at.getScaleY(),
 281                                 0.0, 0.0);
 282             }
 283         }
 284         int aa = FontStrikeDesc.getAAHintIntVal(this, font, frc);
 285         int fm = FontStrikeDesc.getFMHintIntVal(frc.getFractionalMetricsHint());
 286         FontStrikeDesc desc = new FontStrikeDesc(frc.getTransform(),
 287                                                  at, font.getStyle(),
 288                                                  aa, fm);
 289         return getStrike(desc, false);
 290     }
 291 
 292     FontStrike getStrike(FontStrikeDesc desc) {
 293         return getStrike(desc, true);
 294     }
 295 
 296     private FontStrike getStrike(FontStrikeDesc desc, boolean copy) {
 297         /* Before looking in the map, see if the descriptor matches the
 298          * last strike returned from this Font2D. This should often be a win
 299          * since its common for the same font, in the same size to be
 300          * used frequently, for example in many parts of a UI.
 301          *
 302          * If its not the same then we use the descriptor to locate a
 303          * Reference to the strike. If it exists and points to a strike,
 304          * then we update the last strike to refer to that and return it.
 305          *
 306          * If the key isn't in the map, or its reference object has been
 307          * collected, then we create a new strike, put it in the map and
 308          * set it to be the last strike.
 309          */
 310         FontStrike strike = lastFontStrike.get();
 311         if (strike != null && desc.equals(strike.desc)) {
 312             //strike.lastlookupTime = System.currentTimeMillis();
 313             return strike;
 314         } else {
 315             Reference<FontStrike> strikeRef = strikeCache.get(desc);
 316             if (strikeRef != null) {
 317                 strike = strikeRef.get();
 318                 if (strike != null) {
 319                     //strike.lastlookupTime = System.currentTimeMillis();
 320                     lastFontStrike = new SoftReference<>(strike);
 321                     StrikeCache.refStrike(strike);
 322                     return strike;
 323                 }
 324             }
 325             /* When we create a new FontStrike instance, we *must*
 326              * ask the StrikeCache for a reference. We must then ensure
 327              * this reference remains reachable, by storing it in the
 328              * Font2D's strikeCache map.
 329              * So long as the Reference is there (reachable) then if the
 330              * reference is cleared, it will be enqueued for disposal.
 331              * If for some reason we explicitly remove this reference, it
 332              * must only be done when holding a strong reference to the
 333              * referent (the FontStrike), or if the reference is cleared,
 334              * then we must explicitly "dispose" of the native resources.
 335              * The only place this currently happens is in this same method,
 336              * where we find a cleared reference and need to overwrite it
 337              * here with a new reference.
 338              * Clearing the whilst holding a strong reference, should only
 339              * be done if the
 340              */
 341             if (copy) {
 342                 desc = new FontStrikeDesc(desc);
 343             }
 344             strike = createStrike(desc);
 345             //StrikeCache.addStrike();
 346             /* If we are creating many strikes on this font which
 347              * involve non-quadrant rotations, or more general
 348              * transforms which include shears, then force the use
 349              * of weak references rather than soft references.
 350              * This means that it won't live much beyond the next GC,
 351              * which is what we want for what is likely a transient strike.
 352              */
 353             int txType = desc.glyphTx.getType();
 354             if (txType == AffineTransform.TYPE_GENERAL_TRANSFORM ||
 355                 (txType & AffineTransform.TYPE_GENERAL_ROTATION) != 0 &&
 356                 strikeCache.size() > 10) {
 357                 strikeRef = StrikeCache.getStrikeRef(strike, true);
 358             } else {
 359                 strikeRef = StrikeCache.getStrikeRef(strike);
 360             }
 361             strikeCache.put(desc, strikeRef);
 362             //strike.lastlookupTime = System.currentTimeMillis();
 363             lastFontStrike = new SoftReference<>(strike);
 364             StrikeCache.refStrike(strike);
 365             return strike;
 366         }
 367     }
 368 
 369     void removeFromCache(FontStrikeDesc desc) {
 370         Reference<FontStrike> ref = strikeCache.get(desc);
 371         if (ref != null) {
 372             Object o = ref.get();
 373             if (o == null) {
 374                 strikeCache.remove(desc);
 375             }
 376         }
 377     }
 378 
 379     /**
 380      * The length of the metrics array must be >= 8.  This method will
 381      * store the following elements in that array before returning:
 382      *    metrics[0]: ascent
 383      *    metrics[1]: descent
 384      *    metrics[2]: leading
 385      *    metrics[3]: max advance
 386      *    metrics[4]: strikethrough offset
 387      *    metrics[5]: strikethrough thickness
 388      *    metrics[6]: underline offset
 389      *    metrics[7]: underline thickness
 390      */
 391     public void getFontMetrics(Font font, AffineTransform at,
 392                                Object aaHint, Object fmHint,
 393                                float metrics[]) {
 394         /* This is called in just one place in Font with "at" == identity.
 395          * Perhaps this can be eliminated.
 396          */
 397         int aa = FontStrikeDesc.getAAHintIntVal(aaHint, this, font.getSize());
 398         int fm = FontStrikeDesc.getFMHintIntVal(fmHint);
 399         FontStrike strike = getStrike(font, at, aa, fm);
 400         StrikeMetrics strikeMetrics = strike.getFontMetrics();
 401         metrics[0] = strikeMetrics.getAscent();
 402         metrics[1] = strikeMetrics.getDescent();
 403         metrics[2] = strikeMetrics.getLeading();
 404         metrics[3] = strikeMetrics.getMaxAdvance();
 405 
 406         getStyleMetrics(font.getSize2D(), metrics, 4);
 407     }
 408 
 409     /**
 410      * The length of the metrics array must be >= offset+4, and offset must be
 411      * >= 0.  Typically offset is 4.  This method will
 412      * store the following elements in that array before returning:
 413      *    metrics[off+0]: strikethrough offset
 414      *    metrics[off+1]: strikethrough thickness
 415      *    metrics[off+2]: underline offset
 416      *    metrics[off+3]: underline thickness
 417      *
 418      * Note that this implementation simply returns default values;
 419      * subclasses can override this method to provide more accurate values.
 420      */
 421     public void getStyleMetrics(float pointSize, float[] metrics, int offset) {
 422         metrics[offset] = -metrics[0] / 2.5f;
 423         metrics[offset+1] = pointSize / 12;
 424         metrics[offset+2] = metrics[offset+1] / 1.5f;
 425         metrics[offset+3] = metrics[offset+1];
 426     }
 427 
 428     /**
 429      * The length of the metrics array must be >= 4.  This method will
 430      * store the following elements in that array before returning:
 431      *    metrics[0]: ascent
 432      *    metrics[1]: descent
 433      *    metrics[2]: leading
 434      *    metrics[3]: max advance
 435      */
 436     public void getFontMetrics(Font font, FontRenderContext frc,
 437                                float metrics[]) {
 438         StrikeMetrics strikeMetrics = getStrike(font, frc).getFontMetrics();
 439         metrics[0] = strikeMetrics.getAscent();
 440         metrics[1] = strikeMetrics.getDescent();
 441         metrics[2] = strikeMetrics.getLeading();
 442         metrics[3] = strikeMetrics.getMaxAdvance();
 443     }
 444 
 445     /* Currently the layout code calls this. May be better for layout code
 446      * to check the font class before attempting to run, rather than needing
 447      * to promote this method up from TrueTypeFont
 448      */
 449     byte[] getTableBytes(int tag) {
 450         return null;
 451     }
 452 
 453     /* for layout code */
 454     protected long getUnitsPerEm() {
 455         return 2048;
 456     }
 457 
 458     boolean supportsEncoding(String encoding) {
 459         return false;
 460     }
 461 
 462     public boolean canDoStyle(int style) {
 463         return (style == this.style);
 464     }
 465 
 466     /*
 467      * All the important subclasses override this which is principally for
 468      * the TrueType 'gasp' table.
 469      */
 470     public boolean useAAForPtSize(int ptsize) {
 471         return true;
 472     }
 473 
 474     public boolean hasSupplementaryChars() {
 475         return false;
 476     }
 477 
 478     /* The following methods implement public methods on java.awt.Font */
 479     public String getPostscriptName() {
 480         return fullName;
 481     }
 482 
 483     public String getFontName(Locale l) {
 484         return fullName;
 485     }
 486 
 487     public String getFamilyName(Locale l) {
 488         return familyName;
 489     }
 490 
 491     public int getNumGlyphs() {
 492         return getMapper().getNumGlyphs();
 493     }
 494 
 495     public int charToGlyph(int wchar) {
 496         return getMapper().charToGlyph(wchar);
 497     }
 498 
 499     public int getMissingGlyphCode() {
 500         return getMapper().getMissingGlyphCode();
 501     }
 502 
 503     public boolean canDisplay(char c) {
 504         return getMapper().canDisplay(c);
 505     }
 506 
 507     public boolean canDisplay(int cp) {
 508         return getMapper().canDisplay(cp);
 509     }
 510 
 511     public byte getBaselineFor(char c) {
 512         return Font.ROMAN_BASELINE;
 513     }
 514 
 515     public float getItalicAngle(Font font, AffineTransform at,
 516                                 Object aaHint, Object fmHint) {
 517         /* hardwire psz=12 as that's typical and AA vs non-AA for 'gasp' mode
 518          * isn't important for the caret slope of this rarely used API.
 519          */
 520         int aa = FontStrikeDesc.getAAHintIntVal(aaHint, this, 12);
 521         int fm = FontStrikeDesc.getFMHintIntVal(fmHint);
 522         FontStrike strike = getStrike(font, at, aa, fm);
 523         StrikeMetrics metrics = strike.getFontMetrics();
 524         if (metrics.ascentY == 0 || metrics.ascentX == 0) {
 525             return 0f;
 526         } else {
 527             /* ascent is "up" from the baseline so its typically
 528              * a negative value, so we need to compensate
 529              */
 530             return metrics.ascentX/-metrics.ascentY;
 531         }
 532     }
 533 
 534 }