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