1 /* 2 * Copyright (c) 2003, 2013, 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.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.HashMap; 43 import java.util.HashSet; 44 import java.util.Map; 45 import java.util.Locale; 46 import sun.java2d.Disposer; 47 import sun.java2d.DisposerRecord; 48 49 /** 50 * TrueTypeFont is not called SFntFont because it is not expected 51 * to handle all types that may be housed in a such a font file. 52 * If additional types are supported later, it may make sense to 53 * create an SFnt superclass. Eg to handle sfnt-housed postscript fonts. 54 * OpenType fonts are handled by this class, and possibly should be 55 * represented by a subclass. 56 * An instance stores some information from the font file to faciliate 57 * faster access. File size, the table directory and the names of the font 58 * are the most important of these. It amounts to approx 400 bytes 59 * for a typical font. Systems with mutiple locales sometimes have up to 400 60 * font files, and an app which loads all font files would need around 61 * 160Kbytes. So storing any more info than this would be expensive. 62 */ 63 public class TrueTypeFont extends FileFont { 64 65 /* -- Tags for required TrueType tables */ 66 public static final int cmapTag = 0x636D6170; // 'cmap' 67 public static final int glyfTag = 0x676C7966; // 'glyf' 68 public static final int headTag = 0x68656164; // 'head' 69 public static final int hheaTag = 0x68686561; // 'hhea' 70 public static final int hmtxTag = 0x686D7478; // 'hmtx' 71 public static final int locaTag = 0x6C6F6361; // 'loca' 72 public static final int maxpTag = 0x6D617870; // 'maxp' 73 public static final int nameTag = 0x6E616D65; // 'name' 74 public static final int postTag = 0x706F7374; // 'post' 75 public static final int os_2Tag = 0x4F532F32; // 'OS/2' 76 77 /* -- Tags for opentype related tables */ 78 public static final int GDEFTag = 0x47444546; // 'GDEF' 79 public static final int GPOSTag = 0x47504F53; // 'GPOS' 80 public static final int GSUBTag = 0x47535542; // 'GSUB' 81 public static final int mortTag = 0x6D6F7274; // 'mort' 82 public static final int morxTag = 0x6D6F7278; // 'morx' 83 84 /* -- Tags for non-standard tables */ 85 public static final int fdscTag = 0x66647363; // 'fdsc' - gxFont descriptor 86 public static final int fvarTag = 0x66766172; // 'fvar' - gxFont variations 87 public static final int featTag = 0x66656174; // 'feat' - layout features 88 public static final int EBLCTag = 0x45424C43; // 'EBLC' - embedded bitmaps 89 public static final int gaspTag = 0x67617370; // 'gasp' - hint/smooth sizes 90 91 /* -- Other tags */ 92 public static final int ttcfTag = 0x74746366; // 'ttcf' - TTC file 93 public static final int v1ttTag = 0x00010000; // 'v1tt' - Version 1 TT font 94 public static final int trueTag = 0x74727565; // 'true' - Version 2 TT font 95 public static final int ottoTag = 0x4f54544f; // 'otto' - OpenType font 96 97 /* -- ID's used in the 'name' table */ 98 public static final int MS_PLATFORM_ID = 3; 99 /* MS locale id for US English is the "default" */ 100 public static final short ENGLISH_LOCALE_ID = 0x0409; // 1033 decimal 101 public static final int FAMILY_NAME_ID = 1; 102 // public static final int STYLE_WEIGHT_ID = 2; // currently unused. 103 public static final int FULL_NAME_ID = 4; 104 public static final int POSTSCRIPT_NAME_ID = 6; 105 106 private static final short US_LCID = 0x0409; // US English - default 107 108 private static Map<String, Short> lcidMap; 109 110 static class DirectoryEntry { 111 int tag; 112 int offset; 113 int length; 114 } 115 116 /* There is a pool which limits the number of fd's that are in 117 * use. Normally fd's are closed as they are replaced in the pool. 118 * But if an instance of this class becomes unreferenced, then there 119 * needs to be a way to close the fd. A finalize() method could do this, 120 * but using the Disposer class will ensure its called in a more timely 121 * manner. This is not something which should be relied upon to free 122 * fd's - its a safeguard. 123 */ 124 private static class TTDisposerRecord implements DisposerRecord { 125 126 FileChannel channel = null; 127 dispose()128 public synchronized void dispose() { 129 try { 130 if (channel != null) { 131 channel.close(); 132 } 133 } catch (IOException e) { 134 } finally { 135 channel = null; 136 } 137 } 138 } 139 140 TTDisposerRecord disposerRecord = new TTDisposerRecord(); 141 142 /* > 0 only if this font is a part of a collection */ 143 int fontIndex = 0; 144 145 /* Number of fonts in this collection. ==1 if not a collection */ 146 int directoryCount = 1; 147 148 /* offset in file of table directory for this font */ 149 int directoryOffset; // 12 if its not a collection. 150 151 /* number of table entries in the directory/offsets table */ 152 int numTables; 153 154 /* The contents of the the directory/offsets table */ 155 DirectoryEntry []tableDirectory; 156 157 // protected byte []gposTable = null; 158 // protected byte []gdefTable = null; 159 // protected byte []gsubTable = null; 160 // protected byte []mortTable = null; 161 // protected boolean hintsTabledChecked = false; 162 // protected boolean containsHintsTable = false; 163 164 /* These fields are set from os/2 table info. */ 165 private boolean supportsJA; 166 private boolean supportsCJK; 167 168 /* These are for faster access to the name of the font as 169 * typically exposed via API to applications. 170 */ 171 private Locale nameLocale; 172 private String localeFamilyName; 173 private String localeFullName; 174 175 /* 176 * Used on Windows to validate the font selected by GDI for (sub-pixel 177 * antialiased) rendering. For 'standalone' fonts it's equal to the font 178 * file size, for collection (TTC, OTC) members it's the number of bytes in 179 * the collection file from the start of this font's offset table till the 180 * end of the file. 181 */ 182 int fontDataSize; 183 TrueTypeFont(String platname, Object nativeNames, int fIndex, boolean javaRasterizer)184 public TrueTypeFont(String platname, Object nativeNames, int fIndex, 185 boolean javaRasterizer) 186 throws FontFormatException 187 { 188 this(platname, nativeNames, fIndex, javaRasterizer, true); 189 } 190 191 /** 192 * - does basic verification of the file 193 * - reads the header table for this font (within a collection) 194 * - reads the names (full, family). 195 * - determines the style of the font. 196 * - initializes the CMAP 197 * @throws FontFormatException - if the font can't be opened 198 * or fails verification, or there's no usable cmap 199 */ TrueTypeFont(String platname, Object nativeNames, int fIndex, boolean javaRasterizer, boolean useFilePool)200 public TrueTypeFont(String platname, Object nativeNames, int fIndex, 201 boolean javaRasterizer, boolean useFilePool) 202 throws FontFormatException { 203 super(platname, nativeNames); 204 useJavaRasterizer = javaRasterizer; 205 fontRank = Font2D.TTF_RANK; 206 try { 207 verify(useFilePool); 208 init(fIndex); 209 if (!useFilePool) { 210 close(); 211 } 212 } catch (Throwable t) { 213 close(); 214 if (t instanceof FontFormatException) { 215 throw (FontFormatException)t; 216 } else { 217 throw new FontFormatException("Unexpected runtime exception."); 218 } 219 } 220 Disposer.addObjectRecord(this, disposerRecord); 221 } 222 223 /* Enable natives just for fonts picked up from the platform that 224 * may have external bitmaps on Solaris. Could do this just for 225 * the fonts that are specified in font configuration files which 226 * would lighten the burden (think about that). 227 * The EBLCTag is used to skip natives for fonts that contain embedded 228 * bitmaps as there's no need to use X11 for those fonts. 229 * Skip all the latin fonts as they don't need this treatment. 230 * Further refine this to fonts that are natively accessible (ie 231 * as PCF bitmap fonts on the X11 font path). 232 * This method is called when creating the first strike for this font. 233 */ 234 @Override checkUseNatives()235 protected boolean checkUseNatives() { 236 if (checkedNatives) { 237 return useNatives; 238 } 239 if (!FontUtilities.isSolaris || useJavaRasterizer || 240 FontUtilities.useT2K || nativeNames == null || 241 getDirectoryEntry(EBLCTag) != null || 242 GraphicsEnvironment.isHeadless()) { 243 checkedNatives = true; 244 return false; /* useNatives is false */ 245 } else if (nativeNames instanceof String) { 246 String name = (String)nativeNames; 247 /* Don't do do this for Latin fonts */ 248 if (name.indexOf("8859") > 0) { 249 checkedNatives = true; 250 return false; 251 } else if (NativeFont.hasExternalBitmaps(name)) { 252 nativeFonts = new NativeFont[1]; 253 try { 254 nativeFonts[0] = new NativeFont(name, true); 255 /* If reach here we have an non-latin font that has 256 * external bitmaps and we successfully created it. 257 */ 258 useNatives = true; 259 } catch (FontFormatException e) { 260 nativeFonts = null; 261 } 262 } 263 } else if (nativeNames instanceof String[]) { 264 String[] natNames = (String[])nativeNames; 265 int numNames = natNames.length; 266 boolean externalBitmaps = false; 267 for (int nn = 0; nn < numNames; nn++) { 268 if (natNames[nn].indexOf("8859") > 0) { 269 checkedNatives = true; 270 return false; 271 } else if (NativeFont.hasExternalBitmaps(natNames[nn])) { 272 externalBitmaps = true; 273 } 274 } 275 if (!externalBitmaps) { 276 checkedNatives = true; 277 return false; 278 } 279 useNatives = true; 280 nativeFonts = new NativeFont[numNames]; 281 for (int nn = 0; nn < numNames; nn++) { 282 try { 283 nativeFonts[nn] = new NativeFont(natNames[nn], true); 284 } catch (FontFormatException e) { 285 useNatives = false; 286 nativeFonts = null; 287 } 288 } 289 } 290 if (useNatives) { 291 glyphToCharMap = new char[getMapper().getNumGlyphs()]; 292 } 293 checkedNatives = true; 294 return useNatives; 295 } 296 297 open()298 private synchronized FileChannel open() throws FontFormatException { 299 return open(true); 300 } 301 302 /* This is intended to be called, and the returned value used, 303 * from within a block synchronized on this font object. 304 * ie the channel returned may be nulled out at any time by "close()" 305 * unless the caller holds a lock. 306 * Deadlock warning: FontManager.addToPool(..) acquires a global lock, 307 * which means nested locks may be in effect. 308 */ open(boolean usePool)309 private synchronized FileChannel open(boolean usePool) 310 throws FontFormatException { 311 if (disposerRecord.channel == null) { 312 if (FontUtilities.isLogging()) { 313 FontUtilities.getLogger().info("open TTF: " + platName); 314 } 315 try { 316 RandomAccessFile raf = (RandomAccessFile) 317 java.security.AccessController.doPrivileged( 318 new java.security.PrivilegedAction() { 319 public Object run() { 320 try { 321 return new RandomAccessFile(platName, "r"); 322 } catch (FileNotFoundException ffne) { 323 } 324 return null; 325 } 326 }); 327 disposerRecord.channel = raf.getChannel(); 328 fileSize = (int)disposerRecord.channel.size(); 329 if (usePool) { 330 FontManager fm = FontManagerFactory.getInstance(); 331 if (fm instanceof SunFontManager) { 332 ((SunFontManager) fm).addToPool(this); 333 } 334 } 335 } catch (NullPointerException e) { 336 close(); 337 throw new FontFormatException(e.toString()); 338 } catch (ClosedChannelException e) { 339 /* NIO I/O is interruptible, recurse to retry operation. 340 * The call to channel.size() above can throw this exception. 341 * Clear interrupts before recursing in case NIO didn't. 342 * Note that close() sets disposerRecord.channel to null. 343 */ 344 Thread.interrupted(); 345 close(); 346 open(); 347 } catch (IOException e) { 348 close(); 349 throw new FontFormatException(e.toString()); 350 } 351 } 352 return disposerRecord.channel; 353 } 354 close()355 protected synchronized void close() { 356 disposerRecord.dispose(); 357 } 358 359 readBlock(ByteBuffer buffer, int offset, int length)360 int readBlock(ByteBuffer buffer, int offset, int length) { 361 int bread = 0; 362 try { 363 synchronized (this) { 364 if (disposerRecord.channel == null) { 365 open(); 366 } 367 if (offset + length > fileSize) { 368 if (offset >= fileSize) { 369 /* Since the caller ensures that offset is < fileSize 370 * this condition suggests that fileSize is now 371 * different than the value we originally provided 372 * to native when the scaler was created. 373 * Also fileSize is updated every time we 374 * open() the file here, but in native the value 375 * isn't updated. If the file has changed whilst we 376 * are executing we want to bail, not spin. 377 */ 378 if (FontUtilities.isLogging()) { 379 String msg = "Read offset is " + offset + 380 " file size is " + fileSize+ 381 " file is " + platName; 382 FontUtilities.getLogger().severe(msg); 383 } 384 return -1; 385 } else { 386 length = fileSize - offset; 387 } 388 } 389 buffer.clear(); 390 disposerRecord.channel.position(offset); 391 while (bread < length) { 392 int cnt = disposerRecord.channel.read(buffer); 393 if (cnt == -1) { 394 String msg = "Unexpected EOF " + this; 395 int currSize = (int)disposerRecord.channel.size(); 396 if (currSize != fileSize) { 397 msg += " File size was " + fileSize + 398 " and now is " + currSize; 399 } 400 if (FontUtilities.isLogging()) { 401 FontUtilities.getLogger().severe(msg); 402 } 403 // We could still flip() the buffer here because 404 // it's possible that we did read some data in 405 // an earlier loop, and we probably should 406 // return that to the caller. Although if 407 // the caller expected 8K of data and we return 408 // only a few bytes then maybe it's better instead to 409 // set bread = -1 to indicate failure. 410 // The following is therefore using arbitrary values 411 // but is meant to allow cases where enough 412 // data was read to probably continue. 413 if (bread > length/2 || bread > 16384) { 414 buffer.flip(); 415 if (FontUtilities.isLogging()) { 416 msg = "Returning " + bread + 417 " bytes instead of " + length; 418 FontUtilities.getLogger().severe(msg); 419 } 420 } else { 421 bread = -1; 422 } 423 throw new IOException(msg); 424 } 425 bread += cnt; 426 } 427 buffer.flip(); 428 if (bread > length) { // possible if buffer.size() > length 429 bread = length; 430 } 431 } 432 } catch (FontFormatException e) { 433 if (FontUtilities.isLogging()) { 434 FontUtilities.getLogger().severe( 435 "While reading " + platName, e); 436 } 437 bread = -1; // signal EOF 438 deregisterFontAndClearStrikeCache(); 439 } catch (ClosedChannelException e) { 440 /* NIO I/O is interruptible, recurse to retry operation. 441 * Clear interrupts before recursing in case NIO didn't. 442 */ 443 Thread.interrupted(); 444 close(); 445 return readBlock(buffer, offset, length); 446 } catch (IOException e) { 447 /* If we did not read any bytes at all and the exception is 448 * not a recoverable one (ie is not ClosedChannelException) then 449 * we should indicate that there is no point in re-trying. 450 * Other than an attempt to read past the end of the file it 451 * seems unlikely this would occur as problems opening the 452 * file are handled as a FontFormatException. 453 */ 454 if (FontUtilities.isLogging()) { 455 FontUtilities.getLogger().severe( 456 "While reading " + platName, e); 457 } 458 if (bread == 0) { 459 bread = -1; // signal EOF 460 deregisterFontAndClearStrikeCache(); 461 } 462 } 463 return bread; 464 } 465 readBlock(int offset, int length)466 ByteBuffer readBlock(int offset, int length) { 467 468 ByteBuffer buffer = ByteBuffer.allocate(length); 469 try { 470 synchronized (this) { 471 if (disposerRecord.channel == null) { 472 open(); 473 } 474 if (offset + length > fileSize) { 475 if (offset > fileSize) { 476 return null; // assert? 477 } else { 478 buffer = ByteBuffer.allocate(fileSize-offset); 479 } 480 } 481 disposerRecord.channel.position(offset); 482 disposerRecord.channel.read(buffer); 483 buffer.flip(); 484 } 485 } catch (FontFormatException e) { 486 return null; 487 } catch (ClosedChannelException e) { 488 /* NIO I/O is interruptible, recurse to retry operation. 489 * Clear interrupts before recursing in case NIO didn't. 490 */ 491 Thread.interrupted(); 492 close(); 493 readBlock(buffer, offset, length); 494 } catch (IOException e) { 495 return null; 496 } 497 return buffer; 498 } 499 500 /* This is used by native code which can't allocate a direct byte 501 * buffer because of bug 4845371. It, and references to it in native 502 * code in scalerMethods.c can be removed once that bug is fixed. 503 * 4845371 is now fixed but we'll keep this around as it doesn't cost 504 * us anything if its never used/called. 505 */ readBytes(int offset, int length)506 byte[] readBytes(int offset, int length) { 507 ByteBuffer buffer = readBlock(offset, length); 508 if (buffer.hasArray()) { 509 return buffer.array(); 510 } else { 511 byte[] bufferBytes = new byte[buffer.limit()]; 512 buffer.get(bufferBytes); 513 return bufferBytes; 514 } 515 } 516 verify(boolean usePool)517 private void verify(boolean usePool) throws FontFormatException { 518 open(usePool); 519 } 520 521 /* sizes, in bytes, of TT/TTC header records */ 522 private static final int TTCHEADERSIZE = 12; 523 private static final int DIRECTORYHEADERSIZE = 12; 524 private static final int DIRECTORYENTRYSIZE = 16; 525 init(int fIndex)526 protected void init(int fIndex) throws FontFormatException { 527 int headerOffset = 0; 528 ByteBuffer buffer = readBlock(0, TTCHEADERSIZE); 529 try { 530 switch (buffer.getInt()) { 531 532 case ttcfTag: 533 buffer.getInt(); // skip TTC version ID 534 directoryCount = buffer.getInt(); 535 if (fIndex >= directoryCount) { 536 throw new FontFormatException("Bad collection index"); 537 } 538 fontIndex = fIndex; 539 buffer = readBlock(TTCHEADERSIZE+4*fIndex, 4); 540 headerOffset = buffer.getInt(); 541 fontDataSize = Math.max(0, fileSize - headerOffset); 542 break; 543 544 case v1ttTag: 545 case trueTag: 546 case ottoTag: 547 fontDataSize = fileSize; 548 break; 549 550 default: 551 throw new FontFormatException("Unsupported sfnt " + 552 getPublicFileName()); 553 } 554 555 /* Now have the offset of this TT font (possibly within a TTC) 556 * After the TT version/scaler type field, is the short 557 * representing the number of tables in the table directory. 558 * The table directory begins at 12 bytes after the header. 559 * Each table entry is 16 bytes long (4 32-bit ints) 560 */ 561 buffer = readBlock(headerOffset+4, 2); 562 numTables = buffer.getShort(); 563 directoryOffset = headerOffset+DIRECTORYHEADERSIZE; 564 ByteBuffer bbuffer = readBlock(directoryOffset, 565 numTables*DIRECTORYENTRYSIZE); 566 IntBuffer ibuffer = bbuffer.asIntBuffer(); 567 DirectoryEntry table; 568 tableDirectory = new DirectoryEntry[numTables]; 569 for (int i=0; i<numTables;i++) { 570 tableDirectory[i] = table = new DirectoryEntry(); 571 table.tag = ibuffer.get(); 572 /* checksum */ ibuffer.get(); 573 table.offset = ibuffer.get() & 0x7FFFFFFF; 574 table.length = ibuffer.get() & 0x7FFFFFFF; 575 if (table.offset + table.length > fileSize) { 576 throw new FontFormatException("bad table, tag="+table.tag); 577 } 578 } 579 580 if (getDirectoryEntry(headTag) == null) { 581 throw new FontFormatException("missing head table"); 582 } 583 if (getDirectoryEntry(maxpTag) == null) { 584 throw new FontFormatException("missing maxp table"); 585 } 586 if (getDirectoryEntry(hmtxTag) != null 587 && getDirectoryEntry(hheaTag) == null) { 588 throw new FontFormatException("missing hhea table"); 589 } 590 ByteBuffer maxpTable = getTableBuffer(maxpTag); 591 if (maxpTable.getChar(4) == 0) { 592 throw new FontFormatException("zero glyphs"); 593 } 594 initNames(); 595 } catch (Exception e) { 596 if (FontUtilities.isLogging()) { 597 FontUtilities.getLogger().severe(e.toString()); 598 } 599 if (e instanceof FontFormatException) { 600 throw (FontFormatException)e; 601 } else { 602 throw new FontFormatException(e.toString()); 603 } 604 } 605 if (familyName == null || fullName == null) { 606 throw new FontFormatException("Font name not found"); 607 } 608 /* The os2_Table is needed to gather some info, but we don't 609 * want to keep it around (as a field) so obtain it once and 610 * pass it to the code that needs it. 611 */ 612 ByteBuffer os2_Table = getTableBuffer(os_2Tag); 613 setStyle(os2_Table); 614 setCJKSupport(os2_Table); 615 } 616 617 /* The array index corresponds to a bit offset in the TrueType 618 * font's OS/2 compatibility table's code page ranges fields. 619 * These are two 32 bit unsigned int fields at offsets 78 and 82. 620 * We are only interested in determining if the font supports 621 * the windows encodings we expect as the default encoding in 622 * supported locales, so we only map the first of these fields. 623 */ 624 static final String encoding_mapping[] = { 625 "cp1252", /* 0:Latin 1 */ 626 "cp1250", /* 1:Latin 2 */ 627 "cp1251", /* 2:Cyrillic */ 628 "cp1253", /* 3:Greek */ 629 "cp1254", /* 4:Turkish/Latin 5 */ 630 "cp1255", /* 5:Hebrew */ 631 "cp1256", /* 6:Arabic */ 632 "cp1257", /* 7:Windows Baltic */ 633 "", /* 8:reserved for alternate ANSI */ 634 "", /* 9:reserved for alternate ANSI */ 635 "", /* 10:reserved for alternate ANSI */ 636 "", /* 11:reserved for alternate ANSI */ 637 "", /* 12:reserved for alternate ANSI */ 638 "", /* 13:reserved for alternate ANSI */ 639 "", /* 14:reserved for alternate ANSI */ 640 "", /* 15:reserved for alternate ANSI */ 641 "ms874", /* 16:Thai */ 642 "ms932", /* 17:JIS/Japanese */ 643 "gbk", /* 18:PRC GBK Cp950 */ 644 "ms949", /* 19:Korean Extended Wansung */ 645 "ms950", /* 20:Chinese (Taiwan, Hongkong, Macau) */ 646 "ms1361", /* 21:Korean Johab */ 647 "", /* 22 */ 648 "", /* 23 */ 649 "", /* 24 */ 650 "", /* 25 */ 651 "", /* 26 */ 652 "", /* 27 */ 653 "", /* 28 */ 654 "", /* 29 */ 655 "", /* 30 */ 656 "", /* 31 */ 657 }; 658 659 /* This maps two letter language codes to a Windows code page. 660 * Note that eg Cp1252 (the first subarray) is not exactly the same as 661 * Latin-1 since Windows code pages are do not necessarily correspond. 662 * There are two codepages for zh and ko so if a font supports 663 * only one of these ranges then we need to distinguish based on 664 * country. So far this only seems to matter for zh. 665 * REMIND: Unicode locales such as Hindi do not have a code page so 666 * this whole mechanism needs to be revised to map languages to 667 * the Unicode ranges either when this fails, or as an additional 668 * validating test. Basing it on Unicode ranges should get us away 669 * from needing to map to this small and incomplete set of Windows 670 * code pages which looks odd on non-Windows platforms. 671 */ 672 private static final String languages[][] = { 673 674 /* cp1252/Latin 1 */ 675 { "en", "ca", "da", "de", "es", "fi", "fr", "is", "it", 676 "nl", "no", "pt", "sq", "sv", }, 677 678 /* cp1250/Latin2 */ 679 { "cs", "cz", "et", "hr", "hu", "nr", "pl", "ro", "sk", 680 "sl", "sq", "sr", }, 681 682 /* cp1251/Cyrillic */ 683 { "bg", "mk", "ru", "sh", "uk" }, 684 685 /* cp1253/Greek*/ 686 { "el" }, 687 688 /* cp1254/Turkish,Latin 5 */ 689 { "tr" }, 690 691 /* cp1255/Hebrew */ 692 { "he" }, 693 694 /* cp1256/Arabic */ 695 { "ar" }, 696 697 /* cp1257/Windows Baltic */ 698 { "et", "lt", "lv" }, 699 700 /* ms874/Thai */ 701 { "th" }, 702 703 /* ms932/Japanese */ 704 { "ja" }, 705 706 /* gbk/Chinese (PRC GBK Cp950) */ 707 { "zh", "zh_CN", }, 708 709 /* ms949/Korean Extended Wansung */ 710 { "ko" }, 711 712 /* ms950/Chinese (Taiwan, Hongkong, Macau) */ 713 { "zh_HK", "zh_TW", }, 714 715 /* ms1361/Korean Johab */ 716 { "ko" }, 717 }; 718 719 private static final String codePages[] = { 720 "cp1252", 721 "cp1250", 722 "cp1251", 723 "cp1253", 724 "cp1254", 725 "cp1255", 726 "cp1256", 727 "cp1257", 728 "ms874", 729 "ms932", 730 "gbk", 731 "ms949", 732 "ms950", 733 "ms1361", 734 }; 735 736 private static String defaultCodePage = null; getCodePage()737 static String getCodePage() { 738 739 if (defaultCodePage != null) { 740 return defaultCodePage; 741 } 742 743 if (FontUtilities.isWindows) { 744 defaultCodePage = 745 (String)java.security.AccessController.doPrivileged( 746 new sun.security.action.GetPropertyAction("file.encoding")); 747 } else { 748 if (languages.length != codePages.length) { 749 throw new InternalError("wrong code pages array length"); 750 } 751 Locale locale = sun.awt.SunToolkit.getStartupLocale(); 752 753 String language = locale.getLanguage(); 754 if (language != null) { 755 if (language.equals("zh")) { 756 String country = locale.getCountry(); 757 if (country != null) { 758 language = language + "_" + country; 759 } 760 } 761 for (int i=0; i<languages.length;i++) { 762 for (int l=0;l<languages[i].length; l++) { 763 if (language.equals(languages[i][l])) { 764 defaultCodePage = codePages[i]; 765 return defaultCodePage; 766 } 767 } 768 } 769 } 770 } 771 if (defaultCodePage == null) { 772 defaultCodePage = ""; 773 } 774 return defaultCodePage; 775 } 776 777 /* Theoretically, reserved bits must not be set, include symbol bits */ 778 public static final int reserved_bits1 = 0x80000000; 779 public static final int reserved_bits2 = 0x0000ffff; 780 @Override supportsEncoding(String encoding)781 boolean supportsEncoding(String encoding) { 782 if (encoding == null) { 783 encoding = getCodePage(); 784 } 785 if ("".equals(encoding)) { 786 return false; 787 } 788 789 encoding = encoding.toLowerCase(); 790 791 /* java_props_md.c has a couple of special cases 792 * if language packs are installed. In these encodings the 793 * fontconfig files pick up different fonts : 794 * SimSun-18030 and MingLiU_HKSCS. Since these fonts will 795 * indicate they support the base encoding, we need to rewrite 796 * these encodings here before checking the map/array. 797 */ 798 if (encoding.equals("gb18030")) { 799 encoding = "gbk"; 800 } else if (encoding.equals("ms950_hkscs")) { 801 encoding = "ms950"; 802 } 803 804 ByteBuffer buffer = getTableBuffer(os_2Tag); 805 /* required info is at offsets 78 and 82 */ 806 if (buffer == null || buffer.capacity() < 86) { 807 return false; 808 } 809 810 int range1 = buffer.getInt(78); /* ulCodePageRange1 */ 811 int range2 = buffer.getInt(82); /* ulCodePageRange2 */ 812 813 /* This test is too stringent for Arial on Solaris (and perhaps 814 * other fonts). Arial has at least one reserved bit set for an 815 * unknown reason. 816 */ 817 // if (((range1 & reserved_bits1) | (range2 & reserved_bits2)) != 0) { 818 // return false; 819 // } 820 821 for (int em=0; em<encoding_mapping.length; em++) { 822 if (encoding_mapping[em].equals(encoding)) { 823 if (((1 << em) & range1) != 0) { 824 return true; 825 } 826 } 827 } 828 return false; 829 } 830 831 832 /* Use info in the os_2Table to test CJK support */ setCJKSupport(ByteBuffer os2Table)833 private void setCJKSupport(ByteBuffer os2Table) { 834 /* required info is in ulong at offset 46 */ 835 if (os2Table == null || os2Table.capacity() < 50) { 836 return; 837 } 838 int range2 = os2Table.getInt(46); /* ulUnicodeRange2 */ 839 840 /* Any of these bits set in the 32-63 range indicate a font with 841 * support for a CJK range. We aren't looking at some other bits 842 * in the 64-69 range such as half width forms as its unlikely a font 843 * would include those and none of these. 844 */ 845 supportsCJK = ((range2 & 0x29bf0000) != 0); 846 847 /* This should be generalised, but for now just need to know if 848 * Hiragana or Katakana ranges are supported by the font. 849 * In the 4 longs representing unicode ranges supported 850 * bits 49 & 50 indicate hiragana and katakana 851 * This is bits 17 & 18 in the 2nd ulong. If either is supported 852 * we presume this is a JA font. 853 */ 854 supportsJA = ((range2 & 0x60000) != 0); 855 } 856 supportsJA()857 boolean supportsJA() { 858 return supportsJA; 859 } 860 getTableBuffer(int tag)861 ByteBuffer getTableBuffer(int tag) { 862 DirectoryEntry entry = null; 863 864 for (int i=0;i<numTables;i++) { 865 if (tableDirectory[i].tag == tag) { 866 entry = tableDirectory[i]; 867 break; 868 } 869 } 870 if (entry == null || entry.length == 0 || 871 entry.offset+entry.length > fileSize) { 872 return null; 873 } 874 875 int bread = 0; 876 ByteBuffer buffer = ByteBuffer.allocate(entry.length); 877 synchronized (this) { 878 try { 879 if (disposerRecord.channel == null) { 880 open(); 881 } 882 disposerRecord.channel.position(entry.offset); 883 bread = disposerRecord.channel.read(buffer); 884 buffer.flip(); 885 } catch (ClosedChannelException e) { 886 /* NIO I/O is interruptible, recurse to retry operation. 887 * Clear interrupts before recursing in case NIO didn't. 888 */ 889 Thread.interrupted(); 890 close(); 891 return getTableBuffer(tag); 892 } catch (IOException e) { 893 return null; 894 } catch (FontFormatException e) { 895 return null; 896 } 897 898 if (bread < entry.length) { 899 return null; 900 } else { 901 return buffer; 902 } 903 } 904 } 905 906 @Override getLayoutTableCache()907 protected long getLayoutTableCache() { 908 try { 909 return getScaler().getLayoutTableCache(); 910 } catch(FontScalerException fe) { 911 return 0L; 912 } 913 } 914 915 @Override getTableBytes(int tag)916 protected byte[] getTableBytes(int tag) { 917 ByteBuffer buffer = getTableBuffer(tag); 918 if (buffer == null) { 919 return null; 920 } else if (buffer.hasArray()) { 921 try { 922 return buffer.array(); 923 } catch (Exception re) { 924 } 925 } 926 byte []data = new byte[getTableSize(tag)]; 927 buffer.get(data); 928 return data; 929 } 930 getTableSize(int tag)931 int getTableSize(int tag) { 932 for (int i=0;i<numTables;i++) { 933 if (tableDirectory[i].tag == tag) { 934 return tableDirectory[i].length; 935 } 936 } 937 return 0; 938 } 939 getTableOffset(int tag)940 int getTableOffset(int tag) { 941 for (int i=0;i<numTables;i++) { 942 if (tableDirectory[i].tag == tag) { 943 return tableDirectory[i].offset; 944 } 945 } 946 return 0; 947 } 948 getDirectoryEntry(int tag)949 DirectoryEntry getDirectoryEntry(int tag) { 950 for (int i=0;i<numTables;i++) { 951 if (tableDirectory[i].tag == tag) { 952 return tableDirectory[i]; 953 } 954 } 955 return null; 956 } 957 958 /* Used to determine if this size has embedded bitmaps, which 959 * for CJK fonts should be used in preference to LCD glyphs. 960 */ useEmbeddedBitmapsForSize(int ptSize)961 boolean useEmbeddedBitmapsForSize(int ptSize) { 962 if (!supportsCJK) { 963 return false; 964 } 965 if (getDirectoryEntry(EBLCTag) == null) { 966 return false; 967 } 968 ByteBuffer eblcTable = getTableBuffer(EBLCTag); 969 int numSizes = eblcTable.getInt(4); 970 /* The bitmapSizeTable's start at offset of 8. 971 * Each bitmapSizeTable entry is 48 bytes. 972 * The offset of ppemY in the entry is 45. 973 */ 974 for (int i=0;i<numSizes;i++) { 975 int ppemY = eblcTable.get(8+(i*48)+45) &0xff; 976 if (ppemY == ptSize) { 977 return true; 978 } 979 } 980 return false; 981 } 982 getFullName()983 public String getFullName() { 984 return fullName; 985 } 986 987 /* This probably won't get called but is there to support the 988 * contract() of setStyle() defined in the superclass. 989 */ 990 @Override setStyle()991 protected void setStyle() { 992 setStyle(getTableBuffer(os_2Tag)); 993 } 994 995 private int fontWidth = 0; 996 @Override getWidth()997 public int getWidth() { 998 return (fontWidth > 0) ? fontWidth : super.getWidth(); 999 } 1000 1001 private int fontWeight = 0; 1002 @Override getWeight()1003 public int getWeight() { 1004 return (fontWeight > 0) ? fontWeight : super.getWeight(); 1005 } 1006 1007 /* TrueTypeFont can use the fsSelection fields of OS/2 table 1008 * to determine the style. In the unlikely case that doesn't exist, 1009 * can use macStyle in the 'head' table but simpler to 1010 * fall back to super class algorithm of looking for well known string. 1011 * A very few fonts don't specify this information, but I only 1012 * came across one: Lucida Sans Thai Typewriter Oblique in 1013 * /usr/openwin/lib/locale/th_TH/X11/fonts/TrueType/lucidai.ttf 1014 * that explicitly specified the wrong value. It says its regular. 1015 * I didn't find any fonts that were inconsistent (ie regular plus some 1016 * other value). 1017 */ 1018 private static final int fsSelectionItalicBit = 0x00001; 1019 private static final int fsSelectionBoldBit = 0x00020; 1020 private static final int fsSelectionRegularBit = 0x00040; setStyle(ByteBuffer os_2Table)1021 private void setStyle(ByteBuffer os_2Table) { 1022 if (os_2Table == null) { 1023 return; 1024 } 1025 if (os_2Table.capacity() >= 8) { 1026 fontWeight = os_2Table.getChar(4) & 0xffff; 1027 fontWidth = os_2Table.getChar(6) & 0xffff; 1028 } 1029 /* fsSelection is unsigned short at buffer offset 62 */ 1030 if (os_2Table.capacity() < 64) { 1031 super.setStyle(); 1032 return; 1033 } 1034 int fsSelection = os_2Table.getChar(62) & 0xffff; 1035 int italic = fsSelection & fsSelectionItalicBit; 1036 int bold = fsSelection & fsSelectionBoldBit; 1037 int regular = fsSelection & fsSelectionRegularBit; 1038 // System.out.println("platname="+platName+" font="+fullName+ 1039 // " family="+familyName+ 1040 // " R="+regular+" I="+italic+" B="+bold); 1041 if (regular!=0 && ((italic|bold)!=0)) { 1042 /* This is inconsistent. Try using the font name algorithm */ 1043 super.setStyle(); 1044 return; 1045 } else if ((regular|italic|bold) == 0) { 1046 /* No style specified. Try using the font name algorithm */ 1047 super.setStyle(); 1048 return; 1049 } 1050 switch (bold|italic) { 1051 case fsSelectionItalicBit: 1052 style = Font.ITALIC; 1053 break; 1054 case fsSelectionBoldBit: 1055 if (FontUtilities.isSolaris && platName.endsWith("HG-GothicB.ttf")) { 1056 /* Workaround for Solaris's use of a JA font that's marked as 1057 * being designed bold, but is used as a PLAIN font. 1058 */ 1059 style = Font.PLAIN; 1060 } else { 1061 style = Font.BOLD; 1062 } 1063 break; 1064 case fsSelectionBoldBit|fsSelectionItalicBit: 1065 style = Font.BOLD|Font.ITALIC; 1066 } 1067 } 1068 1069 private float stSize, stPos, ulSize, ulPos; 1070 setStrikethroughMetrics(ByteBuffer os_2Table, int upem)1071 private void setStrikethroughMetrics(ByteBuffer os_2Table, int upem) { 1072 if (os_2Table == null || os_2Table.capacity() < 30 || upem < 0) { 1073 stSize = 0.05f; 1074 stPos = -0.4f; 1075 return; 1076 } 1077 ShortBuffer sb = os_2Table.asShortBuffer(); 1078 stSize = sb.get(13) / (float)upem; 1079 stPos = -sb.get(14) / (float)upem; 1080 if (stSize < 0f) { 1081 stSize = 0.05f; 1082 } 1083 if (Math.abs(stPos) > 2.0f) { 1084 stPos = -0.4f; 1085 } 1086 } 1087 setUnderlineMetrics(ByteBuffer postTable, int upem)1088 private void setUnderlineMetrics(ByteBuffer postTable, int upem) { 1089 if (postTable == null || postTable.capacity() < 12 || upem < 0) { 1090 ulSize = 0.05f; 1091 ulPos = 0.1f; 1092 return; 1093 } 1094 ShortBuffer sb = postTable.asShortBuffer(); 1095 ulSize = sb.get(5) / (float)upem; 1096 ulPos = -sb.get(4) / (float)upem; 1097 if (ulSize < 0f) { 1098 ulSize = 0.05f; 1099 } 1100 if (Math.abs(ulPos) > 2.0f) { 1101 ulPos = 0.1f; 1102 } 1103 } 1104 1105 @Override getStyleMetrics(float pointSize, float[] metrics, int offset)1106 public void getStyleMetrics(float pointSize, float[] metrics, int offset) { 1107 1108 if (ulSize == 0f && ulPos == 0f) { 1109 1110 ByteBuffer head_Table = getTableBuffer(headTag); 1111 int upem = -1; 1112 if (head_Table != null && head_Table.capacity() >= 18) { 1113 ShortBuffer sb = head_Table.asShortBuffer(); 1114 upem = sb.get(9) & 0xffff; 1115 if (upem < 16 || upem > 16384) { 1116 upem = 2048; 1117 } 1118 } 1119 1120 ByteBuffer os2_Table = getTableBuffer(os_2Tag); 1121 setStrikethroughMetrics(os2_Table, upem); 1122 1123 ByteBuffer post_Table = getTableBuffer(postTag); 1124 setUnderlineMetrics(post_Table, upem); 1125 } 1126 1127 metrics[offset] = stPos * pointSize; 1128 metrics[offset+1] = stSize * pointSize; 1129 1130 metrics[offset+2] = ulPos * pointSize; 1131 metrics[offset+3] = ulSize * pointSize; 1132 } 1133 makeString(byte[] bytes, int len, short encoding)1134 private String makeString(byte[] bytes, int len, short encoding) { 1135 1136 /* Check for fonts using encodings 2->6 is just for 1137 * some old DBCS fonts, apparently mostly on Solaris. 1138 * Some of these fonts encode ascii names as double-byte characters. 1139 * ie with a leading zero byte for what properly should be a 1140 * single byte-char. 1141 */ 1142 if (encoding >=2 && encoding <= 6) { 1143 byte[] oldbytes = bytes; 1144 int oldlen = len; 1145 bytes = new byte[oldlen]; 1146 len = 0; 1147 for (int i=0; i<oldlen; i++) { 1148 if (oldbytes[i] != 0) { 1149 bytes[len++] = oldbytes[i]; 1150 } 1151 } 1152 } 1153 1154 String charset; 1155 switch (encoding) { 1156 case 1: charset = "UTF-16"; break; // most common case first. 1157 case 0: charset = "UTF-16"; break; // symbol uses this 1158 case 2: charset = "SJIS"; break; 1159 case 3: charset = "GBK"; break; 1160 case 4: charset = "MS950"; break; 1161 case 5: charset = "EUC_KR"; break; 1162 case 6: charset = "Johab"; break; 1163 default: charset = "UTF-16"; break; 1164 } 1165 1166 try { 1167 return new String(bytes, 0, len, charset); 1168 } catch (UnsupportedEncodingException e) { 1169 if (FontUtilities.isLogging()) { 1170 FontUtilities.getLogger().warning(e + " EncodingID=" + encoding); 1171 } 1172 return new String(bytes, 0, len); 1173 } catch (Throwable t) { 1174 return null; 1175 } 1176 } 1177 initNames()1178 protected void initNames() { 1179 1180 byte[] name = new byte[256]; 1181 ByteBuffer buffer = getTableBuffer(nameTag); 1182 1183 if (buffer != null) { 1184 ShortBuffer sbuffer = buffer.asShortBuffer(); 1185 sbuffer.get(); // format - not needed. 1186 short numRecords = sbuffer.get(); 1187 /* The name table uses unsigned shorts. Many of these 1188 * are known small values that fit in a short. 1189 * The values that are sizes or offsets into the table could be 1190 * greater than 32767, so read and store those as ints 1191 */ 1192 int stringPtr = sbuffer.get() & 0xffff; 1193 1194 nameLocale = sun.awt.SunToolkit.getStartupLocale(); 1195 short nameLocaleID = getLCIDFromLocale(nameLocale); 1196 1197 for (int i=0; i<numRecords; i++) { 1198 short platformID = sbuffer.get(); 1199 if (platformID != MS_PLATFORM_ID) { 1200 sbuffer.position(sbuffer.position()+5); 1201 continue; // skip over this record. 1202 } 1203 short encodingID = sbuffer.get(); 1204 short langID = sbuffer.get(); 1205 short nameID = sbuffer.get(); 1206 int nameLen = ((int) sbuffer.get()) & 0xffff; 1207 int namePtr = (((int) sbuffer.get()) & 0xffff) + stringPtr; 1208 String tmpName = null; 1209 switch (nameID) { 1210 1211 case FAMILY_NAME_ID: 1212 1213 if (familyName == null || langID == ENGLISH_LOCALE_ID || 1214 langID == nameLocaleID) 1215 { 1216 buffer.position(namePtr); 1217 buffer.get(name, 0, nameLen); 1218 tmpName = makeString(name, nameLen, encodingID); 1219 1220 if (familyName == null || langID == ENGLISH_LOCALE_ID){ 1221 familyName = tmpName; 1222 } 1223 if (langID == nameLocaleID) { 1224 localeFamilyName = tmpName; 1225 } 1226 } 1227 /* 1228 for (int ii=0;ii<nameLen;ii++) { 1229 int val = (int)name[ii]&0xff; 1230 System.err.print(Integer.toHexString(val)+ " "); 1231 } 1232 System.err.println(); 1233 System.err.println("familyName="+familyName + 1234 " nameLen="+nameLen+ 1235 " langID="+langID+ " eid="+encodingID + 1236 " str len="+familyName.length()); 1237 1238 */ 1239 break; 1240 1241 case FULL_NAME_ID: 1242 1243 if (fullName == null || langID == ENGLISH_LOCALE_ID || 1244 langID == nameLocaleID) 1245 { 1246 buffer.position(namePtr); 1247 buffer.get(name, 0, nameLen); 1248 tmpName = makeString(name, nameLen, encodingID); 1249 1250 if (fullName == null || langID == ENGLISH_LOCALE_ID) { 1251 fullName = tmpName; 1252 } 1253 if (langID == nameLocaleID) { 1254 localeFullName = tmpName; 1255 } 1256 } 1257 break; 1258 } 1259 } 1260 if (localeFamilyName == null) { 1261 localeFamilyName = familyName; 1262 } 1263 if (localeFullName == null) { 1264 localeFullName = fullName; 1265 } 1266 } 1267 } 1268 1269 /* Return the requested name in the requested locale, for the 1270 * MS platform ID. If the requested locale isn't found, return US 1271 * English, if that isn't found, return null and let the caller 1272 * figure out how to handle that. 1273 */ lookupName(short findLocaleID, int findNameID)1274 protected String lookupName(short findLocaleID, int findNameID) { 1275 String foundName = null; 1276 byte[] name = new byte[1024]; 1277 1278 ByteBuffer buffer = getTableBuffer(nameTag); 1279 if (buffer != null) { 1280 ShortBuffer sbuffer = buffer.asShortBuffer(); 1281 sbuffer.get(); // format - not needed. 1282 short numRecords = sbuffer.get(); 1283 1284 /* The name table uses unsigned shorts. Many of these 1285 * are known small values that fit in a short. 1286 * The values that are sizes or offsets into the table could be 1287 * greater than 32767, so read and store those as ints 1288 */ 1289 int stringPtr = ((int) sbuffer.get()) & 0xffff; 1290 1291 for (int i=0; i<numRecords; i++) { 1292 short platformID = sbuffer.get(); 1293 if (platformID != MS_PLATFORM_ID) { 1294 sbuffer.position(sbuffer.position()+5); 1295 continue; // skip over this record. 1296 } 1297 short encodingID = sbuffer.get(); 1298 short langID = sbuffer.get(); 1299 short nameID = sbuffer.get(); 1300 int nameLen = ((int) sbuffer.get()) & 0xffff; 1301 int namePtr = (((int) sbuffer.get()) & 0xffff) + stringPtr; 1302 if (nameID == findNameID && 1303 ((foundName == null && langID == ENGLISH_LOCALE_ID) 1304 || langID == findLocaleID)) { 1305 buffer.position(namePtr); 1306 buffer.get(name, 0, nameLen); 1307 foundName = makeString(name, nameLen, encodingID); 1308 if (langID == findLocaleID) { 1309 return foundName; 1310 } 1311 } 1312 } 1313 } 1314 return foundName; 1315 } 1316 1317 /** 1318 * @return number of logical fonts. Is "1" for all but TTC files 1319 */ getFontCount()1320 public int getFontCount() { 1321 return directoryCount; 1322 } 1323 getScaler()1324 protected synchronized FontScaler getScaler() { 1325 if (scaler == null) { 1326 scaler = FontScaler.getScaler(this, fontIndex, 1327 supportsCJK, fileSize); 1328 } 1329 return scaler; 1330 } 1331 1332 1333 /* Postscript name is rarely requested. Don't waste cycles locating it 1334 * as part of font creation, nor storage to hold it. Get it only on demand. 1335 */ 1336 @Override getPostscriptName()1337 public String getPostscriptName() { 1338 String name = lookupName(ENGLISH_LOCALE_ID, POSTSCRIPT_NAME_ID); 1339 if (name == null) { 1340 return fullName; 1341 } else { 1342 return name; 1343 } 1344 } 1345 1346 @Override getFontName(Locale locale)1347 public String getFontName(Locale locale) { 1348 if (locale == null) { 1349 return fullName; 1350 } else if (locale.equals(nameLocale) && localeFullName != null) { 1351 return localeFullName; 1352 } else { 1353 short localeID = getLCIDFromLocale(locale); 1354 String name = lookupName(localeID, FULL_NAME_ID); 1355 if (name == null) { 1356 return fullName; 1357 } else { 1358 return name; 1359 } 1360 } 1361 } 1362 1363 // Return a Microsoft LCID from the given Locale. 1364 // Used when getting localized font data. 1365 addLCIDMapEntry(Map<String, Short> map, String key, short value)1366 private static void addLCIDMapEntry(Map<String, Short> map, 1367 String key, short value) { 1368 map.put(key, Short.valueOf(value)); 1369 } 1370 createLCIDMap()1371 private static synchronized void createLCIDMap() { 1372 if (lcidMap != null) { 1373 return; 1374 } 1375 1376 Map<String, Short> map = new HashMap<String, Short>(200); 1377 1378 // the following statements are derived from the langIDMap 1379 // in src/windows/native/java/lang/java_props_md.c using the following 1380 // awk script: 1381 // $1~/\/\*/ { next} 1382 // $3~/\?\?/ { next } 1383 // $3!~/_/ { next } 1384 // $1~/0x0409/ { next } 1385 // $1~/0x0c0a/ { next } 1386 // $1~/0x042c/ { next } 1387 // $1~/0x0443/ { next } 1388 // $1~/0x0812/ { next } 1389 // $1~/0x04/ { print " addLCIDMapEntry(map, " substr($3, 0, 3) "\", (short) " substr($1, 0, 6) ");" ; next } 1390 // $3~/,/ { print " addLCIDMapEntry(map, " $3 " (short) " substr($1, 0, 6) ");" ; next } 1391 // { print " addLCIDMapEntry(map, " $3 ", (short) " substr($1, 0, 6) ");" ; next } 1392 // The lines of this script: 1393 // - eliminate comments 1394 // - eliminate questionable locales 1395 // - eliminate language-only locales 1396 // - eliminate the default LCID value 1397 // - eliminate a few other unneeded LCID values 1398 // - print language-only locale entries for x04* LCID values 1399 // (apparently Microsoft doesn't use language-only LCID values - 1400 // see http://www.microsoft.com/OpenType/otspec/name.htm 1401 // - print complete entries for all other LCID values 1402 // Run 1403 // awk -f awk-script langIDMap > statements 1404 addLCIDMapEntry(map, "ar", (short) 0x0401); 1405 addLCIDMapEntry(map, "bg", (short) 0x0402); 1406 addLCIDMapEntry(map, "ca", (short) 0x0403); 1407 addLCIDMapEntry(map, "zh", (short) 0x0404); 1408 addLCIDMapEntry(map, "cs", (short) 0x0405); 1409 addLCIDMapEntry(map, "da", (short) 0x0406); 1410 addLCIDMapEntry(map, "de", (short) 0x0407); 1411 addLCIDMapEntry(map, "el", (short) 0x0408); 1412 addLCIDMapEntry(map, "es", (short) 0x040a); 1413 addLCIDMapEntry(map, "fi", (short) 0x040b); 1414 addLCIDMapEntry(map, "fr", (short) 0x040c); 1415 addLCIDMapEntry(map, "iw", (short) 0x040d); 1416 addLCIDMapEntry(map, "hu", (short) 0x040e); 1417 addLCIDMapEntry(map, "is", (short) 0x040f); 1418 addLCIDMapEntry(map, "it", (short) 0x0410); 1419 addLCIDMapEntry(map, "ja", (short) 0x0411); 1420 addLCIDMapEntry(map, "ko", (short) 0x0412); 1421 addLCIDMapEntry(map, "nl", (short) 0x0413); 1422 addLCIDMapEntry(map, "no", (short) 0x0414); 1423 addLCIDMapEntry(map, "pl", (short) 0x0415); 1424 addLCIDMapEntry(map, "pt", (short) 0x0416); 1425 addLCIDMapEntry(map, "rm", (short) 0x0417); 1426 addLCIDMapEntry(map, "ro", (short) 0x0418); 1427 addLCIDMapEntry(map, "ru", (short) 0x0419); 1428 addLCIDMapEntry(map, "hr", (short) 0x041a); 1429 addLCIDMapEntry(map, "sk", (short) 0x041b); 1430 addLCIDMapEntry(map, "sq", (short) 0x041c); 1431 addLCIDMapEntry(map, "sv", (short) 0x041d); 1432 addLCIDMapEntry(map, "th", (short) 0x041e); 1433 addLCIDMapEntry(map, "tr", (short) 0x041f); 1434 addLCIDMapEntry(map, "ur", (short) 0x0420); 1435 addLCIDMapEntry(map, "in", (short) 0x0421); 1436 addLCIDMapEntry(map, "uk", (short) 0x0422); 1437 addLCIDMapEntry(map, "be", (short) 0x0423); 1438 addLCIDMapEntry(map, "sl", (short) 0x0424); 1439 addLCIDMapEntry(map, "et", (short) 0x0425); 1440 addLCIDMapEntry(map, "lv", (short) 0x0426); 1441 addLCIDMapEntry(map, "lt", (short) 0x0427); 1442 addLCIDMapEntry(map, "fa", (short) 0x0429); 1443 addLCIDMapEntry(map, "vi", (short) 0x042a); 1444 addLCIDMapEntry(map, "hy", (short) 0x042b); 1445 addLCIDMapEntry(map, "eu", (short) 0x042d); 1446 addLCIDMapEntry(map, "mk", (short) 0x042f); 1447 addLCIDMapEntry(map, "tn", (short) 0x0432); 1448 addLCIDMapEntry(map, "xh", (short) 0x0434); 1449 addLCIDMapEntry(map, "zu", (short) 0x0435); 1450 addLCIDMapEntry(map, "af", (short) 0x0436); 1451 addLCIDMapEntry(map, "ka", (short) 0x0437); 1452 addLCIDMapEntry(map, "fo", (short) 0x0438); 1453 addLCIDMapEntry(map, "hi", (short) 0x0439); 1454 addLCIDMapEntry(map, "mt", (short) 0x043a); 1455 addLCIDMapEntry(map, "se", (short) 0x043b); 1456 addLCIDMapEntry(map, "gd", (short) 0x043c); 1457 addLCIDMapEntry(map, "ms", (short) 0x043e); 1458 addLCIDMapEntry(map, "kk", (short) 0x043f); 1459 addLCIDMapEntry(map, "ky", (short) 0x0440); 1460 addLCIDMapEntry(map, "sw", (short) 0x0441); 1461 addLCIDMapEntry(map, "tt", (short) 0x0444); 1462 addLCIDMapEntry(map, "bn", (short) 0x0445); 1463 addLCIDMapEntry(map, "pa", (short) 0x0446); 1464 addLCIDMapEntry(map, "gu", (short) 0x0447); 1465 addLCIDMapEntry(map, "ta", (short) 0x0449); 1466 addLCIDMapEntry(map, "te", (short) 0x044a); 1467 addLCIDMapEntry(map, "kn", (short) 0x044b); 1468 addLCIDMapEntry(map, "ml", (short) 0x044c); 1469 addLCIDMapEntry(map, "mr", (short) 0x044e); 1470 addLCIDMapEntry(map, "sa", (short) 0x044f); 1471 addLCIDMapEntry(map, "mn", (short) 0x0450); 1472 addLCIDMapEntry(map, "cy", (short) 0x0452); 1473 addLCIDMapEntry(map, "gl", (short) 0x0456); 1474 addLCIDMapEntry(map, "dv", (short) 0x0465); 1475 addLCIDMapEntry(map, "qu", (short) 0x046b); 1476 addLCIDMapEntry(map, "mi", (short) 0x0481); 1477 addLCIDMapEntry(map, "ar_IQ", (short) 0x0801); 1478 addLCIDMapEntry(map, "zh_CN", (short) 0x0804); 1479 addLCIDMapEntry(map, "de_CH", (short) 0x0807); 1480 addLCIDMapEntry(map, "en_GB", (short) 0x0809); 1481 addLCIDMapEntry(map, "es_MX", (short) 0x080a); 1482 addLCIDMapEntry(map, "fr_BE", (short) 0x080c); 1483 addLCIDMapEntry(map, "it_CH", (short) 0x0810); 1484 addLCIDMapEntry(map, "nl_BE", (short) 0x0813); 1485 addLCIDMapEntry(map, "no_NO_NY", (short) 0x0814); 1486 addLCIDMapEntry(map, "pt_PT", (short) 0x0816); 1487 addLCIDMapEntry(map, "ro_MD", (short) 0x0818); 1488 addLCIDMapEntry(map, "ru_MD", (short) 0x0819); 1489 addLCIDMapEntry(map, "sr_CS", (short) 0x081a); 1490 addLCIDMapEntry(map, "sv_FI", (short) 0x081d); 1491 addLCIDMapEntry(map, "az_AZ", (short) 0x082c); 1492 addLCIDMapEntry(map, "se_SE", (short) 0x083b); 1493 addLCIDMapEntry(map, "ga_IE", (short) 0x083c); 1494 addLCIDMapEntry(map, "ms_BN", (short) 0x083e); 1495 addLCIDMapEntry(map, "uz_UZ", (short) 0x0843); 1496 addLCIDMapEntry(map, "qu_EC", (short) 0x086b); 1497 addLCIDMapEntry(map, "ar_EG", (short) 0x0c01); 1498 addLCIDMapEntry(map, "zh_HK", (short) 0x0c04); 1499 addLCIDMapEntry(map, "de_AT", (short) 0x0c07); 1500 addLCIDMapEntry(map, "en_AU", (short) 0x0c09); 1501 addLCIDMapEntry(map, "fr_CA", (short) 0x0c0c); 1502 addLCIDMapEntry(map, "sr_CS", (short) 0x0c1a); 1503 addLCIDMapEntry(map, "se_FI", (short) 0x0c3b); 1504 addLCIDMapEntry(map, "qu_PE", (short) 0x0c6b); 1505 addLCIDMapEntry(map, "ar_LY", (short) 0x1001); 1506 addLCIDMapEntry(map, "zh_SG", (short) 0x1004); 1507 addLCIDMapEntry(map, "de_LU", (short) 0x1007); 1508 addLCIDMapEntry(map, "en_CA", (short) 0x1009); 1509 addLCIDMapEntry(map, "es_GT", (short) 0x100a); 1510 addLCIDMapEntry(map, "fr_CH", (short) 0x100c); 1511 addLCIDMapEntry(map, "hr_BA", (short) 0x101a); 1512 addLCIDMapEntry(map, "ar_DZ", (short) 0x1401); 1513 addLCIDMapEntry(map, "zh_MO", (short) 0x1404); 1514 addLCIDMapEntry(map, "de_LI", (short) 0x1407); 1515 addLCIDMapEntry(map, "en_NZ", (short) 0x1409); 1516 addLCIDMapEntry(map, "es_CR", (short) 0x140a); 1517 addLCIDMapEntry(map, "fr_LU", (short) 0x140c); 1518 addLCIDMapEntry(map, "bs_BA", (short) 0x141a); 1519 addLCIDMapEntry(map, "ar_MA", (short) 0x1801); 1520 addLCIDMapEntry(map, "en_IE", (short) 0x1809); 1521 addLCIDMapEntry(map, "es_PA", (short) 0x180a); 1522 addLCIDMapEntry(map, "fr_MC", (short) 0x180c); 1523 addLCIDMapEntry(map, "sr_BA", (short) 0x181a); 1524 addLCIDMapEntry(map, "ar_TN", (short) 0x1c01); 1525 addLCIDMapEntry(map, "en_ZA", (short) 0x1c09); 1526 addLCIDMapEntry(map, "es_DO", (short) 0x1c0a); 1527 addLCIDMapEntry(map, "sr_BA", (short) 0x1c1a); 1528 addLCIDMapEntry(map, "ar_OM", (short) 0x2001); 1529 addLCIDMapEntry(map, "en_JM", (short) 0x2009); 1530 addLCIDMapEntry(map, "es_VE", (short) 0x200a); 1531 addLCIDMapEntry(map, "ar_YE", (short) 0x2401); 1532 addLCIDMapEntry(map, "es_CO", (short) 0x240a); 1533 addLCIDMapEntry(map, "ar_SY", (short) 0x2801); 1534 addLCIDMapEntry(map, "en_BZ", (short) 0x2809); 1535 addLCIDMapEntry(map, "es_PE", (short) 0x280a); 1536 addLCIDMapEntry(map, "ar_JO", (short) 0x2c01); 1537 addLCIDMapEntry(map, "en_TT", (short) 0x2c09); 1538 addLCIDMapEntry(map, "es_AR", (short) 0x2c0a); 1539 addLCIDMapEntry(map, "ar_LB", (short) 0x3001); 1540 addLCIDMapEntry(map, "en_ZW", (short) 0x3009); 1541 addLCIDMapEntry(map, "es_EC", (short) 0x300a); 1542 addLCIDMapEntry(map, "ar_KW", (short) 0x3401); 1543 addLCIDMapEntry(map, "en_PH", (short) 0x3409); 1544 addLCIDMapEntry(map, "es_CL", (short) 0x340a); 1545 addLCIDMapEntry(map, "ar_AE", (short) 0x3801); 1546 addLCIDMapEntry(map, "es_UY", (short) 0x380a); 1547 addLCIDMapEntry(map, "ar_BH", (short) 0x3c01); 1548 addLCIDMapEntry(map, "es_PY", (short) 0x3c0a); 1549 addLCIDMapEntry(map, "ar_QA", (short) 0x4001); 1550 addLCIDMapEntry(map, "es_BO", (short) 0x400a); 1551 addLCIDMapEntry(map, "es_SV", (short) 0x440a); 1552 addLCIDMapEntry(map, "es_HN", (short) 0x480a); 1553 addLCIDMapEntry(map, "es_NI", (short) 0x4c0a); 1554 addLCIDMapEntry(map, "es_PR", (short) 0x500a); 1555 1556 lcidMap = map; 1557 } 1558 getLCIDFromLocale(Locale locale)1559 private static short getLCIDFromLocale(Locale locale) { 1560 // optimize for common case 1561 if (locale.equals(Locale.US)) { 1562 return US_LCID; 1563 } 1564 1565 if (lcidMap == null) { 1566 createLCIDMap(); 1567 } 1568 1569 String key = locale.toString(); 1570 while (!"".equals(key)) { 1571 Short lcidObject = (Short) lcidMap.get(key); 1572 if (lcidObject != null) { 1573 return lcidObject.shortValue(); 1574 } 1575 int pos = key.lastIndexOf('_'); 1576 if (pos < 1) { 1577 return US_LCID; 1578 } 1579 key = key.substring(0, pos); 1580 } 1581 1582 return US_LCID; 1583 } 1584 1585 @Override getFamilyName(Locale locale)1586 public String getFamilyName(Locale locale) { 1587 if (locale == null) { 1588 return familyName; 1589 } else if (locale.equals(nameLocale) && localeFamilyName != null) { 1590 return localeFamilyName; 1591 } else { 1592 short localeID = getLCIDFromLocale(locale); 1593 String name = lookupName(localeID, FAMILY_NAME_ID); 1594 if (name == null) { 1595 return familyName; 1596 } else { 1597 return name; 1598 } 1599 } 1600 } 1601 getMapper()1602 public CharToGlyphMapper getMapper() { 1603 if (mapper == null) { 1604 mapper = new TrueTypeGlyphMapper(this); 1605 } 1606 return mapper; 1607 } 1608 1609 /* This duplicates initNames() but that has to run fast as its used 1610 * during typical start-up and the information here is likely never 1611 * needed. 1612 */ initAllNames(int requestedID, HashSet names)1613 protected void initAllNames(int requestedID, HashSet names) { 1614 1615 byte[] name = new byte[256]; 1616 ByteBuffer buffer = getTableBuffer(nameTag); 1617 1618 if (buffer != null) { 1619 ShortBuffer sbuffer = buffer.asShortBuffer(); 1620 sbuffer.get(); // format - not needed. 1621 short numRecords = sbuffer.get(); 1622 1623 /* The name table uses unsigned shorts. Many of these 1624 * are known small values that fit in a short. 1625 * The values that are sizes or offsets into the table could be 1626 * greater than 32767, so read and store those as ints 1627 */ 1628 int stringPtr = ((int) sbuffer.get()) & 0xffff; 1629 for (int i=0; i<numRecords; i++) { 1630 short platformID = sbuffer.get(); 1631 if (platformID != MS_PLATFORM_ID) { 1632 sbuffer.position(sbuffer.position()+5); 1633 continue; // skip over this record. 1634 } 1635 short encodingID = sbuffer.get(); 1636 short langID = sbuffer.get(); 1637 short nameID = sbuffer.get(); 1638 int nameLen = ((int) sbuffer.get()) & 0xffff; 1639 int namePtr = (((int) sbuffer.get()) & 0xffff) + stringPtr; 1640 1641 if (nameID == requestedID) { 1642 buffer.position(namePtr); 1643 buffer.get(name, 0, nameLen); 1644 names.add(makeString(name, nameLen, encodingID)); 1645 } 1646 } 1647 } 1648 } 1649 getAllFamilyNames()1650 String[] getAllFamilyNames() { 1651 HashSet aSet = new HashSet(); 1652 try { 1653 initAllNames(FAMILY_NAME_ID, aSet); 1654 } catch (Exception e) { 1655 /* In case of malformed font */ 1656 } 1657 return (String[])aSet.toArray(new String[0]); 1658 } 1659 getAllFullNames()1660 String[] getAllFullNames() { 1661 HashSet aSet = new HashSet(); 1662 try { 1663 initAllNames(FULL_NAME_ID, aSet); 1664 } catch (Exception e) { 1665 /* In case of malformed font */ 1666 } 1667 return (String[])aSet.toArray(new String[0]); 1668 } 1669 1670 /* Used by the OpenType engine for mark positioning. 1671 */ 1672 @Override getGlyphPoint(long pScalerContext, int glyphCode, int ptNumber)1673 Point2D.Float getGlyphPoint(long pScalerContext, 1674 int glyphCode, int ptNumber) { 1675 try { 1676 return getScaler().getGlyphPoint(pScalerContext, 1677 glyphCode, ptNumber); 1678 } catch(FontScalerException fe) { 1679 return null; 1680 } 1681 } 1682 1683 private char[] gaspTable; 1684 getGaspTable()1685 private char[] getGaspTable() { 1686 1687 if (gaspTable != null) { 1688 return gaspTable; 1689 } 1690 1691 ByteBuffer buffer = getTableBuffer(gaspTag); 1692 if (buffer == null) { 1693 return gaspTable = new char[0]; 1694 } 1695 1696 CharBuffer cbuffer = buffer.asCharBuffer(); 1697 char format = cbuffer.get(); 1698 /* format "1" has appeared for some Windows Vista fonts. 1699 * Its presently undocumented but the existing values 1700 * seem to be still valid so we can use it. 1701 */ 1702 if (format > 1) { // unrecognised format 1703 return gaspTable = new char[0]; 1704 } 1705 1706 char numRanges = cbuffer.get(); 1707 if (4+numRanges*4 > getTableSize(gaspTag)) { // sanity check 1708 return gaspTable = new char[0]; 1709 } 1710 gaspTable = new char[2*numRanges]; 1711 cbuffer.get(gaspTable); 1712 return gaspTable; 1713 } 1714 1715 /* This is to obtain info from the TT 'gasp' (grid-fitting and 1716 * scan-conversion procedure) table which specifies three combinations: 1717 * Hint, Smooth (greyscale), Hint and Smooth. 1718 * In this simplified scheme we don't distinguish the latter two. We 1719 * hint even at small sizes, so as to preserve metrics consistency. 1720 * If the information isn't available default values are substituted. 1721 * The more precise defaults we'd do if we distinguished the cases are: 1722 * Bold (no other style) fonts : 1723 * 0-8 : Smooth ( do grey) 1724 * 9+ : Hint + smooth (gridfit + grey) 1725 * Plain, Italic and Bold-Italic fonts : 1726 * 0-8 : Smooth ( do grey) 1727 * 9-17 : Hint (gridfit) 1728 * 18+ : Hint + smooth (gridfit + grey) 1729 * The defaults should rarely come into play as most TT fonts provide 1730 * better defaults. 1731 * REMIND: consider unpacking the table into an array of booleans 1732 * for faster use. 1733 */ 1734 @Override useAAForPtSize(int ptsize)1735 public boolean useAAForPtSize(int ptsize) { 1736 1737 char[] gasp = getGaspTable(); 1738 if (gasp.length > 0) { 1739 for (int i=0;i<gasp.length;i+=2) { 1740 if (ptsize <= gasp[i]) { 1741 return ((gasp[i+1] & 0x2) != 0); // bit 2 means DO_GRAY; 1742 } 1743 } 1744 return true; 1745 } 1746 1747 if (style == Font.BOLD) { 1748 return true; 1749 } else { 1750 return ptsize <= 8 || ptsize >= 18; 1751 } 1752 } 1753 1754 @Override hasSupplementaryChars()1755 public boolean hasSupplementaryChars() { 1756 return ((TrueTypeGlyphMapper)getMapper()).hasSupplementaryChars(); 1757 } 1758 1759 @Override toString()1760 public String toString() { 1761 return "** TrueType Font: Family="+familyName+ " Name="+fullName+ 1762 " style="+style+" fileName="+getPublicFileName(); 1763 } 1764 } 1765