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