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