blob: 9e6c46165fe45dedd16a614946b90ed525d23776 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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.HashSet;
43import java.util.Locale;
44import java.util.logging.Level;
45import sun.java2d.Disposer;
46import sun.java2d.DisposerRecord;
47
48/**
49 * TrueTypeFont is not called SFntFont because it is not expected
50 * to handle all types that may be housed in a such a font file.
51 * If additional types are supported later, it may make sense to
52 * create an SFnt superclass. Eg to handle sfnt-housed postscript fonts.
53 * OpenType fonts are handled by this class, and possibly should be
54 * represented by a subclass.
55 * An instance stores some information from the font file to faciliate
56 * faster access. File size, the table directory and the names of the font
57 * are the most important of these. It amounts to approx 400 bytes
58 * for a typical font. Systems with mutiple locales sometimes have up to 400
59 * font files, and an app which loads all font files would need around
60 * 160Kbytes. So storing any more info than this would be expensive.
61 */
62public class TrueTypeFont extends FileFont {
63
64 /* -- Tags for required TrueType tables */
65 public static final int cmapTag = 0x636D6170; // 'cmap'
66 public static final int glyfTag = 0x676C7966; // 'glyf'
67 public static final int headTag = 0x68656164; // 'head'
68 public static final int hheaTag = 0x68686561; // 'hhea'
69 public static final int hmtxTag = 0x686D7478; // 'hmtx'
70 public static final int locaTag = 0x6C6F6361; // 'loca'
71 public static final int maxpTag = 0x6D617870; // 'maxp'
72 public static final int nameTag = 0x6E616D65; // 'name'
73 public static final int postTag = 0x706F7374; // 'post'
74 public static final int os_2Tag = 0x4F532F32; // 'OS/2'
75
76 /* -- Tags for opentype related tables */
77 public static final int GDEFTag = 0x47444546; // 'GDEF'
78 public static final int GPOSTag = 0x47504F53; // 'GPOS'
79 public static final int GSUBTag = 0x47535542; // 'GSUB'
80 public static final int mortTag = 0x6D6F7274; // 'mort'
81
82 /* -- Tags for non-standard tables */
83 public static final int fdscTag = 0x66647363; // 'fdsc' - gxFont descriptor
84 public static final int fvarTag = 0x66766172; // 'fvar' - gxFont variations
85 public static final int featTag = 0x66656174; // 'feat' - layout features
86 public static final int EBLCTag = 0x45424C43; // 'EBLC' - embedded bitmaps
87 public static final int gaspTag = 0x67617370; // 'gasp' - hint/smooth sizes
88
89 /* -- Other tags */
90 public static final int ttcfTag = 0x74746366; // 'ttcf' - TTC file
91 public static final int v1ttTag = 0x00010000; // 'v1tt' - Version 1 TT font
92 public static final int trueTag = 0x74727565; // 'true' - Version 2 TT font
93
94 /* -- ID's used in the 'name' table */
95 public static final int MS_PLATFORM_ID = 3;
96 /* MS locale id for US English is the "default" */
97 public static final short ENGLISH_LOCALE_ID = 0x0409; // 1033 decimal
98 public static final int FAMILY_NAME_ID = 1;
99 // public static final int STYLE_WEIGHT_ID = 2; // currently unused.
100 public static final int FULL_NAME_ID = 4;
101 public static final int POSTSCRIPT_NAME_ID = 6;
102
103
104 class DirectoryEntry {
105 int tag;
106 int offset;
107 int length;
108 }
109
110 /* There is a pool which limits the number of fd's that are in
111 * use. Normally fd's are closed as they are replaced in the pool.
112 * But if an instance of this class becomes unreferenced, then there
113 * needs to be a way to close the fd. A finalize() method could do this,
114 * but using the Disposer class will ensure its called in a more timely
115 * manner. This is not something which should be relied upon to free
116 * fd's - its a safeguard.
117 */
118 private static class TTDisposerRecord implements DisposerRecord {
119
120 FileChannel channel = null;
121
122 public synchronized void dispose() {
123 try {
124 if (channel != null) {
125 channel.close();
126 }
127 } catch (IOException e) {
128 } finally {
129 channel = null;
130 }
131 }
132 }
133
134 TTDisposerRecord disposerRecord = new TTDisposerRecord();
135
136 /* > 0 only if this font is a part of a collection */
137 int fontIndex = 0;
138
139 /* Number of fonts in this collection. ==1 if not a collection */
140 int directoryCount = 1;
141
142 /* offset in file of table directory for this font */
143 int directoryOffset; // 12 if its not a collection.
144
145 /* number of table entries in the directory/offsets table */
146 int numTables;
147
148 /* The contents of the the directory/offsets table */
149 DirectoryEntry []tableDirectory;
150
151// protected byte []gposTable = null;
152// protected byte []gdefTable = null;
153// protected byte []gsubTable = null;
154// protected byte []mortTable = null;
155// protected boolean hintsTabledChecked = false;
156// protected boolean containsHintsTable = false;
157
158 /* These fields are set from os/2 table info. */
159 private boolean supportsJA;
160 private boolean supportsCJK;
161
162 /**
163 * - does basic verification of the file
164 * - reads the header table for this font (within a collection)
165 * - reads the names (full, family).
166 * - determines the style of the font.
167 * - initializes the CMAP
168 * @throws FontFormatException - if the font can't be opened
169 * or fails verification, or there's no usable cmap
170 */
171 TrueTypeFont(String platname, Object nativeNames, int fIndex,
172 boolean javaRasterizer)
173 throws FontFormatException {
174 super(platname, nativeNames);
175 useJavaRasterizer = javaRasterizer;
176 fontRank = Font2D.TTF_RANK;
177 verify();
178 init(fIndex);
179 Disposer.addObjectRecord(this, disposerRecord);
180 }
181
182 /* Enable natives just for fonts picked up from the platform that
183 * may have external bitmaps on Solaris. Could do this just for
184 * the fonts that are specified in font configuration files which
185 * would lighten the burden (think about that).
186 * The EBLCTag is used to skip natives for fonts that contain embedded
187 * bitmaps as there's no need to use X11 for those fonts.
188 * Skip all the latin fonts as they don't need this treatment.
189 * Further refine this to fonts that are natively accessible (ie
190 * as PCF bitmap fonts on the X11 font path).
191 * This method is called when creating the first strike for this font.
192 */
193 protected boolean checkUseNatives() {
194 if (checkedNatives) {
195 return useNatives;
196 }
197 if (!FontManager.isSolaris || useJavaRasterizer ||
198 FontManager.useT2K || nativeNames == null ||
199 getDirectoryEntry(EBLCTag) != null ||
200 GraphicsEnvironment.isHeadless()) {
201 checkedNatives = true;
202 return false; /* useNatives is false */
203 } else if (nativeNames instanceof String) {
204 String name = (String)nativeNames;
205 /* Don't do do this for Latin fonts */
206 if (name.indexOf("8859") > 0) {
207 checkedNatives = true;
208 return false;
209 } else if (NativeFont.hasExternalBitmaps(name)) {
210 nativeFonts = new NativeFont[1];
211 try {
212 nativeFonts[0] = new NativeFont(name, true);
213 /* If reach here we have an non-latin font that has
214 * external bitmaps and we successfully created it.
215 */
216 useNatives = true;
217 } catch (FontFormatException e) {
218 nativeFonts = null;
219 }
220 }
221 } else if (nativeNames instanceof String[]) {
222 String[] natNames = (String[])nativeNames;
223 int numNames = natNames.length;
224 boolean externalBitmaps = false;
225 for (int nn = 0; nn < numNames; nn++) {
226 if (natNames[nn].indexOf("8859") > 0) {
227 checkedNatives = true;
228 return false;
229 } else if (NativeFont.hasExternalBitmaps(natNames[nn])) {
230 externalBitmaps = true;
231 }
232 }
233 if (!externalBitmaps) {
234 checkedNatives = true;
235 return false;
236 }
237 useNatives = true;
238 nativeFonts = new NativeFont[numNames];
239 for (int nn = 0; nn < numNames; nn++) {
240 try {
241 nativeFonts[nn] = new NativeFont(natNames[nn], true);
242 } catch (FontFormatException e) {
243 useNatives = false;
244 nativeFonts = null;
245 }
246 }
247 }
248 if (useNatives) {
249 glyphToCharMap = new char[getMapper().getNumGlyphs()];
250 }
251 checkedNatives = true;
252 return useNatives;
253 }
254
255
256 /* This is intended to be called, and the returned value used,
257 * from within a block synchronized on this font object.
258 * ie the channel returned may be nulled out at any time by "close()"
259 * unless the caller holds a lock.
260 * Deadlock warning: FontManager.addToPool(..) acquires a global lock,
261 * which means nested locks may be in effect.
262 */
263 private synchronized FileChannel open() throws FontFormatException {
264 if (disposerRecord.channel == null) {
265 if (FontManager.logging) {
266 FontManager.logger.info("open TTF: " + platName);
267 }
268 try {
269 RandomAccessFile raf = (RandomAccessFile)
270 java.security.AccessController.doPrivileged(
271 new java.security.PrivilegedAction() {
272 public Object run() {
273 try {
274 return new RandomAccessFile(platName, "r");
275 } catch (FileNotFoundException ffne) {
276 }
277 return null;
278 }
279 });
280 disposerRecord.channel = raf.getChannel();
281 fileSize = (int)disposerRecord.channel.size();
282 FontManager.addToPool(this);
283 } catch (NullPointerException e) {
284 close();
285 throw new FontFormatException(e.toString());
286 } catch (ClosedChannelException e) {
287 /* NIO I/O is interruptible, recurse to retry operation.
288 * The call to channel.size() above can throw this exception.
289 * Clear interrupts before recursing in case NIO didn't.
290 * Note that close() sets disposerRecord.channel to null.
291 */
292 Thread.interrupted();
293 close();
294 open();
295 } catch (IOException e) {
296 close();
297 throw new FontFormatException(e.toString());
298 }
299 }
300 return disposerRecord.channel;
301 }
302
303 protected synchronized void close() {
304 disposerRecord.dispose();
305 }
306
307
308 int readBlock(ByteBuffer buffer, int offset, int length) {
309 int bread = 0;
310 try {
311 synchronized (this) {
312 if (disposerRecord.channel == null) {
313 open();
314 }
315 if (offset + length > fileSize) {
316 if (offset >= fileSize) {
317 /* Since the caller ensures that offset is < fileSize
318 * this condition suggests that fileSize is now
319 * different than the value we originally provided
320 * to native when the scaler was created.
321 * Also fileSize is updated every time we
322 * open() the file here, but in native the value
323 * isn't updated. If the file has changed whilst we
324 * are executing we want to bail, not spin.
325 */
326 if (FontManager.logging) {
327 String msg = "Read offset is " + offset +
328 " file size is " + fileSize+
329 " file is " + platName;
330 FontManager.logger.severe(msg);
331 }
332 return -1;
333 } else {
334 length = fileSize - offset;
335 }
336 }
337 buffer.clear();
338 disposerRecord.channel.position(offset);
339 while (bread < length) {
340 int cnt = disposerRecord.channel.read(buffer);
341 if (cnt == -1) {
342 String msg = "Unexpected EOF " + this;
343 int currSize = (int)disposerRecord.channel.size();
344 if (currSize != fileSize) {
345 msg += " File size was " + fileSize +
346 " and now is " + currSize;
347 }
348 if (FontManager.logging) {
349 FontManager.logger.severe(msg);
350 }
351 // We could still flip() the buffer here because
352 // it's possible that we did read some data in
353 // an earlier loop, and we probably should
354 // return that to the caller. Although if
355 // the caller expected 8K of data and we return
356 // only a few bytes then maybe it's better instead to
357 // set bread = -1 to indicate failure.
358 // The following is therefore using arbitrary values
359 // but is meant to allow cases where enough
360 // data was read to probably continue.
361 if (bread > length/2 || bread > 16384) {
362 buffer.flip();
363 if (FontManager.logging) {
364 msg = "Returning " + bread +
365 " bytes instead of " + length;
366 FontManager.logger.severe(msg);
367 }
368 } else {
369 bread = -1;
370 }
371 throw new IOException(msg);
372 }
373 bread += cnt;
374 }
375 buffer.flip();
376 if (bread > length) { // possible if buffer.size() > length
377 bread = length;
378 }
379 }
380 } catch (FontFormatException e) {
381 if (FontManager.logging) {
382 FontManager.logger.log(Level.SEVERE,
383 "While reading " + platName, e);
384 }
385 bread = -1; // signal EOF
386 deregisterFontAndClearStrikeCache();
387 } catch (ClosedChannelException e) {
388 /* NIO I/O is interruptible, recurse to retry operation.
389 * Clear interrupts before recursing in case NIO didn't.
390 */
391 Thread.interrupted();
392 close();
393 return readBlock(buffer, offset, length);
394 } catch (IOException e) {
395 /* If we did not read any bytes at all and the exception is
396 * not a recoverable one (ie is not ClosedChannelException) then
397 * we should indicate that there is no point in re-trying.
398 * Other than an attempt to read past the end of the file it
399 * seems unlikely this would occur as problems opening the
400 * file are handled as a FontFormatException.
401 */
402 if (FontManager.logging) {
403 FontManager.logger.log(Level.SEVERE,
404 "While reading " + platName, e);
405 }
406 if (bread == 0) {
407 bread = -1; // signal EOF
408 deregisterFontAndClearStrikeCache();
409 }
410 }
411 return bread;
412 }
413
414 ByteBuffer readBlock(int offset, int length) {
415
416 ByteBuffer buffer = ByteBuffer.allocate(length);
417 try {
418 synchronized (this) {
419 if (disposerRecord.channel == null) {
420 open();
421 }
422 if (offset + length > fileSize) {
423 if (offset > fileSize) {
424 return null; // assert?
425 } else {
426 buffer = ByteBuffer.allocate(fileSize-offset);
427 }
428 }
429 disposerRecord.channel.position(offset);
430 disposerRecord.channel.read(buffer);
431 buffer.flip();
432 }
433 } catch (FontFormatException e) {
434 return null;
435 } catch (ClosedChannelException e) {
436 /* NIO I/O is interruptible, recurse to retry operation.
437 * Clear interrupts before recursing in case NIO didn't.
438 */
439 Thread.interrupted();
440 close();
441 readBlock(buffer, offset, length);
442 } catch (IOException e) {
443 return null;
444 }
445 return buffer;
446 }
447
448 /* This is used by native code which can't allocate a direct byte
449 * buffer because of bug 4845371. It, and references to it in native
450 * code in scalerMethods.c can be removed once that bug is fixed.
451 * 4845371 is now fixed but we'll keep this around as it doesn't cost
452 * us anything if its never used/called.
453 */
454 byte[] readBytes(int offset, int length) {
455 ByteBuffer buffer = readBlock(offset, length);
456 if (buffer.hasArray()) {
457 return buffer.array();
458 } else {
459 byte[] bufferBytes = new byte[buffer.limit()];
460 buffer.get(bufferBytes);
461 return bufferBytes;
462 }
463 }
464
465 private void verify() throws FontFormatException {
466 open();
467 }
468
469 /* sizes, in bytes, of TT/TTC header records */
470 private static final int TTCHEADERSIZE = 12;
471 private static final int DIRECTORYHEADERSIZE = 12;
472 private static final int DIRECTORYENTRYSIZE = 16;
473
474 protected void init(int fIndex) throws FontFormatException {
475 int headerOffset = 0;
476 ByteBuffer buffer = readBlock(0, TTCHEADERSIZE);
477 try {
478 switch (buffer.getInt()) {
479
480 case ttcfTag:
481 buffer.getInt(); // skip TTC version ID
482 directoryCount = buffer.getInt();
483 if (fIndex >= directoryCount) {
484 throw new FontFormatException("Bad collection index");
485 }
486 fontIndex = fIndex;
487 buffer = readBlock(TTCHEADERSIZE+4*fIndex, 4);
488 headerOffset = buffer.getInt();
489 break;
490
491 case v1ttTag:
492 case trueTag:
493 break;
494
495 default:
496 throw new FontFormatException("Unsupported sfnt " + platName);
497 }
498
499 /* Now have the offset of this TT font (possibly within a TTC)
500 * After the TT version/scaler type field, is the short
501 * representing the number of tables in the table directory.
502 * The table directory begins at 12 bytes after the header.
503 * Each table entry is 16 bytes long (4 32-bit ints)
504 */
505 buffer = readBlock(headerOffset+4, 2);
506 numTables = buffer.getShort();
507 directoryOffset = headerOffset+DIRECTORYHEADERSIZE;
508 ByteBuffer bbuffer = readBlock(directoryOffset,
509 numTables*DIRECTORYENTRYSIZE);
510 IntBuffer ibuffer = bbuffer.asIntBuffer();
511 DirectoryEntry table;
512 tableDirectory = new DirectoryEntry[numTables];
513 for (int i=0; i<numTables;i++) {
514 tableDirectory[i] = table = new DirectoryEntry();
515 table.tag = ibuffer.get();
516 /* checksum */ ibuffer.get();
517 table.offset = ibuffer.get();
518 table.length = ibuffer.get();
519 if (table.offset + table.length > fileSize) {
520 throw new FontFormatException("bad table, tag="+table.tag);
521 }
522 }
523 initNames();
524 } catch (Exception e) {
525 if (FontManager.logging) {
526 FontManager.logger.severe(e.toString());
527 }
528 if (e instanceof FontFormatException) {
529 throw (FontFormatException)e;
530 } else {
531 throw new FontFormatException(e.toString());
532 }
533 }
534 if (familyName == null || fullName == null) {
535 throw new FontFormatException("Font name not found");
536 }
537 /* The os2_Table is needed to gather some info, but we don't
538 * want to keep it around (as a field) so obtain it once and
539 * pass it to the code that needs it.
540 */
541 ByteBuffer os2_Table = getTableBuffer(os_2Tag);
542 setStyle(os2_Table);
543 setCJKSupport(os2_Table);
544
545 ByteBuffer head_Table = getTableBuffer(headTag);
546 int upem = -1;
547 if (head_Table != null && head_Table.capacity() >= 18) {
548 ShortBuffer sb = head_Table.asShortBuffer();
549 upem = sb.get(9) & 0xffff;
550 }
551 setStrikethroughMetrics(os2_Table, upem);
552
553 ByteBuffer post_Table = getTableBuffer(postTag);
554 setUnderlineMetrics(post_Table, upem);
555 }
556
557 /* The array index corresponds to a bit offset in the TrueType
558 * font's OS/2 compatibility table's code page ranges fields.
559 * These are two 32 bit unsigned int fields at offsets 78 and 82.
560 * We are only interested in determining if the font supports
561 * the windows encodings we expect as the default encoding in
562 * supported locales, so we only map the first of these fields.
563 */
564 static final String encoding_mapping[] = {
565 "cp1252", /* 0:Latin 1 */
566 "cp1250", /* 1:Latin 2 */
567 "cp1251", /* 2:Cyrillic */
568 "cp1253", /* 3:Greek */
569 "cp1254", /* 4:Turkish/Latin 5 */
570 "cp1255", /* 5:Hebrew */
571 "cp1256", /* 6:Arabic */
572 "cp1257", /* 7:Windows Baltic */
573 "", /* 8:reserved for alternate ANSI */
574 "", /* 9:reserved for alternate ANSI */
575 "", /* 10:reserved for alternate ANSI */
576 "", /* 11:reserved for alternate ANSI */
577 "", /* 12:reserved for alternate ANSI */
578 "", /* 13:reserved for alternate ANSI */
579 "", /* 14:reserved for alternate ANSI */
580 "", /* 15:reserved for alternate ANSI */
581 "ms874", /* 16:Thai */
582 "ms932", /* 17:JIS/Japanese */
583 "gbk", /* 18:PRC GBK Cp950 */
584 "ms949", /* 19:Korean Extended Wansung */
585 "ms950", /* 20:Chinese (Taiwan, Hongkong, Macau) */
586 "ms1361", /* 21:Korean Johab */
587 "", /* 22 */
588 "", /* 23 */
589 "", /* 24 */
590 "", /* 25 */
591 "", /* 26 */
592 "", /* 27 */
593 "", /* 28 */
594 "", /* 29 */
595 "", /* 30 */
596 "", /* 31 */
597 };
598
599 /* This maps two letter language codes to a Windows code page.
600 * Note that eg Cp1252 (the first subarray) is not exactly the same as
601 * Latin-1 since Windows code pages are do not necessarily correspond.
602 * There are two codepages for zh and ko so if a font supports
603 * only one of these ranges then we need to distinguish based on
604 * country. So far this only seems to matter for zh.
605 * REMIND: Unicode locales such as Hindi do not have a code page so
606 * this whole mechansim needs to be revised to map languages to
607 * the Unicode ranges either when this fails, or as an additional
608 * validating test. Basing it on Unicode ranges should get us away
609 * from needing to map to this small and incomplete set of Windows
610 * code pages which looks odd on non-Windows platforms.
611 */
612 private static final String languages[][] = {
613
614 /* cp1252/Latin 1 */
615 { "en", "ca", "da", "de", "es", "fi", "fr", "is", "it",
616 "nl", "no", "pt", "sq", "sv", },
617
618 /* cp1250/Latin2 */
619 { "cs", "cz", "et", "hr", "hu", "nr", "pl", "ro", "sk",
620 "sl", "sq", "sr", },
621
622 /* cp1251/Cyrillic */
623 { "bg", "mk", "ru", "sh", "uk" },
624
625 /* cp1253/Greek*/
626 { "el" },
627
628 /* cp1254/Turkish,Latin 5 */
629 { "tr" },
630
631 /* cp1255/Hebrew */
632 { "he" },
633
634 /* cp1256/Arabic */
635 { "ar" },
636
637 /* cp1257/Windows Baltic */
638 { "et", "lt", "lv" },
639
640 /* ms874/Thai */
641 { "th" },
642
643 /* ms932/Japanese */
644 { "ja" },
645
646 /* gbk/Chinese (PRC GBK Cp950) */
647 { "zh", "zh_CN", },
648
649 /* ms949/Korean Extended Wansung */
650 { "ko" },
651
652 /* ms950/Chinese (Taiwan, Hongkong, Macau) */
653 { "zh_HK", "zh_TW", },
654
655 /* ms1361/Korean Johab */
656 { "ko" },
657 };
658
659 private static final String codePages[] = {
660 "cp1252",
661 "cp1250",
662 "cp1251",
663 "cp1253",
664 "cp1254",
665 "cp1255",
666 "cp1256",
667 "cp1257",
668 "ms874",
669 "ms932",
670 "gbk",
671 "ms949",
672 "ms950",
673 "ms1361",
674 };
675
676 private static String defaultCodePage = null;
677 static String getCodePage() {
678
679 if (defaultCodePage != null) {
680 return defaultCodePage;
681 }
682
683 if (FontManager.isWindows) {
684 defaultCodePage =
685 (String)java.security.AccessController.doPrivileged(
686 new sun.security.action.GetPropertyAction("file.encoding"));
687 } else {
688 if (languages.length != codePages.length) {
689 throw new InternalError("wrong code pages array length");
690 }
691 Locale locale = sun.awt.SunToolkit.getStartupLocale();
692
693 String language = locale.getLanguage();
694 if (language != null) {
695 if (language.equals("zh")) {
696 String country = locale.getCountry();
697 if (country != null) {
698 language = language + "_" + country;
699 }
700 }
701 for (int i=0; i<languages.length;i++) {
702 for (int l=0;l<languages[i].length; l++) {
703 if (language.equals(languages[i][l])) {
704 defaultCodePage = codePages[i];
705 return defaultCodePage;
706 }
707 }
708 }
709 }
710 }
711 if (defaultCodePage == null) {
712 defaultCodePage = "";
713 }
714 return defaultCodePage;
715 }
716
717 /* Theoretically, reserved bits must not be set, include symbol bits */
718 public static final int reserved_bits1 = 0x80000000;
719 public static final int reserved_bits2 = 0x0000ffff;
720 boolean supportsEncoding(String encoding) {
721 if (encoding == null) {
722 encoding = getCodePage();
723 }
724 if ("".equals(encoding)) {
725 return false;
726 }
727
728 encoding = encoding.toLowerCase();
729
730 /* java_props_md.c has a couple of special cases
731 * if language packs are installed. In these encodings the
732 * fontconfig files pick up different fonts :
733 * SimSun-18030 and MingLiU_HKSCS. Since these fonts will
734 * indicate they support the base encoding, we need to rewrite
735 * these encodings here before checking the map/array.
736 */
737 if (encoding.equals("gb18030")) {
738 encoding = "gbk";
739 } else if (encoding.equals("ms950_hkscs")) {
740 encoding = "ms950";
741 }
742
743 ByteBuffer buffer = getTableBuffer(os_2Tag);
744 /* required info is at offsets 78 and 82 */
745 if (buffer == null || buffer.capacity() < 86) {
746 return false;
747 }
748
749 int range1 = buffer.getInt(78); /* ulCodePageRange1 */
750 int range2 = buffer.getInt(82); /* ulCodePageRange2 */
751
752 /* This test is too stringent for Arial on Solaris (and perhaps
753 * other fonts). Arial has at least one reserved bit set for an
754 * unknown reason.
755 */
756// if (((range1 & reserved_bits1) | (range2 & reserved_bits2)) != 0) {
757// return false;
758// }
759
760 for (int em=0; em<encoding_mapping.length; em++) {
761 if (encoding_mapping[em].equals(encoding)) {
762 if (((1 << em) & range1) != 0) {
763 return true;
764 }
765 }
766 }
767 return false;
768 }
769
770
771 /* Use info in the os_2Table to test CJK support */
772 private void setCJKSupport(ByteBuffer os2Table) {
773 /* required info is in ulong at offset 46 */
774 if (os2Table == null || os2Table.capacity() < 50) {
775 return;
776 }
777 int range2 = os2Table.getInt(46); /* ulUnicodeRange2 */
778
779 /* Any of these bits set in the 32-63 range indicate a font with
780 * support for a CJK range. We aren't looking at some other bits
781 * in the 64-69 range such as half width forms as its unlikely a font
782 * would include those and none of these.
783 */
784 supportsCJK = ((range2 & 0x29bf0000) != 0);
785
786 /* This should be generalised, but for now just need to know if
787 * Hiragana or Katakana ranges are supported by the font.
788 * In the 4 longs representing unicode ranges supported
789 * bits 49 & 50 indicate hiragana and katakana
790 * This is bits 17 & 18 in the 2nd ulong. If either is supported
791 * we presume this is a JA font.
792 */
793 supportsJA = ((range2 & 0x60000) != 0);
794 }
795
796 boolean supportsJA() {
797 return supportsJA;
798 }
799
800 ByteBuffer getTableBuffer(int tag) {
801 DirectoryEntry entry = null;
802
803 for (int i=0;i<numTables;i++) {
804 if (tableDirectory[i].tag == tag) {
805 entry = tableDirectory[i];
806 break;
807 }
808 }
809 if (entry == null || entry.length == 0 ||
810 entry.offset+entry.length > fileSize) {
811 return null;
812 }
813
814 int bread = 0;
815 ByteBuffer buffer = ByteBuffer.allocate(entry.length);
816 synchronized (this) {
817 try {
818 if (disposerRecord.channel == null) {
819 open();
820 }
821 disposerRecord.channel.position(entry.offset);
822 bread = disposerRecord.channel.read(buffer);
823 buffer.flip();
824 } catch (ClosedChannelException e) {
825 /* NIO I/O is interruptible, recurse to retry operation.
826 * Clear interrupts before recursing in case NIO didn't.
827 */
828 Thread.interrupted();
829 close();
830 return getTableBuffer(tag);
831 } catch (IOException e) {
832 return null;
833 } catch (FontFormatException e) {
834 return null;
835 }
836
837 if (bread < entry.length) {
838 return null;
839 } else {
840 return buffer;
841 }
842 }
843 }
844
845 /* NB: is it better to move declaration to Font2D? */
846 long getLayoutTableCache() {
847 try {
848 return getScaler().getLayoutTableCache();
849 } catch(FontScalerException fe) {
850 return 0L;
851 }
852 }
853
854 byte[] getTableBytes(int tag) {
855 ByteBuffer buffer = getTableBuffer(tag);
856 if (buffer == null) {
857 return null;
858 } else if (buffer.hasArray()) {
859 try {
860 return buffer.array();
861 } catch (Exception re) {
862 }
863 }
864 byte []data = new byte[getTableSize(tag)];
865 buffer.get(data);
866 return data;
867 }
868
869 int getTableSize(int tag) {
870 for (int i=0;i<numTables;i++) {
871 if (tableDirectory[i].tag == tag) {
872 return tableDirectory[i].length;
873 }
874 }
875 return 0;
876 }
877
878 int getTableOffset(int tag) {
879 for (int i=0;i<numTables;i++) {
880 if (tableDirectory[i].tag == tag) {
881 return tableDirectory[i].offset;
882 }
883 }
884 return 0;
885 }
886
887 DirectoryEntry getDirectoryEntry(int tag) {
888 for (int i=0;i<numTables;i++) {
889 if (tableDirectory[i].tag == tag) {
890 return tableDirectory[i];
891 }
892 }
893 return null;
894 }
895
896 public String getFullName() {
897 return fullName;
898 }
899
900 /* This probably won't get called but is there to support the
901 * contract() of setStyle() defined in the superclass.
902 */
903 protected void setStyle() {
904 setStyle(getTableBuffer(os_2Tag));
905 }
906
907 /* TrueTypeFont can use the fsSelection fields of OS/2 table
908 * to determine the style. In the unlikely case that doesn't exist,
909 * can use macStyle in the 'head' table but simpler to
910 * fall back to super class algorithm of looking for well known string.
911 * A very few fonts don't specify this information, but I only
912 * came across one: Lucida Sans Thai Typewriter Oblique in
913 * /usr/openwin/lib/locale/th_TH/X11/fonts/TrueType/lucidai.ttf
914 * that explicitly specified the wrong value. It says its regular.
915 * I didn't find any fonts that were inconsistent (ie regular plus some
916 * other value).
917 */
918 private static final int fsSelectionItalicBit = 0x00001;
919 private static final int fsSelectionBoldBit = 0x00020;
920 private static final int fsSelectionRegularBit = 0x00040;
921 private void setStyle(ByteBuffer os_2Table) {
922 /* fsSelection is unsigned short at buffer offset 62 */
923 if (os_2Table == null || os_2Table.capacity() < 64) {
924 super.setStyle();
925 return;
926 }
927 int fsSelection = os_2Table.getChar(62) & 0xffff;
928 int italic = fsSelection & fsSelectionItalicBit;
929 int bold = fsSelection & fsSelectionBoldBit;
930 int regular = fsSelection & fsSelectionRegularBit;
931// System.out.println("platname="+platName+" font="+fullName+
932// " family="+familyName+
933// " R="+regular+" I="+italic+" B="+bold);
934 if (regular!=0 && ((italic|bold)!=0)) {
935 /* This is inconsistent. Try using the font name algorithm */
936 super.setStyle();
937 return;
938 } else if ((regular|italic|bold) == 0) {
939 /* No style specified. Try using the font name algorithm */
940 super.setStyle();
941 return;
942 }
943 switch (bold|italic) {
944 case fsSelectionItalicBit:
945 style = Font.ITALIC;
946 break;
947 case fsSelectionBoldBit:
948 if (FontManager.isSolaris && platName.endsWith("HG-GothicB.ttf")) {
949 /* Workaround for Solaris's use of a JA font that's marked as
950 * being designed bold, but is used as a PLAIN font.
951 */
952 style = Font.PLAIN;
953 } else {
954 style = Font.BOLD;
955 }
956 break;
957 case fsSelectionBoldBit|fsSelectionItalicBit:
958 style = Font.BOLD|Font.ITALIC;
959 }
960 }
961
962 private float stSize, stPos, ulSize, ulPos;
963
964 private void setStrikethroughMetrics(ByteBuffer os_2Table, int upem) {
965 if (os_2Table == null || os_2Table.capacity() < 30 || upem < 0) {
966 stSize = .05f;
967 stPos = -.4f;
968 return;
969 }
970 ShortBuffer sb = os_2Table.asShortBuffer();
971 stSize = sb.get(13) / (float)upem;
972 stPos = -sb.get(14) / (float)upem;
973 }
974
975 private void setUnderlineMetrics(ByteBuffer postTable, int upem) {
976 if (postTable == null || postTable.capacity() < 12 || upem < 0) {
977 ulSize = .05f;
978 ulPos = .1f;
979 return;
980 }
981 ShortBuffer sb = postTable.asShortBuffer();
982 ulSize = sb.get(5) / (float)upem;
983 ulPos = -sb.get(4) / (float)upem;
984 }
985
986 public void getStyleMetrics(float pointSize, float[] metrics, int offset) {
987 metrics[offset] = stPos * pointSize;
988 metrics[offset+1] = stSize * pointSize;
989 metrics[offset+2] = ulPos * pointSize;
990 metrics[offset+3] = ulSize * pointSize;
991 }
992
993 private String makeString(byte[] bytes, int len, short encoding) {
994
995 /* Check for fonts using encodings 2->6 is just for
996 * some old DBCS fonts, apparently mostly on Solaris.
997 * Some of these fonts encode ascii names as double-byte characters.
998 * ie with a leading zero byte for what properly should be a
999 * single byte-char.
1000 */
1001 if (encoding >=2 && encoding <= 6) {
1002 byte[] oldbytes = bytes;
1003 int oldlen = len;
1004 bytes = new byte[oldlen];
1005 len = 0;
1006 for (int i=0; i<oldlen; i++) {
1007 if (oldbytes[i] != 0) {
1008 bytes[len++] = oldbytes[i];
1009 }
1010 }
1011 }
1012
1013 String charset;
1014 switch (encoding) {
1015 case 1: charset = "UTF-16"; break; // most common case first.
1016 case 0: charset = "UTF-16"; break; // symbol uses this
1017 case 2: charset = "SJIS"; break;
1018 case 3: charset = "GBK"; break;
1019 case 4: charset = "MS950"; break;
1020 case 5: charset = "EUC_KR"; break;
1021 case 6: charset = "Johab"; break;
1022 default: charset = "UTF-16"; break;
1023 }
1024
1025 try {
1026 return new String(bytes, 0, len, charset);
1027 } catch (UnsupportedEncodingException e) {
1028 if (FontManager.logging) {
1029 FontManager.logger.warning(e + " EncodingID=" + encoding);
1030 }
1031 return new String(bytes, 0, len);
1032 } catch (Throwable t) {
1033 return null;
1034 }
1035 }
1036
1037 protected void initNames() {
1038
1039 byte[] name = new byte[256];
1040 ByteBuffer buffer = getTableBuffer(nameTag);
1041
1042 if (buffer != null) {
1043 ShortBuffer sbuffer = buffer.asShortBuffer();
1044 sbuffer.get(); // format - not needed.
1045 short numRecords = sbuffer.get();
1046 /* The name table uses unsigned shorts. Many of these
1047 * are known small values that fit in a short.
1048 * The values that are sizes or offsets into the table could be
1049 * greater than 32767, so read and store those as ints
1050 */
1051 int stringPtr = sbuffer.get() & 0xffff;
1052 for (int i=0; i<numRecords; i++) {
1053 short platformID = sbuffer.get();
1054 if (platformID != MS_PLATFORM_ID) {
1055 sbuffer.position(sbuffer.position()+5);
1056 continue; // skip over this record.
1057 }
1058 short encodingID = sbuffer.get();
1059 short langID = sbuffer.get();
1060 short nameID = sbuffer.get();
1061 int nameLen = ((int) sbuffer.get()) & 0xffff;
1062 int namePtr = (((int) sbuffer.get()) & 0xffff) + stringPtr;
1063
1064 switch (nameID) {
1065
1066 case FAMILY_NAME_ID:
1067
1068 if (familyName == null || langID == ENGLISH_LOCALE_ID) {
1069 buffer.position(namePtr);
1070 buffer.get(name, 0, nameLen);
1071 familyName = makeString(name, nameLen, encodingID);
1072 }
1073/*
1074 for (int ii=0;ii<nameLen;ii++) {
1075 int val = (int)name[ii]&0xff;
1076 System.err.print(Integer.toHexString(val)+ " ");
1077 }
1078 System.err.println();
1079 System.err.println("familyName="+familyName +
1080 " nameLen="+nameLen+
1081 " langID="+langID+ " eid="+encodingID +
1082 " str len="+familyName.length());
1083
1084*/
1085 break;
1086
1087 case FULL_NAME_ID:
1088
1089 if (fullName == null || langID == ENGLISH_LOCALE_ID) {
1090 buffer.position(namePtr);
1091 buffer.get(name, 0, nameLen);
1092 fullName = makeString(name, nameLen, encodingID);
1093 }
1094 break;
1095
1096 }
1097 }
1098 }
1099 }
1100
1101 /* Return the requested name in the requested locale, for the
1102 * MS platform ID. If the requested locale isn't found, return US
1103 * English, if that isn't found, return null and let the caller
1104 * figure out how to handle that.
1105 */
1106 protected String lookupName(short findLocaleID, int findNameID) {
1107 String foundName = null;
1108 byte[] name = new byte[1024];
1109
1110 ByteBuffer buffer = getTableBuffer(nameTag);
1111 if (buffer != null) {
1112 ShortBuffer sbuffer = buffer.asShortBuffer();
1113 sbuffer.get(); // format - not needed.
1114 short numRecords = sbuffer.get();
1115
1116 /* The name table uses unsigned shorts. Many of these
1117 * are known small values that fit in a short.
1118 * The values that are sizes or offsets into the table could be
1119 * greater than 32767, so read and store those as ints
1120 */
1121 int stringPtr = ((int) sbuffer.get()) & 0xffff;
1122
1123 for (int i=0; i<numRecords; i++) {
1124 short platformID = sbuffer.get();
1125 if (platformID != MS_PLATFORM_ID) {
1126 sbuffer.position(sbuffer.position()+5);
1127 continue; // skip over this record.
1128 }
1129 short encodingID = sbuffer.get();
1130 short langID = sbuffer.get();
1131 short nameID = sbuffer.get();
1132 int nameLen = ((int) sbuffer.get()) & 0xffff;
1133 int namePtr = (((int) sbuffer.get()) & 0xffff) + stringPtr;
1134 if (nameID == findNameID &&
1135 ((foundName == null && langID == ENGLISH_LOCALE_ID)
1136 || langID == findLocaleID)) {
1137 buffer.position(namePtr);
1138 buffer.get(name, 0, nameLen);
1139 foundName = makeString(name, nameLen, encodingID);
1140 if (langID == findLocaleID) {
1141 return foundName;
1142 }
1143 }
1144 }
1145 }
1146 return foundName;
1147 }
1148
1149 /**
1150 * @return number of logical fonts. Is "1" for all but TTC files
1151 */
1152 public int getFontCount() {
1153 return directoryCount;
1154 }
1155
1156 protected synchronized FontScaler getScaler() {
1157 if (scaler == null) {
1158 scaler = FontManager.getScaler(this, fontIndex,
1159 supportsCJK, fileSize);
1160 }
1161 return scaler;
1162 }
1163
1164
1165 /* Postscript name is rarely requested. Don't waste cycles locating it
1166 * as part of font creation, nor storage to hold it. Get it only on demand.
1167 */
1168 public String getPostscriptName() {
1169 String name = lookupName(ENGLISH_LOCALE_ID, POSTSCRIPT_NAME_ID);
1170 if (name == null) {
1171 return fullName;
1172 } else {
1173 return name;
1174 }
1175 }
1176
1177 public String getFontName(Locale locale) {
1178 if (locale == null) {
1179 return fullName;
1180 } else {
1181 short localeID = FontManager.getLCIDFromLocale(locale);
1182 String name = lookupName(localeID, FULL_NAME_ID);
1183 if (name == null) {
1184 return fullName;
1185 } else {
1186 return name;
1187 }
1188 }
1189 }
1190
1191 public String getFamilyName(Locale locale) {
1192 if (locale == null) {
1193 return familyName;
1194 } else {
1195 short localeID = FontManager.getLCIDFromLocale(locale);
1196 String name = lookupName(localeID, FAMILY_NAME_ID);
1197 if (name == null) {
1198 return familyName;
1199 } else {
1200 return name;
1201 }
1202 }
1203 }
1204
1205 public CharToGlyphMapper getMapper() {
1206 if (mapper == null) {
1207 mapper = new TrueTypeGlyphMapper(this);
1208 }
1209 return mapper;
1210 }
1211
1212 /* This duplicates initNames() but that has to run fast as its used
1213 * during typical start-up and the information here is likely never
1214 * needed.
1215 */
1216 protected void initAllNames(int requestedID, HashSet names) {
1217
1218 byte[] name = new byte[256];
1219 ByteBuffer buffer = getTableBuffer(nameTag);
1220
1221 if (buffer != null) {
1222 ShortBuffer sbuffer = buffer.asShortBuffer();
1223 sbuffer.get(); // format - not needed.
1224 short numRecords = sbuffer.get();
1225
1226 /* The name table uses unsigned shorts. Many of these
1227 * are known small values that fit in a short.
1228 * The values that are sizes or offsets into the table could be
1229 * greater than 32767, so read and store those as ints
1230 */
1231 int stringPtr = ((int) sbuffer.get()) & 0xffff;
1232 for (int i=0; i<numRecords; i++) {
1233 short platformID = sbuffer.get();
1234 if (platformID != MS_PLATFORM_ID) {
1235 sbuffer.position(sbuffer.position()+5);
1236 continue; // skip over this record.
1237 }
1238 short encodingID = sbuffer.get();
1239 short langID = sbuffer.get();
1240 short nameID = sbuffer.get();
1241 int nameLen = ((int) sbuffer.get()) & 0xffff;
1242 int namePtr = (((int) sbuffer.get()) & 0xffff) + stringPtr;
1243
1244 if (nameID == requestedID) {
1245 buffer.position(namePtr);
1246 buffer.get(name, 0, nameLen);
1247 names.add(makeString(name, nameLen, encodingID));
1248 }
1249 }
1250 }
1251 }
1252
1253 String[] getAllFamilyNames() {
1254 HashSet aSet = new HashSet();
1255 try {
1256 initAllNames(FAMILY_NAME_ID, aSet);
1257 } catch (Exception e) {
1258 /* In case of malformed font */
1259 }
1260 return (String[])aSet.toArray(new String[0]);
1261 }
1262
1263 String[] getAllFullNames() {
1264 HashSet aSet = new HashSet();
1265 try {
1266 initAllNames(FULL_NAME_ID, aSet);
1267 } catch (Exception e) {
1268 /* In case of malformed font */
1269 }
1270 return (String[])aSet.toArray(new String[0]);
1271 }
1272
1273 /* Used by the OpenType engine for mark positioning.
1274 */
1275 Point2D.Float getGlyphPoint(long pScalerContext,
1276 int glyphCode, int ptNumber) {
1277 try {
1278 return getScaler().getGlyphPoint(pScalerContext,
1279 glyphCode, ptNumber);
1280 } catch(FontScalerException fe) {
1281 return null;
1282 }
1283 }
1284
1285 private char[] gaspTable;
1286
1287 private char[] getGaspTable() {
1288
1289 if (gaspTable != null) {
1290 return gaspTable;
1291 }
1292
1293 ByteBuffer buffer = getTableBuffer(gaspTag);
1294 if (buffer == null) {
1295 return gaspTable = new char[0];
1296 }
1297
1298 CharBuffer cbuffer = buffer.asCharBuffer();
1299 char format = cbuffer.get();
1300 /* format "1" has appeared for some Windows Vista fonts.
1301 * Its presently undocumented but the existing values
1302 * seem to be still valid so we can use it.
1303 */
1304 if (format > 1) { // unrecognised format
1305 return gaspTable = new char[0];
1306 }
1307
1308 char numRanges = cbuffer.get();
1309 if (4+numRanges*4 > getTableSize(gaspTag)) { // sanity check
1310 return gaspTable = new char[0];
1311 }
1312 gaspTable = new char[2*numRanges];
1313 cbuffer.get(gaspTable);
1314 return gaspTable;
1315 }
1316
1317 /* This is to obtain info from the TT 'gasp' (grid-fitting and
1318 * scan-conversion procedure) table which specifies three combinations:
1319 * Hint, Smooth (greyscale), Hint and Smooth.
1320 * In this simplified scheme we don't distinguish the latter two. We
1321 * hint even at small sizes, so as to preserve metrics consistency.
1322 * If the information isn't available default values are substituted.
1323 * The more precise defaults we'd do if we distinguished the cases are:
1324 * Bold (no other style) fonts :
1325 * 0-8 : Smooth ( do grey)
1326 * 9+ : Hint + smooth (gridfit + grey)
1327 * Plain, Italic and Bold-Italic fonts :
1328 * 0-8 : Smooth ( do grey)
1329 * 9-17 : Hint (gridfit)
1330 * 18+ : Hint + smooth (gridfit + grey)
1331 * The defaults should rarely come into play as most TT fonts provide
1332 * better defaults.
1333 * REMIND: consider unpacking the table into an array of booleans
1334 * for faster use.
1335 */
1336 public boolean useAAForPtSize(int ptsize) {
1337
1338 char[] gasp = getGaspTable();
1339 if (gasp.length > 0) {
1340 for (int i=0;i<gasp.length;i+=2) {
1341 if (ptsize <= gasp[i]) {
1342 return ((gasp[i+1] & 0x2) != 0); // bit 2 means DO_GRAY;
1343 }
1344 }
1345 return true;
1346 }
1347
1348 if (style == Font.BOLD) {
1349 return true;
1350 } else {
1351 return ptsize <= 8 || ptsize >= 18;
1352 }
1353 }
1354
1355 public boolean hasSupplementaryChars() {
1356 return ((TrueTypeGlyphMapper)getMapper()).hasSupplementaryChars();
1357 }
1358
1359 public String toString() {
1360 return "** TrueType Font: Family="+familyName+ " Name="+fullName+
1361 " style="+style+" fileName="+platName;
1362 }
1363}