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+
26+package sun.font;
27+
28+import java.awt.Font;
29+import java.awt.FontFormatException;
30+import java.awt.GraphicsEnvironment;
31+import java.awt.geom.Point2D;
32+import java.io.FileNotFoundException;
33+import java.io.IOException;
34+import java.io.RandomAccessFile;
35+import java.io.UnsupportedEncodingException;
36+import java.nio.ByteBuffer;
37+import java.nio.CharBuffer;
38+import java.nio.IntBuffer;
39+import java.nio.ShortBuffer;
40+import java.nio.channels.ClosedChannelException;
41+import java.nio.channels.FileChannel;
42+import java.util.HashMap;
43+import java.util.HashSet;
44+import java.util.Map;
45+import java.util.Locale;
46+import sun.java2d.Disposer;
47+import sun.java2d.DisposerRecord;
48+
49+/**
50+ * TrueTypeFont is not called SFntFont because it is not expected
51+ * to handle all types that may be housed in a such a font file.
52+ * If additional types are supported later, it may make sense to
53+ * create an SFnt superclass. Eg to handle sfnt-housed postscript fonts.
54+ * OpenType fonts are handled by this class, and possibly should be
55+ * represented by a subclass.
56+ * An instance stores some information from the font file to faciliate
57+ * faster access. File size, the table directory and the names of the font
58+ * are the most important of these. It amounts to approx 400 bytes
59+ * for a typical font. Systems with mutiple locales sometimes have up to 400
60+ * font files, and an app which loads all font files would need around
61+ * 160Kbytes. So storing any more info than this would be expensive.
62+ */
63+public class TrueTypeFont extends FileFont {
64+
65+ /* -- Tags for required TrueType tables */
66+ public static final int cmapTag = 0x636D6170; // 'cmap'
67+ public static final int glyfTag = 0x676C7966; // 'glyf'
68+ public static final int headTag = 0x68656164; // 'head'
69+ public static final int hheaTag = 0x68686561; // 'hhea'
70+ public static final int hmtxTag = 0x686D7478; // 'hmtx'
71+ public static final int locaTag = 0x6C6F6361; // 'loca'
72+ public static final int maxpTag = 0x6D617870; // 'maxp'
73+ public static final int nameTag = 0x6E616D65; // 'name'
74+ public static final int postTag = 0x706F7374; // 'post'
75+ public static final int os_2Tag = 0x4F532F32; // 'OS/2'
76+
77+ /* -- Tags for opentype related tables */
78+ public static final int GDEFTag = 0x47444546; // 'GDEF'
79+ public static final int GPOSTag = 0x47504F53; // 'GPOS'
80+ public static final int GSUBTag = 0x47535542; // 'GSUB'
81+ public static final int mortTag = 0x6D6F7274; // 'mort'
82+
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+}