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