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