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