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