History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/font/TrueTypeFont.java (1 of 18352 files). Show all

Unified · Split

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