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