1 /* 2 * Copyright 2003-2008 Sun Microsystems, Inc. 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. Sun designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 22 * CA 95054 USA or visit www.sun.com if you need additional information or 23 * have any questions. 24 */ 25 26 package sun.font; 27 28 import java.awt.Font; 29 import java.awt.FontFormatException; 30 import java.awt.GraphicsEnvironment; 31 import java.awt.geom.Point2D; 32 import java.io.FileNotFoundException; 33 import java.io.IOException; 34 import java.io.RandomAccessFile; 35 import java.io.UnsupportedEncodingException; 36 import java.nio.ByteBuffer; 37 import java.nio.CharBuffer; 38 import java.nio.IntBuffer; 39 import java.nio.ShortBuffer; 40 import java.nio.channels.ClosedChannelException; 41 import java.nio.channels.FileChannel; 42 import java.util.HashSet; 43 import java.util.Locale; 44 import java.util.logging.Level; 45 import sun.java2d.Disposer; 46 import sun.java2d.DisposerRecord; 47 48 /** 49 * TrueTypeFont is not called SFntFont because it is not expected 50 * to handle all types that may be housed in a such a font file. 51 * If additional types are supported later, it may make sense to 52 * create an SFnt superclass. Eg to handle sfnt-housed postscript fonts. 53 * OpenType fonts are handled by this class, and possibly should be 54 * represented by a subclass. 55 * An instance stores some information from the font file to faciliate 56 * faster access. File size, the table directory and the names of the font 57 * are the most important of these. It amounts to approx 400 bytes 58 * for a typical font. Systems with mutiple locales sometimes have up to 400 59 * font files, and an app which loads all font files would need around 60 * 160Kbytes. So storing any more info than this would be expensive. 61 */ 62 public class TrueTypeFont extends FileFont { 63 64 /* -- Tags for required TrueType tables */ 65 public static final int cmapTag = 0x636D6170; // 'cmap' 66 public static final int glyfTag = 0x676C7966; // 'glyf' 67 public static final int headTag = 0x68656164; // 'head' 68 public static final int hheaTag = 0x68686561; // 'hhea' 69 public static final int hmtxTag = 0x686D7478; // 'hmtx' 70 public static final int locaTag = 0x6C6F6361; // 'loca' 71 public static final int maxpTag = 0x6D617870; // 'maxp' 72 public static final int nameTag = 0x6E616D65; // 'name' 73 public static final int postTag = 0x706F7374; // 'post' 74 public static final int os_2Tag = 0x4F532F32; // 'OS/2' 75 76 /* -- Tags for opentype related tables */ 77 public static final int GDEFTag = 0x47444546; // 'GDEF' 78 public static final int GPOSTag = 0x47504F53; // 'GPOS' 79 public static final int GSUBTag = 0x47535542; // 'GSUB' 80 public static final int mortTag = 0x6D6F7274; // 'mort' 81 82 /* -- Tags for non-standard tables */ 83 public static final int fdscTag = 0x66647363; // 'fdsc' - gxFont descriptor 84 public static final int fvarTag = 0x66766172; // 'fvar' - gxFont variations 85 public static final int featTag = 0x66656174; // 'feat' - layout features 86 public static final int EBLCTag = 0x45424C43; // 'EBLC' - embedded bitmaps 87 public static final int gaspTag = 0x67617370; // 'gasp' - hint/smooth sizes 88 89 /* -- Other tags */ 90 public static final int ttcfTag = 0x74746366; // 'ttcf' - TTC file 91 public static final int v1ttTag = 0x00010000; // 'v1tt' - Version 1 TT font 92 public static final int trueTag = 0x74727565; // 'true' - Version 2 TT font 93 public static final int ottoTag = 0x4f54544f; // 'otto' - OpenType font 94 95 /* -- ID's used in the 'name' table */ 96 public static final int MS_PLATFORM_ID = 3; 97 /* MS locale id for US English is the "default" */ 98 public static final short ENGLISH_LOCALE_ID = 0x0409; // 1033 decimal 99 public static final int FAMILY_NAME_ID = 1; 100 // public static final int STYLE_WEIGHT_ID = 2; // currently unused. 101 public static final int FULL_NAME_ID = 4; 102 public static final int POSTSCRIPT_NAME_ID = 6; 103 104 105 class DirectoryEntry { 106 int tag; 107 int offset; 108 int length; 109 } 110 111 /* There is a pool which limits the number of fd's that are in 112 * use. Normally fd's are closed as they are replaced in the pool. 113 * But if an instance of this class becomes unreferenced, then there 114 * needs to be a way to close the fd. A finalize() method could do this, 115 * but using the Disposer class will ensure its called in a more timely 116 * manner. This is not something which should be relied upon to free 117 * fd's - its a safeguard. 118 */ 119 private static class TTDisposerRecord implements DisposerRecord { 120 121 FileChannel channel = null; 122 123 public synchronized void dispose() { 124 try { 125 if (channel != null) { 126 channel.close(); 127 } 128 } catch (IOException e) { 129 } finally { 130 channel = null; 131 } 132 } 133 } 134 135 TTDisposerRecord disposerRecord = new TTDisposerRecord(); 136 137 /* > 0 only if this font is a part of a collection */ 138 int fontIndex = 0; 139 140 /* Number of fonts in this collection. ==1 if not a collection */ 141 int directoryCount = 1; 142 143 /* offset in file of table directory for this font */ 144 int directoryOffset; // 12 if its not a collection. 145 146 /* number of table entries in the directory/offsets table */ 147 int numTables; 148 149 /* The contents of the the directory/offsets table */ 150 DirectoryEntry []tableDirectory; 151 152 // protected byte []gposTable = null; 153 // protected byte []gdefTable = null; 154 // protected byte []gsubTable = null; 155 // protected byte []mortTable = null; 156 // protected boolean hintsTabledChecked = false; 157 // protected boolean containsHintsTable = false; 158 159 /* These fields are set from os/2 table info. */ 160 private boolean supportsJA; 161 private boolean supportsCJK; 162 163 /* These are for faster access to the name of the font as 164 * typically exposed via API to applications. 165 */ 166 private Locale nameLocale; 167 private String localeFamilyName; 168 private String localeFullName; 169 170 /** 171 * - does basic verification of the file 172 * - reads the header table for this font (within a collection) 173 * - reads the names (full, family). 174 * - determines the style of the font. 175 * - initializes the CMAP 176 * @throws FontFormatException - if the font can't be opened 177 * or fails verification, or there's no usable cmap 178 */ 179 TrueTypeFont(String platname, Object nativeNames, int fIndex, 180 boolean javaRasterizer) 181 throws FontFormatException { 182 super(platname, nativeNames); 183 useJavaRasterizer = javaRasterizer; 184 fontRank = Font2D.TTF_RANK; 185 try { 186 verify(); 187 init(fIndex); 188 } catch (Throwable t) { 189 close(); 190 if (t instanceof FontFormatException) { 191 throw (FontFormatException)t; 192 } else { 193 throw new FontFormatException("Unexpected runtime exception."); 194 } 195 } 196 Disposer.addObjectRecord(this, disposerRecord); 197 } 198 199 /* Enable natives just for fonts picked up from the platform that 200 * may have external bitmaps on Solaris. Could do this just for 201 * the fonts that are specified in font configuration files which 202 * would lighten the burden (think about that). 203 * The EBLCTag is used to skip natives for fonts that contain embedded 204 * bitmaps as there's no need to use X11 for those fonts. 205 * Skip all the latin fonts as they don't need this treatment. 206 * Further refine this to fonts that are natively accessible (ie 207 * as PCF bitmap fonts on the X11 font path). 208 * This method is called when creating the first strike for this font. 209 */ 210 protected boolean checkUseNatives() { 211 if (checkedNatives) { 212 return useNatives; 213 } 214 if (!FontManager.isSolaris || useJavaRasterizer || 215 FontManager.useT2K || nativeNames == null || 216 getDirectoryEntry(EBLCTag) != null || 217 GraphicsEnvironment.isHeadless()) { 218 checkedNatives = true; 219 return false; /* useNatives is false */ 220 } else if (nativeNames instanceof String) { 221 String name = (String)nativeNames; 222 /* Don't do do this for Latin fonts */ 223 if (name.indexOf("8859") > 0) { 224 checkedNatives = true; 225 return false; 226 } else if (NativeFont.hasExternalBitmaps(name)) { 227 nativeFonts = new NativeFont[1]; 228 try { 229 nativeFonts[0] = new NativeFont(name, true); 230 /* If reach here we have an non-latin font that has 231 * external bitmaps and we successfully created it. 232 */ 233 useNatives = true; 234 } catch (FontFormatException e) { 235 nativeFonts = null; 236 } 237 } 238 } else if (nativeNames instanceof String[]) { 239 String[] natNames = (String[])nativeNames; 240 int numNames = natNames.length; 241 boolean externalBitmaps = false; 242 for (int nn = 0; nn < numNames; nn++) { 243 if (natNames[nn].indexOf("8859") > 0) { 244 checkedNatives = true; 245 return false; 246 } else if (NativeFont.hasExternalBitmaps(natNames[nn])) { 247 externalBitmaps = true; 248 } 249 } 250 if (!externalBitmaps) { 251 checkedNatives = true; 252 return false; 253 } 254 useNatives = true; 255 nativeFonts = new NativeFont[numNames]; 256 for (int nn = 0; nn < numNames; nn++) { 257 try { 258 nativeFonts[nn] = new NativeFont(natNames[nn], true); 259 } catch (FontFormatException e) { 260 useNatives = false; 261 nativeFonts = null; 262 } 263 } 264 } 265 if (useNatives) { 266 glyphToCharMap = new char[getMapper().getNumGlyphs()]; 267 } 268 checkedNatives = true; 269 return useNatives; 270 } 271 272 273 /* This is intended to be called, and the returned value used, 274 * from within a block synchronized on this font object. 275 * ie the channel returned may be nulled out at any time by "close()" 276 * unless the caller holds a lock. 277 * Deadlock warning: FontManager.addToPool(..) acquires a global lock, 278 * which means nested locks may be in effect. 279 */ 280 private synchronized FileChannel open() throws FontFormatException { 281 if (disposerRecord.channel == null) { 282 if (FontManager.logging) { 283 FontManager.logger.info("open TTF: " + platName); 284 } 285 try { 286 RandomAccessFile raf = (RandomAccessFile) 287 java.security.AccessController.doPrivileged( 288 new java.security.PrivilegedAction() { 289 public Object run() { 290 try { 291 return new RandomAccessFile(platName, "r"); 292 } catch (FileNotFoundException ffne) { 293 } 294 return null; 295 } 296 }); 297 disposerRecord.channel = raf.getChannel(); 298 fileSize = (int)disposerRecord.channel.size(); 299 FontManager.addToPool(this); 300 } catch (NullPointerException e) { 301 close(); 302 throw new FontFormatException(e.toString()); 303 } catch (ClosedChannelException e) { 304 /* NIO I/O is interruptible, recurse to retry operation. 305 * The call to channel.size() above can throw this exception. 306 * Clear interrupts before recursing in case NIO didn't. 307 * Note that close() sets disposerRecord.channel to null. 308 */ 309 Thread.interrupted(); 310 close(); 311 open(); 312 } catch (IOException e) { 313 close(); 314 throw new FontFormatException(e.toString()); 315 } 316 } 317 return disposerRecord.channel; 318 } 319 320 protected synchronized void close() { 321 disposerRecord.dispose(); 322 } 323 324 325 int readBlock(ByteBuffer buffer, int offset, int length) { 326 int bread = 0; 327 try { 328 synchronized (this) { 329 if (disposerRecord.channel == null) { 330 open(); 331 } 332 if (offset + length > fileSize) { 333 if (offset >= fileSize) { 334 /* Since the caller ensures that offset is < fileSize 335 * this condition suggests that fileSize is now 336 * different than the value we originally provided 337 * to native when the scaler was created. 338 * Also fileSize is updated every time we 339 * open() the file here, but in native the value 340 * isn't updated. If the file has changed whilst we 341 * are executing we want to bail, not spin. 342 */ 343 if (FontManager.logging) { 344 String msg = "Read offset is " + offset + 345 " file size is " + fileSize+ 346 " file is " + platName; 347 FontManager.logger.severe(msg); 348 } 349 return -1; 350 } else { 351 length = fileSize - offset; 352 } 353 } 354 buffer.clear(); 355 disposerRecord.channel.position(offset); 356 while (bread < length) { 357 int cnt = disposerRecord.channel.read(buffer); 358 if (cnt == -1) { 359 String msg = "Unexpected EOF " + this; 360 int currSize = (int)disposerRecord.channel.size(); 361 if (currSize != fileSize) { 362 msg += " File size was " + fileSize + 363 " and now is " + currSize; 364 } 365 if (FontManager.logging) { 366 FontManager.logger.severe(msg); 367 } 368 // We could still flip() the buffer here because 369 // it's possible that we did read some data in 370 // an earlier loop, and we probably should 371 // return that to the caller. Although if 372 // the caller expected 8K of data and we return 373 // only a few bytes then maybe it's better instead to 374 // set bread = -1 to indicate failure. 375 // The following is therefore using arbitrary values 376 // but is meant to allow cases where enough 377 // data was read to probably continue. 378 if (bread > length/2 || bread > 16384) { 379 buffer.flip(); 380 if (FontManager.logging) { 381 msg = "Returning " + bread + 382 " bytes instead of " + length; 383 FontManager.logger.severe(msg); 384 } 385 } else { 386 bread = -1; 387 } 388 throw new IOException(msg); 389 } 390 bread += cnt; 391 } 392 buffer.flip(); 393 if (bread > length) { // possible if buffer.size() > length 394 bread = length; 395 } 396 } 397 } catch (FontFormatException e) { 398 if (FontManager.logging) { 399 FontManager.logger.log(Level.SEVERE, 400 "While reading " + platName, e); 401 } 402 bread = -1; // signal EOF 403 deregisterFontAndClearStrikeCache(); 404 } catch (ClosedChannelException e) { 405 /* NIO I/O is interruptible, recurse to retry operation. 406 * Clear interrupts before recursing in case NIO didn't. 407 */ 408 Thread.interrupted(); 409 close(); 410 return readBlock(buffer, offset, length); 411 } catch (IOException e) { 412 /* If we did not read any bytes at all and the exception is 413 * not a recoverable one (ie is not ClosedChannelException) then 414 * we should indicate that there is no point in re-trying. 415 * Other than an attempt to read past the end of the file it 416 * seems unlikely this would occur as problems opening the 417 * file are handled as a FontFormatException. 418 */ 419 if (FontManager.logging) { 420 FontManager.logger.log(Level.SEVERE, 421 "While reading " + platName, e); 422 } 423 if (bread == 0) { 424 bread = -1; // signal EOF 425 deregisterFontAndClearStrikeCache(); 426 } 427 } 428 return bread; 429 } 430 431 ByteBuffer readBlock(int offset, int length) { 432 433 ByteBuffer buffer = ByteBuffer.allocate(length); 434 try { 435 synchronized (this) { 436 if (disposerRecord.channel == null) { 437 open(); 438 } 439 if (offset + length > fileSize) { 440 if (offset > fileSize) { 441 return null; // assert? 442 } else { 443 buffer = ByteBuffer.allocate(fileSize-offset); 444 } 445 } 446 disposerRecord.channel.position(offset); 447 disposerRecord.channel.read(buffer); 448 buffer.flip(); 449 } 450 } catch (FontFormatException e) { 451 return null; 452 } catch (ClosedChannelException e) { 453 /* NIO I/O is interruptible, recurse to retry operation. 454 * Clear interrupts before recursing in case NIO didn't. 455 */ 456 Thread.interrupted(); 457 close(); 458 readBlock(buffer, offset, length); 459 } catch (IOException e) { 460 return null; 461 } 462 return buffer; 463 } 464 465 /* This is used by native code which can't allocate a direct byte 466 * buffer because of bug 4845371. It, and references to it in native 467 * code in scalerMethods.c can be removed once that bug is fixed. 468 * 4845371 is now fixed but we'll keep this around as it doesn't cost 469 * us anything if its never used/called. 470 */ 471 byte[] readBytes(int offset, int length) { 472 ByteBuffer buffer = readBlock(offset, length); 473 if (buffer.hasArray()) { 474 return buffer.array(); 475 } else { 476 byte[] bufferBytes = new byte[buffer.limit()]; 477 buffer.get(bufferBytes); 478 return bufferBytes; 479 } 480 } 481 482 private void verify() throws FontFormatException { 483 open(); 484 } 485 486 /* sizes, in bytes, of TT/TTC header records */ 487 private static final int TTCHEADERSIZE = 12; 488 private static final int DIRECTORYHEADERSIZE = 12; 489 private static final int DIRECTORYENTRYSIZE = 16; 490 491 protected void init(int fIndex) throws FontFormatException { 492 int headerOffset = 0; 493 ByteBuffer buffer = readBlock(0, TTCHEADERSIZE); 494 try { 495 switch (buffer.getInt()) { 496 497 case ttcfTag: 498 buffer.getInt(); // skip TTC version ID 499 directoryCount = buffer.getInt(); 500 if (fIndex >= directoryCount) { 501 throw new FontFormatException("Bad collection index"); 502 } 503 fontIndex = fIndex; 504 buffer = readBlock(TTCHEADERSIZE+4*fIndex, 4); 505 headerOffset = buffer.getInt(); 506 break; 507 508 case v1ttTag: 509 case trueTag: 510 case ottoTag: 511 break; 512 513 default: 514 throw new FontFormatException("Unsupported sfnt " + platName); 515 } 516 517 /* Now have the offset of this TT font (possibly within a TTC) 518 * After the TT version/scaler type field, is the short 519 * representing the number of tables in the table directory. 520 * The table directory begins at 12 bytes after the header. 521 * Each table entry is 16 bytes long (4 32-bit ints) 522 */ 523 buffer = readBlock(headerOffset+4, 2); 524 numTables = buffer.getShort(); 525 directoryOffset = headerOffset+DIRECTORYHEADERSIZE; 526 ByteBuffer bbuffer = readBlock(directoryOffset, 527 numTables*DIRECTORYENTRYSIZE); 528 IntBuffer ibuffer = bbuffer.asIntBuffer(); 529 DirectoryEntry table; 530 tableDirectory = new DirectoryEntry[numTables]; 531 for (int i=0; i<numTables;i++) { 532 tableDirectory[i] = table = new DirectoryEntry(); 533 table.tag = ibuffer.get(); 534 /* checksum */ ibuffer.get(); 535 table.offset = ibuffer.get(); 536 table.length = ibuffer.get(); 537 if (table.offset + table.length > fileSize) { 538 throw new FontFormatException("bad table, tag="+table.tag); 539 } 540 } 541 initNames(); 542 } catch (Exception e) { 543 if (FontManager.logging) { 544 FontManager.logger.severe(e.toString()); 545 } 546 if (e instanceof FontFormatException) { 547 throw (FontFormatException)e; 548 } else { 549 throw new FontFormatException(e.toString()); 550 } 551 } 552 if (familyName == null || fullName == null) { 553 throw new FontFormatException("Font name not found"); 554 } 555 /* The os2_Table is needed to gather some info, but we don't 556 * want to keep it around (as a field) so obtain it once and 557 * pass it to the code that needs it. 558 */ 559 ByteBuffer os2_Table = getTableBuffer(os_2Tag); 560 setStyle(os2_Table); 561 setCJKSupport(os2_Table); 562 } 563 564 /* The array index corresponds to a bit offset in the TrueType 565 * font's OS/2 compatibility table's code page ranges fields. 566 * These are two 32 bit unsigned int fields at offsets 78 and 82. 567 * We are only interested in determining if the font supports 568 * the windows encodings we expect as the default encoding in 569 * supported locales, so we only map the first of these fields. 570 */ 571 static final String encoding_mapping[] = { 572 "cp1252", /* 0:Latin 1 */ 573 "cp1250", /* 1:Latin 2 */ 574 "cp1251", /* 2:Cyrillic */ 575 "cp1253", /* 3:Greek */ 576 "cp1254", /* 4:Turkish/Latin 5 */ 577 "cp1255", /* 5:Hebrew */ 578 "cp1256", /* 6:Arabic */ 579 "cp1257", /* 7:Windows Baltic */ 580 "", /* 8:reserved for alternate ANSI */ 581 "", /* 9:reserved for alternate ANSI */ 582 "", /* 10:reserved for alternate ANSI */ 583 "", /* 11:reserved for alternate ANSI */ 584 "", /* 12:reserved for alternate ANSI */ 585 "", /* 13:reserved for alternate ANSI */ 586 "", /* 14:reserved for alternate ANSI */ 587 "", /* 15:reserved for alternate ANSI */ 588 "ms874", /* 16:Thai */ 589 "ms932", /* 17:JIS/Japanese */ 590 "gbk", /* 18:PRC GBK Cp950 */ 591 "ms949", /* 19:Korean Extended Wansung */ 592 "ms950", /* 20:Chinese (Taiwan, Hongkong, Macau) */ 593 "ms1361", /* 21:Korean Johab */ 594 "", /* 22 */ 595 "", /* 23 */ 596 "", /* 24 */ 597 "", /* 25 */ 598 "", /* 26 */ 599 "", /* 27 */ 600 "", /* 28 */ 601 "", /* 29 */ 602 "", /* 30 */ 603 "", /* 31 */ 604 }; 605 606 /* This maps two letter language codes to a Windows code page. 607 * Note that eg Cp1252 (the first subarray) is not exactly the same as 608 * Latin-1 since Windows code pages are do not necessarily correspond. 609 * There are two codepages for zh and ko so if a font supports 610 * only one of these ranges then we need to distinguish based on 611 * country. So far this only seems to matter for zh. 612 * REMIND: Unicode locales such as Hindi do not have a code page so 613 * this whole mechansim needs to be revised to map languages to 614 * the Unicode ranges either when this fails, or as an additional 615 * validating test. Basing it on Unicode ranges should get us away 616 * from needing to map to this small and incomplete set of Windows 617 * code pages which looks odd on non-Windows platforms. 618 */ 619 private static final String languages[][] = { 620 621 /* cp1252/Latin 1 */ 622 { "en", "ca", "da", "de", "es", "fi", "fr", "is", "it", 623 "nl", "no", "pt", "sq", "sv", }, 624 625 /* cp1250/Latin2 */ 626 { "cs", "cz", "et", "hr", "hu", "nr", "pl", "ro", "sk", 627 "sl", "sq", "sr", }, 628 629 /* cp1251/Cyrillic */ 630 { "bg", "mk", "ru", "sh", "uk" }, 631 632 /* cp1253/Greek*/ 633 { "el" }, 634 635 /* cp1254/Turkish,Latin 5 */ 636 { "tr" }, 637 638 /* cp1255/Hebrew */ 639 { "he" }, 640 641 /* cp1256/Arabic */ 642 { "ar" }, 643 644 /* cp1257/Windows Baltic */ 645 { "et", "lt", "lv" }, 646 647 /* ms874/Thai */ 648 { "th" }, 649 650 /* ms932/Japanese */ 651 { "ja" }, 652 653 /* gbk/Chinese (PRC GBK Cp950) */ 654 { "zh", "zh_CN", }, 655 656 /* ms949/Korean Extended Wansung */ 657 { "ko" }, 658 659 /* ms950/Chinese (Taiwan, Hongkong, Macau) */ 660 { "zh_HK", "zh_TW", }, 661 662 /* ms1361/Korean Johab */ 663 { "ko" }, 664 }; 665 666 private static final String codePages[] = { 667 "cp1252", 668 "cp1250", 669 "cp1251", 670 "cp1253", 671 "cp1254", 672 "cp1255", 673 "cp1256", 674 "cp1257", 675 "ms874", 676 "ms932", 677 "gbk", 678 "ms949", 679 "ms950", 680 "ms1361", 681 }; 682 683 private static String defaultCodePage = null; 684 static String getCodePage() { 685 686 if (defaultCodePage != null) { 687 return defaultCodePage; 688 } 689 690 if (FontManager.isWindows) { 691 defaultCodePage = 692 (String)java.security.AccessController.doPrivileged( 693 new sun.security.action.GetPropertyAction("file.encoding")); 694 } else { 695 if (languages.length != codePages.length) { 696 throw new InternalError("wrong code pages array length"); 697 } 698 Locale locale = sun.awt.SunToolkit.getStartupLocale(); 699 700 String language = locale.getLanguage(); 701 if (language != null) { 702 if (language.equals("zh")) { 703 String country = locale.getCountry(); 704 if (country != null) { 705 language = language + "_" + country; 706 } 707 } 708 for (int i=0; i<languages.length;i++) { 709 for (int l=0;l<languages[i].length; l++) { 710 if (language.equals(languages[i][l])) { 711 defaultCodePage = codePages[i]; 712 return defaultCodePage; 713 } 714 } 715 } 716 } 717 } 718 if (defaultCodePage == null) { 719 defaultCodePage = ""; 720 } 721 return defaultCodePage; 722 } 723 724 /* Theoretically, reserved bits must not be set, include symbol bits */ 725 public static final int reserved_bits1 = 0x80000000; 726 public static final int reserved_bits2 = 0x0000ffff; 727 boolean supportsEncoding(String encoding) { 728 if (encoding == null) { 729 encoding = getCodePage(); 730 } 731 if ("".equals(encoding)) { 732 return false; 733 } 734 735 encoding = encoding.toLowerCase(); 736 737 /* java_props_md.c has a couple of special cases 738 * if language packs are installed. In these encodings the 739 * fontconfig files pick up different fonts : 740 * SimSun-18030 and MingLiU_HKSCS. Since these fonts will 741 * indicate they support the base encoding, we need to rewrite 742 * these encodings here before checking the map/array. 743 */ 744 if (encoding.equals("gb18030")) { 745 encoding = "gbk"; 746 } else if (encoding.equals("ms950_hkscs")) { 747 encoding = "ms950"; 748 } 749 750 ByteBuffer buffer = getTableBuffer(os_2Tag); 751 /* required info is at offsets 78 and 82 */ 752 if (buffer == null || buffer.capacity() < 86) { 753 return false; 754 } 755 756 int range1 = buffer.getInt(78); /* ulCodePageRange1 */ 757 int range2 = buffer.getInt(82); /* ulCodePageRange2 */ 758 759 /* This test is too stringent for Arial on Solaris (and perhaps 760 * other fonts). Arial has at least one reserved bit set for an 761 * unknown reason. 762 */ 763 // if (((range1 & reserved_bits1) | (range2 & reserved_bits2)) != 0) { 764 // return false; 765 // } 766 767 for (int em=0; em<encoding_mapping.length; em++) { 768 if (encoding_mapping[em].equals(encoding)) { 769 if (((1 << em) & range1) != 0) { 770 return true; 771 } 772 } 773 } 774 return false; 775 } 776 777 778 /* Use info in the os_2Table to test CJK support */ 779 private void setCJKSupport(ByteBuffer os2Table) { 780 /* required info is in ulong at offset 46 */ 781 if (os2Table == null || os2Table.capacity() < 50) { 782 return; 783 } 784 int range2 = os2Table.getInt(46); /* ulUnicodeRange2 */ 785 786 /* Any of these bits set in the 32-63 range indicate a font with 787 * support for a CJK range. We aren't looking at some other bits 788 * in the 64-69 range such as half width forms as its unlikely a font 789 * would include those and none of these. 790 */ 791 supportsCJK = ((range2 & 0x29bf0000) != 0); 792 793 /* This should be generalised, but for now just need to know if 794 * Hiragana or Katakana ranges are supported by the font. 795 * In the 4 longs representing unicode ranges supported 796 * bits 49 & 50 indicate hiragana and katakana 797 * This is bits 17 & 18 in the 2nd ulong. If either is supported 798 * we presume this is a JA font. 799 */ 800 supportsJA = ((range2 & 0x60000) != 0); 801 } 802 803 boolean supportsJA() { 804 return supportsJA; 805 } 806 807 ByteBuffer getTableBuffer(int tag) { 808 DirectoryEntry entry = null; 809 810 for (int i=0;i<numTables;i++) { 811 if (tableDirectory[i].tag == tag) { 812 entry = tableDirectory[i]; 813 break; 814 } 815 } 816 if (entry == null || entry.length == 0 || 817 entry.offset+entry.length > fileSize) { 818 return null; 819 } 820 821 int bread = 0; 822 ByteBuffer buffer = ByteBuffer.allocate(entry.length); 823 synchronized (this) { 824 try { 825 if (disposerRecord.channel == null) { 826 open(); 827 } 828 disposerRecord.channel.position(entry.offset); 829 bread = disposerRecord.channel.read(buffer); 830 buffer.flip(); 831 } catch (ClosedChannelException e) { 832 /* NIO I/O is interruptible, recurse to retry operation. 833 * Clear interrupts before recursing in case NIO didn't. 834 */ 835 Thread.interrupted(); 836 close(); 837 return getTableBuffer(tag); 838 } catch (IOException e) { 839 return null; 840 } catch (FontFormatException e) { 841 return null; 842 } 843 844 if (bread < entry.length) { 845 return null; 846 } else { 847 return buffer; 848 } 849 } 850 } 851 852 /* NB: is it better to move declaration to Font2D? */ 853 long getLayoutTableCache() { 854 try { 855 return getScaler().getLayoutTableCache(); 856 } catch(FontScalerException fe) { 857 return 0L; 858 } 859 } 860 861 byte[] getTableBytes(int tag) { 862 ByteBuffer buffer = getTableBuffer(tag); 863 if (buffer == null) { 864 return null; 865 } else if (buffer.hasArray()) { 866 try { 867 return buffer.array(); 868 } catch (Exception re) { 869 } 870 } 871 byte []data = new byte[getTableSize(tag)]; 872 buffer.get(data); 873 return data; 874 } 875 876 int getTableSize(int tag) { 877 for (int i=0;i<numTables;i++) { 878 if (tableDirectory[i].tag == tag) { 879 return tableDirectory[i].length; 880 } 881 } 882 return 0; 883 } 884 885 int getTableOffset(int tag) { 886 for (int i=0;i<numTables;i++) { 887 if (tableDirectory[i].tag == tag) { 888 return tableDirectory[i].offset; 889 } 890 } 891 return 0; 892 } 893 894 DirectoryEntry getDirectoryEntry(int tag) { 895 for (int i=0;i<numTables;i++) { 896 if (tableDirectory[i].tag == tag) { 897 return tableDirectory[i]; 898 } 899 } 900 return null; 901 } 902 903 /* Used to determine if this size has embedded bitmaps, which 904 * for CJK fonts should be used in preference to LCD glyphs. 905 */ 906 boolean useEmbeddedBitmapsForSize(int ptSize) { 907 if (!supportsCJK) { 908 return false; 909 } 910 if (getDirectoryEntry(EBLCTag) == null) { 911 return false; 912 } 913 ByteBuffer eblcTable = getTableBuffer(EBLCTag); 914 int numSizes = eblcTable.getInt(4); 915 /* The bitmapSizeTable's start at offset of 8. 916 * Each bitmapSizeTable entry is 48 bytes. 917 * The offset of ppemY in the entry is 45. 918 */ 919 for (int i=0;i<numSizes;i++) { 920 int ppemY = eblcTable.get(8+(i*48)+45) &0xff; 921 if (ppemY == ptSize) { 922 return true; 923 } 924 } 925 return false; 926 } 927 928 public String getFullName() { 929 return fullName; 930 } 931 932 /* This probably won't get called but is there to support the 933 * contract() of setStyle() defined in the superclass. 934 */ 935 protected void setStyle() { 936 setStyle(getTableBuffer(os_2Tag)); 937 } 938 939 /* TrueTypeFont can use the fsSelection fields of OS/2 table 940 * to determine the style. In the unlikely case that doesn't exist, 941 * can use macStyle in the 'head' table but simpler to 942 * fall back to super class algorithm of looking for well known string. 943 * A very few fonts don't specify this information, but I only 944 * came across one: Lucida Sans Thai Typewriter Oblique in 945 * /usr/openwin/lib/locale/th_TH/X11/fonts/TrueType/lucidai.ttf 946 * that explicitly specified the wrong value. It says its regular. 947 * I didn't find any fonts that were inconsistent (ie regular plus some 948 * other value). 949 */ 950 private static final int fsSelectionItalicBit = 0x00001; 951 private static final int fsSelectionBoldBit = 0x00020; 952 private static final int fsSelectionRegularBit = 0x00040; 953 private void setStyle(ByteBuffer os_2Table) { 954 /* fsSelection is unsigned short at buffer offset 62 */ 955 if (os_2Table == null || os_2Table.capacity() < 64) { 956 super.setStyle(); 957 return; 958 } 959 int fsSelection = os_2Table.getChar(62) & 0xffff; 960 int italic = fsSelection & fsSelectionItalicBit; 961 int bold = fsSelection & fsSelectionBoldBit; 962 int regular = fsSelection & fsSelectionRegularBit; 963 // System.out.println("platname="+platName+" font="+fullName+ 964 // " family="+familyName+ 965 // " R="+regular+" I="+italic+" B="+bold); 966 if (regular!=0 && ((italic|bold)!=0)) { 967 /* This is inconsistent. Try using the font name algorithm */ 968 super.setStyle(); 969 return; 970 } else if ((regular|italic|bold) == 0) { 971 /* No style specified. Try using the font name algorithm */ 972 super.setStyle(); 973 return; 974 } 975 switch (bold|italic) { 976 case fsSelectionItalicBit: 977 style = Font.ITALIC; 978 break; 979 case fsSelectionBoldBit: 980 if (FontManager.isSolaris && platName.endsWith("HG-GothicB.ttf")) { 981 /* Workaround for Solaris's use of a JA font that's marked as 982 * being designed bold, but is used as a PLAIN font. 983 */ 984 style = Font.PLAIN; 985 } else { 986 style = Font.BOLD; 987 } 988 break; 989 case fsSelectionBoldBit|fsSelectionItalicBit: 990 style = Font.BOLD|Font.ITALIC; 991 } 992 } 993 994 private float stSize, stPos, ulSize, ulPos; 995 996 private void setStrikethroughMetrics(ByteBuffer os_2Table, int upem) { 997 if (os_2Table == null || os_2Table.capacity() < 30 || upem < 0) { 998 stSize = .05f; 999 stPos = -.4f; 1000 return; 1001 } 1002 ShortBuffer sb = os_2Table.asShortBuffer(); 1003 stSize = sb.get(13) / (float)upem; 1004 stPos = -sb.get(14) / (float)upem; 1005 } 1006 1007 private void setUnderlineMetrics(ByteBuffer postTable, int upem) { 1008 if (postTable == null || postTable.capacity() < 12 || upem < 0) { 1009 ulSize = .05f; 1010 ulPos = .1f; 1011 return; 1012 } 1013 ShortBuffer sb = postTable.asShortBuffer(); 1014 ulSize = sb.get(5) / (float)upem; 1015 ulPos = -sb.get(4) / (float)upem; 1016 } 1017 1018 public void getStyleMetrics(float pointSize, float[] metrics, int offset) { 1019 1020 if (ulSize == 0f && ulPos == 0f) { 1021 1022 ByteBuffer head_Table = getTableBuffer(headTag); 1023 int upem = -1; 1024 if (head_Table != null && head_Table.capacity() >= 18) { 1025 ShortBuffer sb = head_Table.asShortBuffer(); 1026 upem = sb.get(9) & 0xffff; 1027 } 1028 1029 ByteBuffer os2_Table = getTableBuffer(os_2Tag); 1030 setStrikethroughMetrics(os2_Table, upem); 1031 1032 ByteBuffer post_Table = getTableBuffer(postTag); 1033 setUnderlineMetrics(post_Table, upem); 1034 } 1035 1036 metrics[offset] = stPos * pointSize; 1037 metrics[offset+1] = stSize * pointSize; 1038 1039 metrics[offset+2] = ulPos * pointSize; 1040 metrics[offset+3] = ulSize * pointSize; 1041 } 1042 1043 private String makeString(byte[] bytes, int len, short encoding) { 1044 1045 /* Check for fonts using encodings 2->6 is just for 1046 * some old DBCS fonts, apparently mostly on Solaris. 1047 * Some of these fonts encode ascii names as double-byte characters. 1048 * ie with a leading zero byte for what properly should be a 1049 * single byte-char. 1050 */ 1051 if (encoding >=2 && encoding <= 6) { 1052 byte[] oldbytes = bytes; 1053 int oldlen = len; 1054 bytes = new byte[oldlen]; 1055 len = 0; 1056 for (int i=0; i<oldlen; i++) { 1057 if (oldbytes[i] != 0) { 1058 bytes[len++] = oldbytes[i]; 1059 } 1060 } 1061 } 1062 1063 String charset; 1064 switch (encoding) { 1065 case 1: charset = "UTF-16"; break; // most common case first. 1066 case 0: charset = "UTF-16"; break; // symbol uses this 1067 case 2: charset = "SJIS"; break; 1068 case 3: charset = "GBK"; break; 1069 case 4: charset = "MS950"; break; 1070 case 5: charset = "EUC_KR"; break; 1071 case 6: charset = "Johab"; break; 1072 default: charset = "UTF-16"; break; 1073 } 1074 1075 try { 1076 return new String(bytes, 0, len, charset); 1077 } catch (UnsupportedEncodingException e) { 1078 if (FontManager.logging) { 1079 FontManager.logger.warning(e + " EncodingID=" + encoding); 1080 } 1081 return new String(bytes, 0, len); 1082 } catch (Throwable t) { 1083 return null; 1084 } 1085 } 1086 1087 protected void initNames() { 1088 1089 byte[] name = new byte[256]; 1090 ByteBuffer buffer = getTableBuffer(nameTag); 1091 1092 if (buffer != null) { 1093 ShortBuffer sbuffer = buffer.asShortBuffer(); 1094 sbuffer.get(); // format - not needed. 1095 short numRecords = sbuffer.get(); 1096 /* The name table uses unsigned shorts. Many of these 1097 * are known small values that fit in a short. 1098 * The values that are sizes or offsets into the table could be 1099 * greater than 32767, so read and store those as ints 1100 */ 1101 int stringPtr = sbuffer.get() & 0xffff; 1102 1103 nameLocale = sun.awt.SunToolkit.getStartupLocale(); 1104 short nameLocaleID = FontManager.getLCIDFromLocale(nameLocale); 1105 1106 for (int i=0; i<numRecords; i++) { 1107 short platformID = sbuffer.get(); 1108 if (platformID != MS_PLATFORM_ID) { 1109 sbuffer.position(sbuffer.position()+5); 1110 continue; // skip over this record. 1111 } 1112 short encodingID = sbuffer.get(); 1113 short langID = sbuffer.get(); 1114 short nameID = sbuffer.get(); 1115 int nameLen = ((int) sbuffer.get()) & 0xffff; 1116 int namePtr = (((int) sbuffer.get()) & 0xffff) + stringPtr; 1117 String tmpName = null; 1118 switch (nameID) { 1119 1120 case FAMILY_NAME_ID: 1121 1122 if (familyName == null || langID == ENGLISH_LOCALE_ID || 1123 langID == nameLocaleID) 1124 { 1125 buffer.position(namePtr); 1126 buffer.get(name, 0, nameLen); 1127 tmpName = makeString(name, nameLen, encodingID); 1128 1129 if (familyName == null || langID == ENGLISH_LOCALE_ID){ 1130 familyName = tmpName; 1131 } 1132 if (langID == nameLocaleID) { 1133 localeFamilyName = tmpName; 1134 } 1135 } 1136 /* 1137 for (int ii=0;ii<nameLen;ii++) { 1138 int val = (int)name[ii]&0xff; 1139 System.err.print(Integer.toHexString(val)+ " "); 1140 } 1141 System.err.println(); 1142 System.err.println("familyName="+familyName + 1143 " nameLen="+nameLen+ 1144 " langID="+langID+ " eid="+encodingID + 1145 " str len="+familyName.length()); 1146 1147 */ 1148 break; 1149 1150 case FULL_NAME_ID: 1151 1152 if (fullName == null || langID == ENGLISH_LOCALE_ID || 1153 langID == nameLocaleID) 1154 { 1155 buffer.position(namePtr); 1156 buffer.get(name, 0, nameLen); 1157 tmpName = makeString(name, nameLen, encodingID); 1158 1159 if (fullName == null || langID == ENGLISH_LOCALE_ID) { 1160 fullName = tmpName; 1161 } 1162 if (langID == nameLocaleID) { 1163 localeFullName = tmpName; 1164 } 1165 } 1166 break; 1167 } 1168 } 1169 if (localeFamilyName == null) { 1170 localeFamilyName = familyName; 1171 } 1172 if (localeFullName == null) { 1173 localeFullName = fullName; 1174 } 1175 } 1176 } 1177 1178 /* Return the requested name in the requested locale, for the 1179 * MS platform ID. If the requested locale isn't found, return US 1180 * English, if that isn't found, return null and let the caller 1181 * figure out how to handle that. 1182 */ 1183 protected String lookupName(short findLocaleID, int findNameID) { 1184 String foundName = null; 1185 byte[] name = new byte[1024]; 1186 1187 ByteBuffer buffer = getTableBuffer(nameTag); 1188 if (buffer != null) { 1189 ShortBuffer sbuffer = buffer.asShortBuffer(); 1190 sbuffer.get(); // format - not needed. 1191 short numRecords = sbuffer.get(); 1192 1193 /* The name table uses unsigned shorts. Many of these 1194 * are known small values that fit in a short. 1195 * The values that are sizes or offsets into the table could be 1196 * greater than 32767, so read and store those as ints 1197 */ 1198 int stringPtr = ((int) sbuffer.get()) & 0xffff; 1199 1200 for (int i=0; i<numRecords; i++) { 1201 short platformID = sbuffer.get(); 1202 if (platformID != MS_PLATFORM_ID) { 1203 sbuffer.position(sbuffer.position()+5); 1204 continue; // skip over this record. 1205 } 1206 short encodingID = sbuffer.get(); 1207 short langID = sbuffer.get(); 1208 short nameID = sbuffer.get(); 1209 int nameLen = ((int) sbuffer.get()) & 0xffff; 1210 int namePtr = (((int) sbuffer.get()) & 0xffff) + stringPtr; 1211 if (nameID == findNameID && 1212 ((foundName == null && langID == ENGLISH_LOCALE_ID) 1213 || langID == findLocaleID)) { 1214 buffer.position(namePtr); 1215 buffer.get(name, 0, nameLen); 1216 foundName = makeString(name, nameLen, encodingID); 1217 if (langID == findLocaleID) { 1218 return foundName; 1219 } 1220 } 1221 } 1222 } 1223 return foundName; 1224 } 1225 1226 /** 1227 * @return number of logical fonts. Is "1" for all but TTC files 1228 */ 1229 public int getFontCount() { 1230 return directoryCount; 1231 } 1232 1233 protected synchronized FontScaler getScaler() { 1234 if (scaler == null) { 1235 scaler = FontManager.getScaler(this, fontIndex, 1236 supportsCJK, fileSize); 1237 } 1238 return scaler; 1239 } 1240 1241 1242 /* Postscript name is rarely requested. Don't waste cycles locating it 1243 * as part of font creation, nor storage to hold it. Get it only on demand. 1244 */ 1245 public String getPostscriptName() { 1246 String name = lookupName(ENGLISH_LOCALE_ID, POSTSCRIPT_NAME_ID); 1247 if (name == null) { 1248 return fullName; 1249 } else { 1250 return name; 1251 } 1252 } 1253 1254 public String getFontName(Locale locale) { 1255 if (locale == null) { 1256 return fullName; 1257 } else if (locale.equals(nameLocale) && localeFullName != null) { 1258 return localeFullName; 1259 } else { 1260 short localeID = FontManager.getLCIDFromLocale(locale); 1261 String name = lookupName(localeID, FULL_NAME_ID); 1262 if (name == null) { 1263 return fullName; 1264 } else { 1265 return name; 1266 } 1267 } 1268 } 1269 1270 public String getFamilyName(Locale locale) { 1271 if (locale == null) { 1272 return familyName; 1273 } else if (locale.equals(nameLocale) && localeFamilyName != null) { 1274 return localeFamilyName; 1275 } else { 1276 short localeID = FontManager.getLCIDFromLocale(locale); 1277 String name = lookupName(localeID, FAMILY_NAME_ID); 1278 if (name == null) { 1279 return familyName; 1280 } else { 1281 return name; 1282 } 1283 } 1284 } 1285 1286 public CharToGlyphMapper getMapper() { 1287 if (mapper == null) { 1288 mapper = new TrueTypeGlyphMapper(this); 1289 } 1290 return mapper; 1291 } 1292 1293 /* This duplicates initNames() but that has to run fast as its used 1294 * during typical start-up and the information here is likely never 1295 * needed. 1296 */ 1297 protected void initAllNames(int requestedID, HashSet names) { 1298 1299 byte[] name = new byte[256]; 1300 ByteBuffer buffer = getTableBuffer(nameTag); 1301 1302 if (buffer != null) { 1303 ShortBuffer sbuffer = buffer.asShortBuffer(); 1304 sbuffer.get(); // format - not needed. 1305 short numRecords = sbuffer.get(); 1306 1307 /* The name table uses unsigned shorts. Many of these 1308 * are known small values that fit in a short. 1309 * The values that are sizes or offsets into the table could be 1310 * greater than 32767, so read and store those as ints 1311 */ 1312 int stringPtr = ((int) sbuffer.get()) & 0xffff; 1313 for (int i=0; i<numRecords; i++) { 1314 short platformID = sbuffer.get(); 1315 if (platformID != MS_PLATFORM_ID) { 1316 sbuffer.position(sbuffer.position()+5); 1317 continue; // skip over this record. 1318 } 1319 short encodingID = sbuffer.get(); 1320 short langID = sbuffer.get(); 1321 short nameID = sbuffer.get(); 1322 int nameLen = ((int) sbuffer.get()) & 0xffff; 1323 int namePtr = (((int) sbuffer.get()) & 0xffff) + stringPtr; 1324 1325 if (nameID == requestedID) { 1326 buffer.position(namePtr); 1327 buffer.get(name, 0, nameLen); 1328 names.add(makeString(name, nameLen, encodingID)); 1329 } 1330 } 1331 } 1332 } 1333 1334 String[] getAllFamilyNames() { 1335 HashSet aSet = new HashSet(); 1336 try { 1337 initAllNames(FAMILY_NAME_ID, aSet); 1338 } catch (Exception e) { 1339 /* In case of malformed font */ 1340 } 1341 return (String[])aSet.toArray(new String[0]); 1342 } 1343 1344 String[] getAllFullNames() { 1345 HashSet aSet = new HashSet(); 1346 try { 1347 initAllNames(FULL_NAME_ID, aSet); 1348 } catch (Exception e) { 1349 /* In case of malformed font */ 1350 } 1351 return (String[])aSet.toArray(new String[0]); 1352 } 1353 1354 /* Used by the OpenType engine for mark positioning. 1355 */ 1356 Point2D.Float getGlyphPoint(long pScalerContext, 1357 int glyphCode, int ptNumber) { 1358 try { 1359 return getScaler().getGlyphPoint(pScalerContext, 1360 glyphCode, ptNumber); 1361 } catch(FontScalerException fe) { 1362 return null; 1363 } 1364 } 1365 1366 private char[] gaspTable; 1367 1368 private char[] getGaspTable() { 1369 1370 if (gaspTable != null) { 1371 return gaspTable; 1372 } 1373 1374 ByteBuffer buffer = getTableBuffer(gaspTag); 1375 if (buffer == null) { 1376 return gaspTable = new char[0]; 1377 } 1378 1379 CharBuffer cbuffer = buffer.asCharBuffer(); 1380 char format = cbuffer.get(); 1381 /* format "1" has appeared for some Windows Vista fonts. 1382 * Its presently undocumented but the existing values 1383 * seem to be still valid so we can use it. 1384 */ 1385 if (format > 1) { // unrecognised format 1386 return gaspTable = new char[0]; 1387 } 1388 1389 char numRanges = cbuffer.get(); 1390 if (4+numRanges*4 > getTableSize(gaspTag)) { // sanity check 1391 return gaspTable = new char[0]; 1392 } 1393 gaspTable = new char[2*numRanges]; 1394 cbuffer.get(gaspTable); 1395 return gaspTable; 1396 } 1397 1398 /* This is to obtain info from the TT 'gasp' (grid-fitting and 1399 * scan-conversion procedure) table which specifies three combinations: 1400 * Hint, Smooth (greyscale), Hint and Smooth. 1401 * In this simplified scheme we don't distinguish the latter two. We 1402 * hint even at small sizes, so as to preserve metrics consistency. 1403 * If the information isn't available default values are substituted. 1404 * The more precise defaults we'd do if we distinguished the cases are: 1405 * Bold (no other style) fonts : 1406 * 0-8 : Smooth ( do grey) 1407 * 9+ : Hint + smooth (gridfit + grey) 1408 * Plain, Italic and Bold-Italic fonts : 1409 * 0-8 : Smooth ( do grey) 1410 * 9-17 : Hint (gridfit) 1411 * 18+ : Hint + smooth (gridfit + grey) 1412 * The defaults should rarely come into play as most TT fonts provide 1413 * better defaults. 1414 * REMIND: consider unpacking the table into an array of booleans 1415 * for faster use. 1416 */ 1417 public boolean useAAForPtSize(int ptsize) { 1418 1419 char[] gasp = getGaspTable(); 1420 if (gasp.length > 0) { 1421 for (int i=0;i<gasp.length;i+=2) { 1422 if (ptsize <= gasp[i]) { 1423 return ((gasp[i+1] & 0x2) != 0); // bit 2 means DO_GRAY; 1424 } 1425 } 1426 return true; 1427 } 1428 1429 if (style == Font.BOLD) { 1430 return true; 1431 } else { 1432 return ptsize <= 8 || ptsize >= 18; 1433 } 1434 } 1435 1436 public boolean hasSupplementaryChars() { 1437 return ((TrueTypeGlyphMapper)getMapper()).hasSupplementaryChars(); 1438 } 1439 1440 public String toString() { 1441 return "** TrueType Font: Family="+familyName+ " Name="+fullName+ 1442 " style="+style+" fileName="+platName; 1443 } 1444 }