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