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/awt/FontConfiguration.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/awt/FontConfiguration.java+2272−0
@@ -0,0 +1,2272 @@
1+/*
2+ * Copyright (c) 2000, 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.awt;
27+
28+import java.awt.Font;
29+import java.io.DataInputStream;
30+import java.io.DataOutputStream;
31+import java.io.File;
32+import java.io.FileInputStream;
33+import java.io.InputStream;
34+import java.io.IOException;
35+import java.io.OutputStream;
36+import java.nio.charset.Charset;
37+import java.nio.charset.CharsetEncoder;
38+import java.security.AccessController;
39+import java.security.PrivilegedAction;
40+import java.util.Arrays;
41+import java.util.HashMap;
42+import java.util.HashSet;
43+import java.util.Hashtable;
44+import java.util.Locale;
45+import java.util.Map.Entry;
46+import java.util.Properties;
47+import java.util.Set;
48+import java.util.Vector;
49+import sun.font.CompositeFontDescriptor;
50+import sun.font.SunFontManager;
51+import sun.font.FontManagerFactory;
52+import sun.font.FontUtilities;
53+import sun.util.logging.PlatformLogger;
54+
55+/**
56+ * Provides the definitions of the five logical fonts: Serif, SansSerif,
57+ * Monospaced, Dialog, and DialogInput. The necessary information
58+ * is obtained from fontconfig files.
59+ */
60+public abstract class FontConfiguration {
61+
62+ //static global runtime env
63+ protected static String osVersion;
64+ protected static String osName;
65+ protected static String encoding; // canonical name of default nio charset
66+ protected static Locale startupLocale = null;
67+ protected static Hashtable localeMap = null;
68+ private static FontConfiguration fontConfig;
69+ private static PlatformLogger logger;
70+ protected static boolean isProperties = true;
71+
72+ protected SunFontManager fontManager;
73+ protected boolean preferLocaleFonts;
74+ protected boolean preferPropFonts;
75+
76+ private File fontConfigFile;
77+ private boolean foundOsSpecificFile;
78+ private boolean inited;
79+ private String javaLib;
80+
81+ /* A default FontConfiguration must be created before an alternate
82+ * one to ensure proper static initialisation takes place.
83+ */
84+ public FontConfiguration(SunFontManager fm) {
85+ if (FontUtilities.debugFonts()) {
86+ FontUtilities.getLogger()
87+ .info("Creating standard Font Configuration");
88+ }
89+ if (FontUtilities.debugFonts() && logger == null) {
90+ logger = PlatformLogger.getLogger("sun.awt.FontConfiguration");
91+ }
92+ fontManager = fm;
93+ setOsNameAndVersion(); /* static initialization */
94+ setEncoding(); /* static initialization */
95+ /* Separating out the file location from the rest of the
96+ * initialisation, so the caller has the option of doing
97+ * something else if a suitable file isn't found.
98+ */
99+ findFontConfigFile();
100+ }
101+
102+ public synchronized boolean init() {
103+ if (!inited) {
104+ this.preferLocaleFonts = false;
105+ this.preferPropFonts = false;
106+ setFontConfiguration();
107+ readFontConfigFile(fontConfigFile);
108+ initFontConfig();
109+ inited = true;
110+ }
111+ return true;
112+ }
113+
114+ public FontConfiguration(SunFontManager fm,
115+ boolean preferLocaleFonts,
116+ boolean preferPropFonts) {
117+ fontManager = fm;
118+ if (FontUtilities.debugFonts()) {
119+ FontUtilities.getLogger()
120+ .info("Creating alternate Font Configuration");
121+ }
122+ this.preferLocaleFonts = preferLocaleFonts;
123+ this.preferPropFonts = preferPropFonts;
124+ /* fontConfig should be initialised by default constructor, and
125+ * its data tables can be shared, since readFontConfigFile doesn't
126+ * update any other state. Also avoid a doPrivileged block.
127+ */
128+ initFontConfig();
129+ }
130+
131+ /**
132+ * Fills in this instance's osVersion and osName members. By
133+ * default uses the system properties os.name and os.version;
134+ * subclasses may override.
135+ */
136+ protected void setOsNameAndVersion() {
137+ osName = System.getProperty("os.name");
138+ osVersion = System.getProperty("os.version");
139+ }
140+
141+ private void setEncoding() {
142+ encoding = Charset.defaultCharset().name();
143+ startupLocale = SunToolkit.getStartupLocale();
144+ }
145+
146+ /////////////////////////////////////////////////////////////////////
147+ // methods for loading the FontConfig file //
148+ /////////////////////////////////////////////////////////////////////
149+
150+ public boolean foundOsSpecificFile() {
151+ return foundOsSpecificFile;
152+ }
153+
154+ /* Smoke test to see if we can trust this configuration by testing if
155+ * the first slot of a composite font maps to an installed file.
156+ */
157+ public boolean fontFilesArePresent() {
158+ init();
159+ short fontNameID = compFontNameIDs[0][0][0];
160+ short fileNameID = getComponentFileID(fontNameID);
161+ final String fileName = mapFileName(getComponentFileName(fileNameID));
162+ Boolean exists = (Boolean)java.security.AccessController.doPrivileged(
163+ new java.security.PrivilegedAction() {
164+ public Object run() {
165+ try {
166+ File f = new File(fileName);
167+ return Boolean.valueOf(f.exists());
168+ }
169+ catch (Exception e) {
170+ return false;
171+ }
172+ }
173+ });
174+ return exists.booleanValue();
175+ }
176+
177+ private void findFontConfigFile() {
178+
179+ foundOsSpecificFile = true; // default assumption.
180+ String javaHome = System.getProperty("java.home");
181+ if (javaHome == null) {
182+ throw new Error("java.home property not set");
183+ }
184+ javaLib = javaHome + File.separator + "lib";
185+ String userConfigFile = System.getProperty("sun.awt.fontconfig");
186+ if (userConfigFile != null) {
187+ fontConfigFile = new File(userConfigFile);
188+ } else {
189+ fontConfigFile = findFontConfigFile(javaLib);
190+ }
191+ }
192+
193+ private void readFontConfigFile(File f) {
194+ /* This is invoked here as readFontConfigFile is only invoked
195+ * once per VM, and always in a privileged context, thus the
196+ * directory containing installed fall back fonts is accessed
197+ * from this context
198+ */
199+ getInstalledFallbackFonts(javaLib);
200+
201+ if (f != null) {
202+ try {
203+ FileInputStream in = new FileInputStream(f.getPath());
204+ if (isProperties) {
205+ loadProperties(in);
206+ } else {
207+ loadBinary(in);
208+ }
209+ in.close();
210+ if (FontUtilities.debugFonts()) {
211+ logger.config("Read logical font configuration from " + f);
212+ }
213+ } catch (IOException e) {
214+ if (FontUtilities.debugFonts()) {
215+ logger.config("Failed to read logical font configuration from " + f);
216+ }
217+ }
218+ }
219+ String version = getVersion();
220+ if (!"1".equals(version) && FontUtilities.debugFonts()) {
221+ logger.config("Unsupported fontconfig version: " + version);
222+ }
223+ }
224+
225+ protected void getInstalledFallbackFonts(String javaLib) {
226+ String fallbackDirName = javaLib + File.separator +
227+ "fonts" + File.separator + "fallback";
228+
229+ File fallbackDir = new File(fallbackDirName);
230+ if (fallbackDir.exists() && fallbackDir.isDirectory()) {
231+ String[] ttfs = fallbackDir.list(fontManager.getTrueTypeFilter());
232+ String[] t1s = fallbackDir.list(fontManager.getType1Filter());
233+ int numTTFs = (ttfs == null) ? 0 : ttfs.length;
234+ int numT1s = (t1s == null) ? 0 : t1s.length;
235+ int len = numTTFs + numT1s;
236+ if (numTTFs + numT1s == 0) {
237+ return;
238+ }
239+ installedFallbackFontFiles = new String[len];
240+ for (int i=0; i<numTTFs; i++) {
241+ installedFallbackFontFiles[i] =
242+ fallbackDir + File.separator + ttfs[i];
243+ }
244+ for (int i=0; i<numT1s; i++) {
245+ installedFallbackFontFiles[i+numTTFs] =
246+ fallbackDir + File.separator + t1s[i];
247+ }
248+ fontManager.registerFontsInDir(fallbackDirName);
249+ }
250+ }
251+
252+ private File findImpl(String fname) {
253+ File f = new File(fname + ".properties");
254+ if (f.canRead()) {
255+ isProperties = true;
256+ return f;
257+ }
258+ f = new File(fname + ".bfc");
259+ if (f.canRead()) {
260+ isProperties = false;
261+ return f;
262+ }
263+ return null;
264+ }
265+
266+ private File findFontConfigFile(String javaLib) {
267+ String baseName = javaLib + File.separator + "fontconfig";
268+ File configFile;
269+ if (osVersion != null && osName != null) {
270+ configFile = findImpl(baseName + "." + osName + "." + osVersion);
271+ if (configFile != null) {
272+ return configFile;
273+ }
274+ }
275+ if (osName != null) {
276+ configFile = findImpl(baseName + "." + osName);
277+ if (configFile != null) {
278+ return configFile;
279+ }
280+ }
281+ if (osVersion != null) {
282+ configFile = findImpl(baseName + "." + osVersion);
283+ if (configFile != null) {
284+ return configFile;
285+ }
286+ }
287+ foundOsSpecificFile = false;
288+
289+ configFile = findImpl(baseName);
290+ if (configFile != null) {
291+ return configFile;
292+ }
293+ return null;
294+ }
295+
296+ /* Initialize the internal data tables from binary format font
297+ * configuration file.
298+ */
299+ public static void loadBinary(InputStream inStream) throws IOException {
300+ DataInputStream in = new DataInputStream(inStream);
301+ head = readShortTable(in, HEAD_LENGTH);
302+ int[] tableSizes = new int[INDEX_TABLEEND];
303+ for (int i = 0; i < INDEX_TABLEEND; i++) {
304+ tableSizes[i] = head[i + 1] - head[i];
305+ }
306+ table_scriptIDs = readShortTable(in, tableSizes[INDEX_scriptIDs]);
307+ table_scriptFonts = readShortTable(in, tableSizes[INDEX_scriptFonts]);
308+ table_elcIDs = readShortTable(in, tableSizes[INDEX_elcIDs]);
309+ table_sequences = readShortTable(in, tableSizes[INDEX_sequences]);
310+ table_fontfileNameIDs = readShortTable(in, tableSizes[INDEX_fontfileNameIDs]);
311+ table_componentFontNameIDs = readShortTable(in, tableSizes[INDEX_componentFontNameIDs]);
312+ table_filenames = readShortTable(in, tableSizes[INDEX_filenames]);
313+ table_awtfontpaths = readShortTable(in, tableSizes[INDEX_awtfontpaths]);
314+ table_exclusions = readShortTable(in, tableSizes[INDEX_exclusions]);
315+ table_proportionals = readShortTable(in, tableSizes[INDEX_proportionals]);
316+ table_scriptFontsMotif = readShortTable(in, tableSizes[INDEX_scriptFontsMotif]);
317+ table_alphabeticSuffix = readShortTable(in, tableSizes[INDEX_alphabeticSuffix]);
318+ table_stringIDs = readShortTable(in, tableSizes[INDEX_stringIDs]);
319+
320+ //StringTable cache
321+ stringCache = new String[table_stringIDs.length + 1];
322+
323+ int len = tableSizes[INDEX_stringTable];
324+ byte[] bb = new byte[len * 2];
325+ table_stringTable = new char[len];
326+ in.read(bb);
327+ int i = 0, j = 0;
328+ while (i < len) {
329+ table_stringTable[i++] = (char)(bb[j++] << 8 | (bb[j++] & 0xff));
330+ }
331+ if (verbose) {
332+ dump();
333+ }
334+ }
335+
336+ /* Generate a binary format font configuration from internal data
337+ * tables.
338+ */
339+ public static void saveBinary(OutputStream out) throws IOException {
340+ sanityCheck();
341+
342+ DataOutputStream dataOut = new DataOutputStream(out);
343+ writeShortTable(dataOut, head);
344+ writeShortTable(dataOut, table_scriptIDs);
345+ writeShortTable(dataOut, table_scriptFonts);
346+ writeShortTable(dataOut, table_elcIDs);
347+ writeShortTable(dataOut, table_sequences);
348+ writeShortTable(dataOut, table_fontfileNameIDs);
349+ writeShortTable(dataOut, table_componentFontNameIDs);
350+ writeShortTable(dataOut, table_filenames);
351+ writeShortTable(dataOut, table_awtfontpaths);
352+ writeShortTable(dataOut, table_exclusions);
353+ writeShortTable(dataOut, table_proportionals);
354+ writeShortTable(dataOut, table_scriptFontsMotif);
355+ writeShortTable(dataOut, table_alphabeticSuffix);
356+ writeShortTable(dataOut, table_stringIDs);
357+ //stringTable
358+ dataOut.writeChars(new String(table_stringTable));
359+ out.close();
360+ if (verbose) {
361+ dump();
362+ }
363+ }
364+
365+ //private static boolean loadingProperties;
366+ private static short stringIDNum;
367+ private static short[] stringIDs;
368+ private static StringBuilder stringTable;
369+
370+ public static void loadProperties(InputStream in) throws IOException {
371+ //loadingProperties = true;
372+ //StringID starts from "1", "0" is reserved for "not defined"
373+ stringIDNum = 1;
374+ stringIDs = new short[1000];
375+ stringTable = new StringBuilder(4096);
376+
377+ if (verbose && logger == null) {
378+ logger = PlatformLogger.getLogger("sun.awt.FontConfiguration");
379+ }
380+ new PropertiesHandler().load(in);
381+
382+ //loadingProperties = false;
383+ stringIDs = null;
384+ stringTable = null;
385+ }
386+
387+
388+ /////////////////////////////////////////////////////////////////////
389+ // methods for initializing the FontConfig //
390+ /////////////////////////////////////////////////////////////////////
391+
392+ /**
393+ * set initLocale, initEncoding and initELC for this FontConfig object
394+ * currently we just simply use the startup locale and encoding
395+ */
396+ private void initFontConfig() {
397+ initLocale = startupLocale;
398+ initEncoding = encoding;
399+ if (preferLocaleFonts && !willReorderForStartupLocale()) {
400+ preferLocaleFonts = false;
401+ }
402+ initELC = getInitELC();
403+ initAllComponentFonts();
404+ }
405+
406+ //"ELC" stands for "Encoding.Language.Country". This method returns
407+ //the ID of the matched elc setting of "initLocale" in elcIDs table.
408+ //If no match is found, it returns the default ID, which is
409+ //"NULL.NULL.NULL" in elcIDs table.
410+ private short getInitELC() {
411+ if (initELC != -1) {
412+ return initELC;
413+ }
414+ HashMap <String, Integer> elcIDs = new HashMap<String, Integer>();
415+ for (int i = 0; i < table_elcIDs.length; i++) {
416+ elcIDs.put(getString(table_elcIDs[i]), i);
417+ }
418+ String language = initLocale.getLanguage();
419+ String country = initLocale.getCountry();
420+ String elc;
421+ if (elcIDs.containsKey(elc=initEncoding + "." + language + "." + country)
422+ || elcIDs.containsKey(elc=initEncoding + "." + language)
423+ || elcIDs.containsKey(elc=initEncoding)) {
424+ initELC = elcIDs.get(elc).shortValue();
425+ } else {
426+ initELC = elcIDs.get("NULL.NULL.NULL").shortValue();
427+ }
428+ int i = 0;
429+ while (i < table_alphabeticSuffix.length) {
430+ if (initELC == table_alphabeticSuffix[i]) {
431+ alphabeticSuffix = getString(table_alphabeticSuffix[i + 1]);
432+ return initELC;
433+ }
434+ i += 2;
435+ }
436+ return initELC;
437+ }
438+
439+ public static boolean verbose;
440+ private short initELC = -1;
441+ private Locale initLocale;
442+ private String initEncoding;
443+ private String alphabeticSuffix;
444+
445+ private short[][][] compFontNameIDs = new short[NUM_FONTS][NUM_STYLES][];
446+ private int[][][] compExclusions = new int[NUM_FONTS][][];
447+ private int[] compCoreNum = new int[NUM_FONTS];
448+
449+ private Set<Short> coreFontNameIDs = new HashSet<Short>();
450+ private Set<Short> fallbackFontNameIDs = new HashSet<Short>();
451+
452+ private void initAllComponentFonts() {
453+ short[] fallbackScripts = getFallbackScripts();
454+ for (int fontIndex = 0; fontIndex < NUM_FONTS; fontIndex++) {
455+ short[] coreScripts = getCoreScripts(fontIndex);
456+ compCoreNum[fontIndex] = coreScripts.length;
457+ /*
458+ System.out.println("coreScriptID=" + table_sequences[initELC * 5 + fontIndex]);
459+ for (int i = 0; i < coreScripts.length; i++) {
460+ System.out.println(" " + i + " :" + getString(table_scriptIDs[coreScripts[i]]));
461+ }
462+ */
463+ //init exclusionRanges
464+ int[][] exclusions = new int[coreScripts.length][];
465+ for (int i = 0; i < coreScripts.length; i++) {
466+ exclusions[i] = getExclusionRanges(coreScripts[i]);
467+ }
468+ compExclusions[fontIndex] = exclusions;
469+ //init componentFontNames
470+ for (int styleIndex = 0; styleIndex < NUM_STYLES; styleIndex++) {
471+ int index;
472+ short[] nameIDs = new short[coreScripts.length + fallbackScripts.length];
473+ //core
474+ for (index = 0; index < coreScripts.length; index++) {
475+ nameIDs[index] = getComponentFontID(coreScripts[index],
476+ fontIndex, styleIndex);
477+ if (preferLocaleFonts && localeMap != null &&
478+ fontManager.usingAlternateFontforJALocales()) {
479+ nameIDs[index] = remapLocaleMap(fontIndex, styleIndex,
480+ coreScripts[index], nameIDs[index]);
481+ }
482+ if (preferPropFonts) {
483+ nameIDs[index] = remapProportional(fontIndex, nameIDs[index]);
484+ }
485+ //System.out.println("nameid=" + nameIDs[index]);
486+ coreFontNameIDs.add(nameIDs[index]);
487+ }
488+ //fallback
489+ for (int i = 0; i < fallbackScripts.length; i++) {
490+ short id = getComponentFontID(fallbackScripts[i],
491+ fontIndex, styleIndex);
492+ if (preferLocaleFonts && localeMap != null &&
493+ fontManager.usingAlternateFontforJALocales()) {
494+ id = remapLocaleMap(fontIndex, styleIndex, fallbackScripts[i], id);
495+ }
496+ if (preferPropFonts) {
497+ id = remapProportional(fontIndex, id);
498+ }
499+ if (contains(nameIDs, id, index)) {
500+ continue;
501+ }
502+ /*
503+ System.out.println("fontIndex=" + fontIndex + ", styleIndex=" + styleIndex
504+ + ", fbIndex=" + i + ",fbS=" + fallbackScripts[i] + ", id=" + id);
505+ */
506+ fallbackFontNameIDs.add(id);
507+ nameIDs[index++] = id;
508+ }
509+ if (index < nameIDs.length) {
510+ short[] newNameIDs = new short[index];
511+ System.arraycopy(nameIDs, 0, newNameIDs, 0, index);
512+ nameIDs = newNameIDs;
513+ }
514+ compFontNameIDs[fontIndex][styleIndex] = nameIDs;
515+ }
516+ }
517+ }
518+
519+ private short remapLocaleMap(int fontIndex, int styleIndex, short scriptID, short fontID) {
520+ String scriptName = getString(table_scriptIDs[scriptID]);
521+
522+ String value = (String)localeMap.get(scriptName);
523+ if (value == null) {
524+ String fontName = fontNames[fontIndex];
525+ String styleName = styleNames[styleIndex];
526+ value = (String)localeMap.get(fontName + "." + styleName + "." + scriptName);
527+ }
528+ if (value == null) {
529+ return fontID;
530+ }
531+
532+ for (int i = 0; i < table_componentFontNameIDs.length; i++) {
533+ String name = getString(table_componentFontNameIDs[i]);
534+ if (value.equalsIgnoreCase(name)) {
535+ fontID = (short)i;
536+ break;
537+ }
538+ }
539+ return fontID;
540+ }
541+
542+ public static boolean hasMonoToPropMap() {
543+ return table_proportionals != null && table_proportionals.length != 0;
544+ }
545+
546+ private short remapProportional(int fontIndex, short id) {
547+ if (preferPropFonts &&
548+ table_proportionals.length != 0 &&
549+ fontIndex != 2 && //"monospaced"
550+ fontIndex != 4) { //"dialoginput"
551+ int i = 0;
552+ while (i < table_proportionals.length) {
553+ if (table_proportionals[i] == id) {
554+ return table_proportionals[i + 1];
555+ }
556+ i += 2;
557+ }
558+ }
559+ return id;
560+ }
561+
562+ /////////////////////////////////////////////////////////////////////
563+ // Methods for handling font and style names //
564+ /////////////////////////////////////////////////////////////////////
565+ protected static final int NUM_FONTS = 5;
566+ protected static final int NUM_STYLES = 4;
567+ protected static final String[] fontNames
568+ = {"serif", "sansserif", "monospaced", "dialog", "dialoginput"};
569+ protected static final String[] publicFontNames
570+ = {Font.SERIF, Font.SANS_SERIF, Font.MONOSPACED, Font.DIALOG,
571+ Font.DIALOG_INPUT};
572+ protected static final String[] styleNames
573+ = {"plain", "bold", "italic", "bolditalic"};
574+
575+ /**
576+ * Checks whether the given font family name is a valid logical font name.
577+ * The check is case insensitive.
578+ */
579+ public static boolean isLogicalFontFamilyName(String fontName) {
580+ return isLogicalFontFamilyNameLC(fontName.toLowerCase(Locale.ENGLISH));
581+ }
582+
583+ /**
584+ * Checks whether the given font family name is a valid logical font name.
585+ * The check is case sensitive.
586+ */
587+ public static boolean isLogicalFontFamilyNameLC(String fontName) {
588+ for (int i = 0; i < fontNames.length; i++) {
589+ if (fontName.equals(fontNames[i])) {
590+ return true;
591+ }
592+ }
593+ return false;
594+ }
595+
596+ /**
597+ * Checks whether the given style name is a valid logical font style name.
598+ */
599+ private static boolean isLogicalFontStyleName(String styleName) {
600+ for (int i = 0; i < styleNames.length; i++) {
601+ if (styleName.equals(styleNames[i])) {
602+ return true;
603+ }
604+ }
605+ return false;
606+ }
607+
608+ /**
609+ * Checks whether the given font face name is a valid logical font name.
610+ * The check is case insensitive.
611+ */
612+ public static boolean isLogicalFontFaceName(String fontName) {
613+ return isLogicalFontFaceNameLC(fontName.toLowerCase(Locale.ENGLISH));
614+ }
615+
616+ /**
617+ * Checks whether the given font face name is a valid logical font name.
618+ * The check is case sensitive.
619+ */
620+ public static boolean isLogicalFontFaceNameLC(String fontName) {
621+ int period = fontName.indexOf('.');
622+ if (period >= 0) {
623+ String familyName = fontName.substring(0, period);
624+ String styleName = fontName.substring(period + 1);
625+ return isLogicalFontFamilyName(familyName) &&
626+ isLogicalFontStyleName(styleName);
627+ } else {
628+ return isLogicalFontFamilyName(fontName);
629+ }
630+ }
631+
632+ protected static int getFontIndex(String fontName) {
633+ return getArrayIndex(fontNames, fontName);
634+ }
635+
636+ protected static int getStyleIndex(String styleName) {
637+ return getArrayIndex(styleNames, styleName);
638+ }
639+
640+ private static int getArrayIndex(String[] names, String name) {
641+ for (int i = 0; i < names.length; i++) {
642+ if (name.equals(names[i])) {
643+ return i;
644+ }
645+ }
646+ assert false;
647+ return 0;
648+ }
649+
650+ protected static int getStyleIndex(int style) {
651+ switch (style) {
652+ case Font.PLAIN:
653+ return 0;
654+ case Font.BOLD:
655+ return 1;
656+ case Font.ITALIC:
657+ return 2;
658+ case Font.BOLD | Font.ITALIC:
659+ return 3;
660+ default:
661+ return 0;
662+ }
663+ }
664+
665+ protected static String getFontName(int fontIndex) {
666+ return fontNames[fontIndex];
667+ }
668+
669+ protected static String getStyleName(int styleIndex) {
670+ return styleNames[styleIndex];
671+ }
672+
673+ /**
674+ * Returns the font face name for the given logical font
675+ * family name and style.
676+ * The style argument is interpreted as in java.awt.Font.Font.
677+ */
678+ public static String getLogicalFontFaceName(String familyName, int style) {
679+ assert isLogicalFontFamilyName(familyName);
680+ return familyName.toLowerCase(Locale.ENGLISH) + "." + getStyleString(style);
681+ }
682+
683+ /**
684+ * Returns the string typically used in properties files
685+ * for the given style.
686+ * The style argument is interpreted as in java.awt.Font.Font.
687+ */
688+ public static String getStyleString(int style) {
689+ return getStyleName(getStyleIndex(style));
690+ }
691+
692+ /**
693+ * Returns a fallback name for the given font name. For a few known
694+ * font names, matching logical font names are returned. For all
695+ * other font names, defaultFallback is returned.
696+ * defaultFallback differs between AWT and 2D.
697+ */
698+ public abstract String getFallbackFamilyName(String fontName, String defaultFallback);
699+
700+ /**
701+ * Returns the 1.1 equivalent for some old 1.0 font family names for
702+ * which we need to maintain compatibility in some configurations.
703+ * Returns null for other font names.
704+ */
705+ protected String getCompatibilityFamilyName(String fontName) {
706+ fontName = fontName.toLowerCase(Locale.ENGLISH);
707+ if (fontName.equals("timesroman")) {
708+ return "serif";
709+ } else if (fontName.equals("helvetica")) {
710+ return "sansserif";
711+ } else if (fontName.equals("courier")) {
712+ return "monospaced";
713+ }
714+ return null;
715+ }
716+
717+ protected static String[] installedFallbackFontFiles = null;
718+
719+ /**
720+ * Maps a file name given in the font configuration file
721+ * to a format appropriate for the platform.
722+ */
723+ protected String mapFileName(String fileName) {
724+ return fileName;
725+ }
726+
727+ //////////////////////////////////////////////////////////////////////
728+ // reordering //
729+ //////////////////////////////////////////////////////////////////////
730+
731+ /* Mappings from file encoding to font config name for font supporting
732+ * the corresponding language. This is filled in by initReorderMap()
733+ */
734+ protected HashMap reorderMap = null;
735+
736+ /* Platform-specific mappings */
737+ protected abstract void initReorderMap();
738+
739+ /* Move item at index "src" to "dst", shuffling all values in
740+ * between down
741+ */
742+ private void shuffle(String[] seq, int src, int dst) {
743+ if (dst >= src) {
744+ return;
745+ }
746+ String tmp = seq[src];
747+ for (int i=src; i>dst; i--) {
748+ seq[i] = seq[i-1];
749+ }
750+ seq[dst] = tmp;
751+ }
752+
753+ /* Called to determine if there's a re-order sequence for this locale/
754+ * encoding. If there's none then the caller can "bail" and avoid
755+ * unnecessary work
756+ */
757+ public static boolean willReorderForStartupLocale() {
758+ return getReorderSequence() != null;
759+ }
760+
761+ private static Object getReorderSequence() {
762+ if (fontConfig.reorderMap == null) {
763+ fontConfig.initReorderMap();
764+ }
765+ HashMap reorderMap = fontConfig.reorderMap;
766+
767+ /* Find the most specific mapping */
768+ String language = startupLocale.getLanguage();
769+ String country = startupLocale.getCountry();
770+ Object val = reorderMap.get(encoding + "." + language + "." + country);
771+ if (val == null) {
772+ val = reorderMap.get(encoding + "." + language);
773+ }
774+ if (val == null) {
775+ val = reorderMap.get(encoding);
776+ }
777+ return val;
778+ }
779+
780+ /* This method reorders the sequence such that the matches for the
781+ * file encoding are moved ahead of other elements.
782+ * If an encoding uses more than one font, they are all moved up.
783+ */
784+ private void reorderSequenceForLocale(String[] seq) {
785+ Object val = getReorderSequence();
786+ if (val instanceof String) {
787+ for (int i=0; i< seq.length; i++) {
788+ if (seq[i].equals(val)) {
789+ shuffle(seq, i, 0);
790+ return;
791+ }
792+ }
793+ } else if (val instanceof String[]) {
794+ String[] fontLangs = (String[])val;
795+ for (int l=0; l<fontLangs.length;l++) {
796+ for (int i=0; i<seq.length;i++) {
797+ if (seq[i].equals(fontLangs[l])) {
798+ shuffle(seq, i, l);
799+ }
800+ }
801+ }
802+ }
803+ }
804+
805+ private static Vector splitSequence(String sequence) {
806+ //String.split would be more convenient, but incurs big performance penalty
807+ Vector parts = new Vector();
808+ int start = 0;
809+ int end;
810+ while ((end = sequence.indexOf(',', start)) >= 0) {
811+ parts.add(sequence.substring(start, end));
812+ start = end + 1;
813+ }
814+ if (sequence.length() > start) {
815+ parts.add(sequence.substring(start, sequence.length()));
816+ }
817+ return parts;
818+ }
819+
820+ protected String[] split(String sequence) {
821+ Vector v = splitSequence(sequence);
822+ return (String[])v.toArray(new String[0]);
823+ }
824+
825+ ////////////////////////////////////////////////////////////////////////
826+ // Methods for extracting information from the fontconfig data for AWT//
827+ ////////////////////////////////////////////////////////////////////////
828+ private Hashtable charsetRegistry = new Hashtable(5);
829+
830+ /**
831+ * Returns FontDescriptors describing the physical fonts used for the
832+ * given logical font name and style. The font name is interpreted
833+ * in a case insensitive way.
834+ * The style argument is interpreted as in java.awt.Font.Font.
835+ */
836+ public FontDescriptor[] getFontDescriptors(String fontName, int style) {
837+ assert isLogicalFontFamilyName(fontName);
838+ fontName = fontName.toLowerCase(Locale.ENGLISH);
839+ int fontIndex = getFontIndex(fontName);
840+ int styleIndex = getStyleIndex(style);
841+ return getFontDescriptors(fontIndex, styleIndex);
842+ }
843+ private FontDescriptor[][][] fontDescriptors =
844+ new FontDescriptor[NUM_FONTS][NUM_STYLES][];
845+
846+ private FontDescriptor[] getFontDescriptors(int fontIndex, int styleIndex) {
847+ FontDescriptor[] descriptors = fontDescriptors[fontIndex][styleIndex];
848+ if (descriptors == null) {
849+ descriptors = buildFontDescriptors(fontIndex, styleIndex);
850+ fontDescriptors[fontIndex][styleIndex] = descriptors;
851+ }
852+ return descriptors;
853+ }
854+
855+ private FontDescriptor[] buildFontDescriptors(int fontIndex, int styleIndex) {
856+ String fontName = fontNames[fontIndex];
857+ String styleName = styleNames[styleIndex];
858+
859+ short[] scriptIDs = getCoreScripts(fontIndex);
860+ short[] nameIDs = compFontNameIDs[fontIndex][styleIndex];
861+ String[] sequence = new String[scriptIDs.length];
862+ String[] names = new String[scriptIDs.length];
863+ for (int i = 0; i < sequence.length; i++) {
864+ names[i] = getComponentFontName(nameIDs[i]);
865+ sequence[i] = getScriptName(scriptIDs[i]);
866+ if (alphabeticSuffix != null && "alphabetic".equals(sequence[i])) {
867+ sequence[i] = sequence[i] + "/" + alphabeticSuffix;
868+ }
869+ }
870+ int[][] fontExclusionRanges = compExclusions[fontIndex];
871+
872+ FontDescriptor[] descriptors = new FontDescriptor[names.length];
873+
874+ for (int i = 0; i < names.length; i++) {
875+ String awtFontName;
876+ String encoding;
877+
878+ awtFontName = makeAWTFontName(names[i], sequence[i]);
879+
880+ // look up character encoding
881+ encoding = getEncoding(names[i], sequence[i]);
882+ if (encoding == null) {
883+ encoding = "default";
884+ }
885+ CharsetEncoder enc
886+ = getFontCharsetEncoder(encoding.trim(), awtFontName);
887+
888+ // we already have the exclusion ranges
889+ int[] exclusionRanges = fontExclusionRanges[i];
890+
891+ // create descriptor
892+ descriptors[i] = new FontDescriptor(awtFontName, enc, exclusionRanges);
893+ }
894+ return descriptors;
895+ }
896+
897+ /**
898+ * Returns the AWT font name for the given platform font name and
899+ * character subset.
900+ */
901+ protected String makeAWTFontName(String platformFontName,
902+ String characterSubsetName) {
903+ return platformFontName;
904+ }
905+
906+ /**
907+ * Returns the java.io name of the platform character encoding for the
908+ * given AWT font name and character subset. May return "default"
909+ * to indicate that getDefaultFontCharset should be called to obtain
910+ * a charset encoder.
911+ */
912+ protected abstract String getEncoding(String awtFontName,
913+ String characterSubsetName);
914+
915+ private CharsetEncoder getFontCharsetEncoder(final String charsetName,
916+ String fontName) {
917+
918+ Charset fc = null;
919+ if (charsetName.equals("default")) {
920+ fc = (Charset) charsetRegistry.get(fontName);
921+ } else {
922+ fc = (Charset) charsetRegistry.get(charsetName);
923+ }
924+ if (fc != null) {
925+ return fc.newEncoder();
926+ }
927+
928+ if (!charsetName.startsWith("sun.awt.") && !charsetName.equals("default")) {
929+ fc = Charset.forName(charsetName);
930+ } else {
931+ Class fcc = (Class) AccessController.doPrivileged(new PrivilegedAction() {
932+ public Object run() {
933+ try {
934+ return Class.forName(charsetName, true,
935+ Thread.currentThread().getContextClassLoader());
936+ } catch (ClassNotFoundException e) {
937+ }
938+ return null;
939+ }
940+ });
941+
942+ if (fcc != null) {
943+ try {
944+ fc = (Charset) fcc.newInstance();
945+ } catch (Exception e) {
946+ }
947+ }
948+ }
949+ if (fc == null) {
950+ fc = getDefaultFontCharset(fontName);
951+ }
952+
953+ if (charsetName.equals("default")){
954+ charsetRegistry.put(fontName, fc);
955+ } else {
956+ charsetRegistry.put(charsetName, fc);
957+ }
958+ return fc.newEncoder();
959+ }
960+
961+ protected abstract Charset getDefaultFontCharset(
962+ String fontName);
963+
964+ /* This retrieves the platform font directories (path) calculated
965+ * by setAWTFontPathSequence(String[]). The default implementation
966+ * returns null, its expected that X11 platforms may return
967+ * non-null.
968+ */
969+ public HashSet<String> getAWTFontPathSet() {
970+ return null;
971+ }
972+
973+ ////////////////////////////////////////////////////////////////////////
974+ // methods for extracting information from the fontconfig data for 2D //
975+ ////////////////////////////////////////////////////////////////////////
976+
977+ /**
978+ * Returns an array of composite font descriptors for all logical font
979+ * faces.
980+ * If the font configuration file doesn't specify Lucida Sans Regular
981+ * or the given fallback font as component fonts, they are added here.
982+ */
983+ public CompositeFontDescriptor[] get2DCompositeFontInfo() {
984+ CompositeFontDescriptor[] result =
985+ new CompositeFontDescriptor[NUM_FONTS * NUM_STYLES];
986+ String defaultFontFile = fontManager.getDefaultFontFile();
987+ String defaultFontFaceName = fontManager.getDefaultFontFaceName();
988+
989+ for (int fontIndex = 0; fontIndex < NUM_FONTS; fontIndex++) {
990+ String fontName = publicFontNames[fontIndex];
991+
992+ // determine exclusion ranges for font
993+ // AWT uses separate exclusion range array per component font.
994+ // 2D packs all range boundaries into one array.
995+ // Both use separate entries for lower and upper boundary.
996+ int[][] exclusions = compExclusions[fontIndex];
997+ int numExclusionRanges = 0;
998+ for (int i = 0; i < exclusions.length; i++) {
999+ numExclusionRanges += exclusions[i].length;
1000+ }
1001+ int[] exclusionRanges = new int[numExclusionRanges];
1002+ int[] exclusionRangeLimits = new int[exclusions.length];
1003+ int exclusionRangeIndex = 0;
1004+ int exclusionRangeLimitIndex = 0;
1005+ for (int i = 0; i < exclusions.length; i++) {
1006+ int[] componentRanges = exclusions[i];
1007+ for (int j = 0; j < componentRanges.length; ) {
1008+ int value = componentRanges[j];
1009+ exclusionRanges[exclusionRangeIndex++] = componentRanges[j++];
1010+ exclusionRanges[exclusionRangeIndex++] = componentRanges[j++];
1011+ }
1012+ exclusionRangeLimits[i] = exclusionRangeIndex;
1013+ }
1014+ // other info is per style
1015+ for (int styleIndex = 0; styleIndex < NUM_STYLES; styleIndex++) {
1016+ int maxComponentFontCount = compFontNameIDs[fontIndex][styleIndex].length;
1017+ boolean sawDefaultFontFile = false;
1018+ // fall back fonts listed in the lib/fonts/fallback directory
1019+ if (installedFallbackFontFiles != null) {
1020+ maxComponentFontCount += installedFallbackFontFiles.length;
1021+ }
1022+ String faceName = fontName + "." + styleNames[styleIndex];
1023+
1024+ // determine face names and file names of component fonts
1025+ String[] componentFaceNames = new String[maxComponentFontCount];
1026+ String[] componentFileNames = new String[maxComponentFontCount];
1027+
1028+ int index;
1029+ for (index = 0; index < compFontNameIDs[fontIndex][styleIndex].length; index++) {
1030+ short fontNameID = compFontNameIDs[fontIndex][styleIndex][index];
1031+ short fileNameID = getComponentFileID(fontNameID);
1032+ componentFaceNames[index] = getFaceNameFromComponentFontName(getComponentFontName(fontNameID));
1033+ componentFileNames[index] = mapFileName(getComponentFileName(fileNameID));
1034+ if (componentFileNames[index] == null ||
1035+ needToSearchForFile(componentFileNames[index])) {
1036+ componentFileNames[index] = getFileNameFromComponentFontName(getComponentFontName(fontNameID));
1037+ }
1038+ if (!sawDefaultFontFile &&
1039+ defaultFontFile.equals(componentFileNames[index])) {
1040+ sawDefaultFontFile = true;
1041+ }
1042+ /*
1043+ System.out.println(publicFontNames[fontIndex] + "." + styleNames[styleIndex] + "."
1044+ + getString(table_scriptIDs[coreScripts[index]]) + "=" + componentFileNames[index]);
1045+ */
1046+ }
1047+
1048+ //"Lucida Sans Regular" is not in the list, we add it here
1049+ if (!sawDefaultFontFile) {
1050+ int len = 0;
1051+ if (installedFallbackFontFiles != null) {
1052+ len = installedFallbackFontFiles.length;
1053+ }
1054+ if (index + len == maxComponentFontCount) {
1055+ String[] newComponentFaceNames = new String[maxComponentFontCount + 1];
1056+ System.arraycopy(componentFaceNames, 0, newComponentFaceNames, 0, index);
1057+ componentFaceNames = newComponentFaceNames;
1058+ String[] newComponentFileNames = new String[maxComponentFontCount + 1];
1059+ System.arraycopy(componentFileNames, 0, newComponentFileNames, 0, index);
1060+ componentFileNames = newComponentFileNames;
1061+ }
1062+ componentFaceNames[index] = defaultFontFaceName;
1063+ componentFileNames[index] = defaultFontFile;
1064+ index++;
1065+ }
1066+
1067+ if (installedFallbackFontFiles != null) {
1068+ for (int ifb=0; ifb<installedFallbackFontFiles.length; ifb++) {
1069+ componentFaceNames[index] = null;
1070+ componentFileNames[index] = installedFallbackFontFiles[ifb];
1071+ index++;
1072+ }
1073+ }
1074+
1075+ if (index < maxComponentFontCount) {
1076+ String[] newComponentFaceNames = new String[index];
1077+ System.arraycopy(componentFaceNames, 0, newComponentFaceNames, 0, index);
1078+ componentFaceNames = newComponentFaceNames;
1079+ String[] newComponentFileNames = new String[index];
1080+ System.arraycopy(componentFileNames, 0, newComponentFileNames, 0, index);
1081+ componentFileNames = newComponentFileNames;
1082+ }
1083+ // exclusion range limit array length must match component face name
1084+ // array length - native code relies on this
1085+
1086+ int[] clippedExclusionRangeLimits = exclusionRangeLimits;
1087+ if (index != clippedExclusionRangeLimits.length) {
1088+ int len = exclusionRangeLimits.length;
1089+ clippedExclusionRangeLimits = new int[index];
1090+ System.arraycopy(exclusionRangeLimits, 0, clippedExclusionRangeLimits, 0, len);
1091+ //padding for various fallback fonts
1092+ for (int i = len; i < index; i++) {
1093+ clippedExclusionRangeLimits[i] = exclusionRanges.length;
1094+ }
1095+ }
1096+ /*
1097+ System.out.println(faceName + ":");
1098+ for (int i = 0; i < componentFileNames.length; i++) {
1099+ System.out.println(" " + componentFaceNames[i]
1100+ + " -> " + componentFileNames[i]);
1101+ }
1102+ */
1103+ result[fontIndex * NUM_STYLES + styleIndex]
1104+ = new CompositeFontDescriptor(
1105+ faceName,
1106+ compCoreNum[fontIndex],
1107+ componentFaceNames,
1108+ componentFileNames,
1109+ exclusionRanges,
1110+ clippedExclusionRangeLimits);
1111+ }
1112+ }
1113+ return result;
1114+ }
1115+
1116+ protected abstract String getFaceNameFromComponentFontName(String componentFontName);
1117+ protected abstract String getFileNameFromComponentFontName(String componentFontName);
1118+
1119+ /*
1120+ public class 2dFont {
1121+ public String platformName;
1122+ public String fontfileName;
1123+ }
1124+ private 2dFont [] componentFonts = null;
1125+ */
1126+
1127+ /* Used on Linux to test if a file referenced in a font configuration
1128+ * file exists in the location that is expected. If it does, no need
1129+ * to search for it. If it doesn't then unless its a fallback font,
1130+ * return that expensive code should be invoked to search for the font.
1131+ */
1132+ HashMap<String, Boolean> existsMap;
1133+ public boolean needToSearchForFile(String fileName) {
1134+ if (!FontUtilities.isLinux) {
1135+ return false;
1136+ } else if (existsMap == null) {
1137+ existsMap = new HashMap<String, Boolean>();
1138+ }
1139+ Boolean exists = existsMap.get(fileName);
1140+ if (exists == null) {
1141+ /* call getNumberCoreFonts() to ensure these are initialised, and
1142+ * if this file isn't for a core component, ie, is a for a fallback
1143+ * font which very typically isn't available, then can't afford
1144+ * to take the start-up penalty to search for it.
1145+ */
1146+ getNumberCoreFonts();
1147+ if (!coreFontFileNames.contains(fileName)) {
1148+ exists = Boolean.TRUE;
1149+ } else {
1150+ exists = Boolean.valueOf((new File(fileName)).exists());
1151+ existsMap.put(fileName, exists);
1152+ if (FontUtilities.debugFonts() &&
1153+ exists == Boolean.FALSE) {
1154+ logger.warning("Couldn't locate font file " + fileName);
1155+ }
1156+ }
1157+ }
1158+ return exists == Boolean.FALSE;
1159+ }
1160+
1161+ private int numCoreFonts = -1;
1162+ private String[] componentFonts = null;
1163+ HashMap <String, String> filenamesMap = new HashMap<String, String>();
1164+ HashSet <String> coreFontFileNames = new HashSet<String>();
1165+
1166+ /* Return the number of core fonts. Note this isn't thread safe but
1167+ * a calling thread can call this and getPlatformFontNames() in either
1168+ * order.
1169+ */
1170+ public int getNumberCoreFonts() {
1171+ if (numCoreFonts == -1) {
1172+ numCoreFonts = coreFontNameIDs.size();
1173+ Short[] emptyShortArray = new Short[0];
1174+ Short[] core = coreFontNameIDs.toArray(emptyShortArray);
1175+ Short[] fallback = fallbackFontNameIDs.toArray(emptyShortArray);
1176+
1177+ int numFallbackFonts = 0;
1178+ int i;
1179+ for (i = 0; i < fallback.length; i++) {
1180+ if (coreFontNameIDs.contains(fallback[i])) {
1181+ fallback[i] = null;
1182+ continue;
1183+ }
1184+ numFallbackFonts++;
1185+ }
1186+ componentFonts = new String[numCoreFonts + numFallbackFonts];
1187+ String filename = null;
1188+ for (i = 0; i < core.length; i++) {
1189+ short fontid = core[i];
1190+ short fileid = getComponentFileID(fontid);
1191+ componentFonts[i] = getComponentFontName(fontid);
1192+ String compFileName = getComponentFileName(fileid);
1193+ if (compFileName != null) {
1194+ coreFontFileNames.add(compFileName);
1195+ }
1196+ filenamesMap.put(componentFonts[i], mapFileName(compFileName));
1197+ }
1198+ for (int j = 0; j < fallback.length; j++) {
1199+ if (fallback[j] != null) {
1200+ short fontid = fallback[j];
1201+ short fileid = getComponentFileID(fontid);
1202+ componentFonts[i] = getComponentFontName(fontid);
1203+ filenamesMap.put(componentFonts[i],
1204+ mapFileName(getComponentFileName(fileid)));
1205+ i++;
1206+ }
1207+ }
1208+ }
1209+ return numCoreFonts;
1210+ }
1211+
1212+ /* Return all platform font names used by this font configuration.
1213+ * The first getNumberCoreFonts() entries are guaranteed to be the
1214+ * core fonts - ie no fall back only fonts.
1215+ */
1216+ public String[] getPlatformFontNames() {
1217+ if (numCoreFonts == -1) {
1218+ getNumberCoreFonts();
1219+ }
1220+ return componentFonts;
1221+ }
1222+
1223+ /**
1224+ * Returns a file name for the physical font represented by this platform font name,
1225+ * if the font configuration has such information available, or null if the
1226+ * information is unavailable. The file name returned is just a hint; a null return
1227+ * value doesn't necessarily mean that the font is unavailable, nor does a non-null
1228+ * return value guarantee that the file exists and contains the physical font.
1229+ * The file name can be an absolute or a relative path name.
1230+ */
1231+ public String getFileNameFromPlatformName(String platformName) {
1232+ // get2DCompositeFontInfo
1233+ // -> getFileNameFromComponentfontName() (W/M)
1234+ // -> getFileNameFromPlatformName()
1235+ // it's a waste of time on Win32, but I have to give X11 a chance to
1236+ // call getFileNameFromXLFD()
1237+ return filenamesMap.get(platformName);
1238+ }
1239+
1240+ /**
1241+ * Returns a configuration specific path to be appended to the font
1242+ * search path.
1243+ */
1244+ public String getExtraFontPath() {
1245+ return getString(head[INDEX_appendedfontpath]);
1246+ }
1247+
1248+ public String getVersion() {
1249+ return getString(head[INDEX_version]);
1250+ }
1251+
1252+ /* subclass support */
1253+ protected static FontConfiguration getFontConfiguration() {
1254+ return fontConfig;
1255+ }
1256+
1257+ protected void setFontConfiguration() {
1258+ fontConfig = this; /* static initialization */
1259+ }
1260+
1261+ //////////////////////////////////////////////////////////////////////
1262+ // FontConfig data tables and the index constants in binary file //
1263+ //////////////////////////////////////////////////////////////////////
1264+ /* The binary font configuration file begins with a short[] "head", which
1265+ * contains the offsets to the starts of the individual data table which
1266+ * immediately follow. Teh current implemention includes the tables shown
1267+ * below.
1268+ *
1269+ * (00) table_scriptIDs :stringIDs of all defined CharacterSubsetNames
1270+ * (01) table_scriptFonts :scriptID x fontIndex x styleIndex->
1271+ * PlatformFontNameID mapping. Each scriptID might
1272+ * have 1 or 20 entries depends on if it is defined
1273+ * via a "allfonts.CharacterSubsetname" or a list of
1274+ * "LogicalFontName.StyleName.CharacterSubsetName"
1275+ * entries, positive entry means it's a "allfonts"
1276+ * entry, a negative value means this is a offset to
1277+ * a NUM_FONTS x NUM_STYLES subtable.
1278+ * (02) table_elcIDs :stringIDs of all defined ELC names, string
1279+ * "NULL.NULL.NULL" is used for "default"
1280+ * (03) table_sequences :elcID x logicalFont -> scriptIDs table defined
1281+ * by "sequence.allfonts/LogicalFontName.ELC" in
1282+ * font configuration file, each "elcID" has
1283+ * NUM_FONTS (5) entries in this table.
1284+ * (04) table_fontfileNameIDs
1285+ * :stringIDs of all defined font file names
1286+ * (05) table_componentFontNameIDs
1287+ * :stringIDs of all defined PlatformFontNames
1288+ * (06) table_filenames :platformFontNamesID->fontfileNameID mapping
1289+ * table, the index is the platformFontNamesID.
1290+ * (07) table_awtfontpaths :CharacterSubsetNames->awtfontpaths mapping table,
1291+ * the index is the CharacterSubsetName's stringID
1292+ * and content is the stringID of awtfontpath.
1293+ * (08) table_exclusions :scriptID -> exclusionRanges mapping table,
1294+ * the index is the scriptID and the content is
1295+ a id of an exclusionRanges int[].
1296+ * (09) table_proportionals:list of pairs of PlatformFontNameIDs, stores
1297+ * the replacement info defined by "proportional"
1298+ * keyword.
1299+ * (10) table_scriptFontsMotif
1300+ * :same as (01) except this table stores the
1301+ * info defined with ".motif" keyword
1302+ * (11) table_alphabeticSuffix
1303+ * :elcID -> stringID of alphabetic/XXXX entries
1304+ * (12) table_stringIDs :The index of this table is the string ID, the
1305+ * content is the "start index" of this string in
1306+ * stringTable, use the start index of next entry
1307+ * as the "end index".
1308+ * (13) table_stringTable :The real storage of all character strings defined
1309+ * /used this font configuration, need a pair of
1310+ * "start" and "end" indices to access.
1311+ * (14) reserved
1312+ * (15) table_fallbackScripts
1313+ * :stringIDs of fallback CharacterSubsetnames, stored
1314+ * in the order of they are defined in sequence.fallback.
1315+ * (16) table_appendedfontpath
1316+ * :stringtID of the "appendedfontpath" defined.
1317+ * (17) table_version :stringID of the version number of this fontconfig file.
1318+ */
1319+ private static final int HEAD_LENGTH = 20;
1320+ private static final int INDEX_scriptIDs = 0;
1321+ private static final int INDEX_scriptFonts = 1;
1322+ private static final int INDEX_elcIDs = 2;
1323+ private static final int INDEX_sequences = 3;
1324+ private static final int INDEX_fontfileNameIDs = 4;
1325+ private static final int INDEX_componentFontNameIDs = 5;
1326+ private static final int INDEX_filenames = 6;
1327+ private static final int INDEX_awtfontpaths = 7;
1328+ private static final int INDEX_exclusions = 8;
1329+ private static final int INDEX_proportionals = 9;
1330+ private static final int INDEX_scriptFontsMotif = 10;
1331+ private static final int INDEX_alphabeticSuffix = 11;
1332+ private static final int INDEX_stringIDs = 12;
1333+ private static final int INDEX_stringTable = 13;
1334+ private static final int INDEX_TABLEEND = 14;
1335+ private static final int INDEX_fallbackScripts = 15;
1336+ private static final int INDEX_appendedfontpath = 16;
1337+ private static final int INDEX_version = 17;
1338+
1339+ private static short[] head;
1340+ private static short[] table_scriptIDs;
1341+ private static short[] table_scriptFonts;
1342+ private static short[] table_elcIDs;
1343+ private static short[] table_sequences;
1344+ private static short[] table_fontfileNameIDs;
1345+ private static short[] table_componentFontNameIDs;
1346+ private static short[] table_filenames;
1347+ protected static short[] table_awtfontpaths;
1348+ private static short[] table_exclusions;
1349+ private static short[] table_proportionals;
1350+ private static short[] table_scriptFontsMotif;
1351+ private static short[] table_alphabeticSuffix;
1352+ private static short[] table_stringIDs;
1353+ private static char[] table_stringTable;
1354+
1355+ /**
1356+ * Checks consistencies of complied fontconfig data. This method
1357+ * is called only at the build-time from
1358+ * build.tools.compilefontconfig.CompileFontConfig.
1359+ */
1360+ private static void sanityCheck() {
1361+ int errors = 0;
1362+
1363+ //This method will only be called during build time, do we
1364+ //need do PrivilegedAction?
1365+ String osName = (String)java.security.AccessController.doPrivileged(
1366+ new java.security.PrivilegedAction() {
1367+ public Object run() {
1368+ return System.getProperty("os.name");
1369+ }
1370+ });
1371+
1372+ //componentFontNameID starts from "1"
1373+ for (int ii = 1; ii < table_filenames.length; ii++) {
1374+ if (table_filenames[ii] == -1) {
1375+ // The corresponding finename entry for a component
1376+ // font name is mandatory on Windows, but it's
1377+ // optional on Solaris and Linux.
1378+ if (osName.contains("Windows")) {
1379+ System.err.println("\n Error: <filename."
1380+ + getString(table_componentFontNameIDs[ii])
1381+ + "> entry is missing!!!");
1382+ errors++;
1383+ } else {
1384+ if (verbose && !isEmpty(table_filenames)) {
1385+ System.err.println("\n Note: 'filename' entry is undefined for \""
1386+ + getString(table_componentFontNameIDs[ii])
1387+ + "\"");
1388+ }
1389+ }
1390+ }
1391+ }
1392+ for (int ii = 0; ii < table_scriptIDs.length; ii++) {
1393+ short fid = table_scriptFonts[ii];
1394+ if (fid == 0) {
1395+ System.out.println("\n Error: <allfonts."
1396+ + getString(table_scriptIDs[ii])
1397+ + "> entry is missing!!!");
1398+ errors++;
1399+ continue;
1400+ } else if (fid < 0) {
1401+ fid = (short)-fid;
1402+ for (int iii = 0; iii < NUM_FONTS; iii++) {
1403+ for (int iij = 0; iij < NUM_STYLES; iij++) {
1404+ int jj = iii * NUM_STYLES + iij;
1405+ short ffid = table_scriptFonts[fid + jj];
1406+ if (ffid == 0) {
1407+ System.err.println("\n Error: <"
1408+ + getFontName(iii) + "."
1409+ + getStyleName(iij) + "."
1410+ + getString(table_scriptIDs[ii])
1411+ + "> entry is missing!!!");
1412+ errors++;
1413+ }
1414+ }
1415+ }
1416+ }
1417+ }
1418+ if ("SunOS".equals(osName)) {
1419+ for (int ii = 0; ii < table_awtfontpaths.length; ii++) {
1420+ if (table_awtfontpaths[ii] == 0) {
1421+ String script = getString(table_scriptIDs[ii]);
1422+ if (script.contains("lucida") ||
1423+ script.contains("dingbats") ||
1424+ script.contains("symbol")) {
1425+ continue;
1426+ }
1427+ System.err.println("\nError: "
1428+ + "<awtfontpath."
1429+ + script
1430+ + "> entry is missing!!!");
1431+ errors++;
1432+ }
1433+ }
1434+ }
1435+ if (errors != 0) {
1436+ System.err.println("!!THERE ARE " + errors + " ERROR(S) IN "
1437+ + "THE FONTCONFIG FILE, PLEASE CHECK ITS CONTENT!!\n");
1438+ System.exit(1);
1439+ }
1440+ }
1441+
1442+ private static boolean isEmpty(short[] a) {
1443+ for (short s : a) {
1444+ if (s != -1) {
1445+ return false;
1446+ }
1447+ }
1448+ return true;
1449+ }
1450+
1451+ //dump the fontconfig data tables
1452+ private static void dump() {
1453+ System.out.println("\n----Head Table------------");
1454+ for (int ii = 0; ii < HEAD_LENGTH; ii++) {
1455+ System.out.println(" " + ii + " : " + head[ii]);
1456+ }
1457+ System.out.println("\n----scriptIDs-------------");
1458+ printTable(table_scriptIDs, 0);
1459+ System.out.println("\n----scriptFonts----------------");
1460+ for (int ii = 0; ii < table_scriptIDs.length; ii++) {
1461+ short fid = table_scriptFonts[ii];
1462+ if (fid >= 0) {
1463+ System.out.println(" allfonts."
1464+ + getString(table_scriptIDs[ii])
1465+ + "="
1466+ + getString(table_componentFontNameIDs[fid]));
1467+ }
1468+ }
1469+ for (int ii = 0; ii < table_scriptIDs.length; ii++) {
1470+ short fid = table_scriptFonts[ii];
1471+ if (fid < 0) {
1472+ fid = (short)-fid;
1473+ for (int iii = 0; iii < NUM_FONTS; iii++) {
1474+ for (int iij = 0; iij < NUM_STYLES; iij++) {
1475+ int jj = iii * NUM_STYLES + iij;
1476+ short ffid = table_scriptFonts[fid + jj];
1477+ System.out.println(" "
1478+ + getFontName(iii) + "."
1479+ + getStyleName(iij) + "."
1480+ + getString(table_scriptIDs[ii])
1481+ + "="
1482+ + getString(table_componentFontNameIDs[ffid]));
1483+ }
1484+ }
1485+
1486+ }
1487+ }
1488+ System.out.println("\n----elcIDs----------------");
1489+ printTable(table_elcIDs, 0);
1490+ System.out.println("\n----sequences-------------");
1491+ for (int ii = 0; ii< table_elcIDs.length; ii++) {
1492+ System.out.println(" " + ii + "/" + getString((short)table_elcIDs[ii]));
1493+ short[] ss = getShortArray(table_sequences[ii * NUM_FONTS + 0]);
1494+ for (int jj = 0; jj < ss.length; jj++) {
1495+ System.out.println(" " + getString((short)table_scriptIDs[ss[jj]]));
1496+ }
1497+ }
1498+ System.out.println("\n----fontfileNameIDs-------");
1499+ printTable(table_fontfileNameIDs, 0);
1500+
1501+ System.out.println("\n----componentFontNameIDs--");
1502+ printTable(table_componentFontNameIDs, 1);
1503+ System.out.println("\n----filenames-------------");
1504+ for (int ii = 0; ii < table_filenames.length; ii++) {
1505+ if (table_filenames[ii] == -1) {
1506+ System.out.println(" " + ii + " : null");
1507+ } else {
1508+ System.out.println(" " + ii + " : "
1509+ + getString(table_fontfileNameIDs[table_filenames[ii]]));
1510+ }
1511+ }
1512+ System.out.println("\n----awtfontpaths---------");
1513+ for (int ii = 0; ii < table_awtfontpaths.length; ii++) {
1514+ System.out.println(" " + getString(table_scriptIDs[ii])
1515+ + " : "
1516+ + getString(table_awtfontpaths[ii]));
1517+ }
1518+ System.out.println("\n----proportionals--------");
1519+ for (int ii = 0; ii < table_proportionals.length; ii++) {
1520+ System.out.println(" "
1521+ + getString((short)table_componentFontNameIDs[table_proportionals[ii++]])
1522+ + " -> "
1523+ + getString((short)table_componentFontNameIDs[table_proportionals[ii]]));
1524+ }
1525+ int i = 0;
1526+ System.out.println("\n----alphabeticSuffix----");
1527+ while (i < table_alphabeticSuffix.length) {
1528+ System.out.println(" " + getString(table_elcIDs[table_alphabeticSuffix[i++]])
1529+ + " -> " + getString(table_alphabeticSuffix[i++]));
1530+ }
1531+ System.out.println("\n----String Table---------");
1532+ System.out.println(" stringID: Num =" + table_stringIDs.length);
1533+ System.out.println(" stringTable: Size=" + table_stringTable.length * 2);
1534+
1535+ System.out.println("\n----fallbackScriptIDs---");
1536+ short[] fbsIDs = getShortArray(head[INDEX_fallbackScripts]);
1537+ for (int ii = 0; ii < fbsIDs.length; ii++) {
1538+ System.out.println(" " + getString(table_scriptIDs[fbsIDs[ii]]));
1539+ }
1540+ System.out.println("\n----appendedfontpath-----");
1541+ System.out.println(" " + getString(head[INDEX_appendedfontpath]));
1542+ System.out.println("\n----Version--------------");
1543+ System.out.println(" " + getString(head[INDEX_version]));
1544+ }
1545+
1546+
1547+ //////////////////////////////////////////////////////////////////////
1548+ // Data table access methods //
1549+ //////////////////////////////////////////////////////////////////////
1550+
1551+ /* Return the fontID of the platformFontName defined in this font config
1552+ * by "LogicalFontName.StyleName.CharacterSubsetName" entry or
1553+ * "allfonts.CharacterSubsetName" entry in properties format fc file.
1554+ */
1555+ protected static short getComponentFontID(short scriptID, int fontIndex, int styleIndex) {
1556+ short fid = table_scriptFonts[scriptID];
1557+ //System.out.println("fid=" + fid + "/ scriptID=" + scriptID + ", fi=" + fontIndex + ", si=" + styleIndex);
1558+ if (fid >= 0) {
1559+ //"allfonts"
1560+ return fid;
1561+ } else {
1562+ return table_scriptFonts[-fid + fontIndex * NUM_STYLES + styleIndex];
1563+ }
1564+ }
1565+
1566+ /* Same as getCompoentFontID() except this method returns the fontID define by
1567+ * "xxxx.motif" entry.
1568+ */
1569+ protected static short getComponentFontIDMotif(short scriptID, int fontIndex, int styleIndex) {
1570+ if (table_scriptFontsMotif.length == 0) {
1571+ return 0;
1572+ }
1573+ short fid = table_scriptFontsMotif[scriptID];
1574+ if (fid >= 0) {
1575+ //"allfonts" > 0 or "not defined" == 0
1576+ return fid;
1577+ } else {
1578+ return table_scriptFontsMotif[-fid + fontIndex * NUM_STYLES + styleIndex];
1579+ }
1580+ }
1581+
1582+ private static int[] getExclusionRanges(short scriptID) {
1583+ short exID = table_exclusions[scriptID];
1584+ if (exID == 0) {
1585+ return EMPTY_INT_ARRAY;
1586+ } else {
1587+ char[] exChar = getString(exID).toCharArray();
1588+ int[] exInt = new int[exChar.length / 2];
1589+ int i = 0;
1590+ for (int j = 0; j < exInt.length; j++) {
1591+ exInt[j] = (exChar[i++] << 16) + (exChar[i++] & 0xffff);
1592+ }
1593+ return exInt;
1594+ }
1595+ }
1596+
1597+ private static boolean contains(short IDs[], short id, int limit) {
1598+ for (int i = 0; i < limit; i++) {
1599+ if (IDs[i] == id) {
1600+ return true;
1601+ }
1602+ }
1603+ return false;
1604+ }
1605+
1606+ /* Return the PlatformFontName from its fontID*/
1607+ protected static String getComponentFontName(short id) {
1608+ if (id < 0) {
1609+ return null;
1610+ }
1611+ return getString(table_componentFontNameIDs[id]);
1612+ }
1613+
1614+ private static String getComponentFileName(short id) {
1615+ if (id < 0) {
1616+ return null;
1617+ }
1618+ return getString(table_fontfileNameIDs[id]);
1619+ }
1620+
1621+ //componentFontID -> componentFileID
1622+ private static short getComponentFileID(short nameID) {
1623+ return table_filenames[nameID];
1624+ }
1625+
1626+ private static String getScriptName(short scriptID) {
1627+ return getString(table_scriptIDs[scriptID]);
1628+ }
1629+
1630+ private HashMap<String, Short> reorderScripts;
1631+ protected short[] getCoreScripts(int fontIndex) {
1632+ short elc = getInitELC();
1633+ /*
1634+ System.out.println("getCoreScripts: elc=" + elc + ", fontIndex=" + fontIndex);
1635+ short[] ss = getShortArray(table_sequences[elc * NUM_FONTS + fontIndex]);
1636+ for (int i = 0; i < ss.length; i++) {
1637+ System.out.println(" " + getString((short)table_scriptIDs[ss[i]]));
1638+ }
1639+ */
1640+ short[] scripts = getShortArray(table_sequences[elc * NUM_FONTS + fontIndex]);
1641+ if (preferLocaleFonts) {
1642+ if (reorderScripts == null) {
1643+ reorderScripts = new HashMap<String, Short>();
1644+ }
1645+ String[] ss = new String[scripts.length];
1646+ for (int i = 0; i < ss.length; i++) {
1647+ ss[i] = getScriptName(scripts[i]);
1648+ reorderScripts.put(ss[i], scripts[i]);
1649+ }
1650+ reorderSequenceForLocale(ss);
1651+ for (int i = 0; i < ss.length; i++) {
1652+ scripts[i] = reorderScripts.get(ss[i]);
1653+ }
1654+ }
1655+ return scripts;
1656+ }
1657+
1658+ private static short[] getFallbackScripts() {
1659+ return getShortArray(head[INDEX_fallbackScripts]);
1660+ }
1661+
1662+ private static void printTable(short[] list, int start) {
1663+ for (int i = start; i < list.length; i++) {
1664+ System.out.println(" " + i + " : " + getString(list[i]));
1665+ }
1666+ }
1667+
1668+ private static short[] readShortTable(DataInputStream in, int len )
1669+ throws IOException {
1670+ if (len == 0) {
1671+ return EMPTY_SHORT_ARRAY;
1672+ }
1673+ short[] data = new short[len];
1674+ byte[] bb = new byte[len * 2];
1675+ in.read(bb);
1676+ int i = 0,j = 0;
1677+ while (i < len) {
1678+ data[i++] = (short)(bb[j++] << 8 | (bb[j++] & 0xff));
1679+ }
1680+ return data;
1681+ }
1682+
1683+ private static void writeShortTable(DataOutputStream out, short[] data)
1684+ throws IOException {
1685+ for (short val : data) {
1686+ out.writeShort(val);
1687+ }
1688+ }
1689+
1690+ private static short[] toList(HashMap<String, Short> map) {
1691+ short[] list = new short[map.size()];
1692+ Arrays.fill(list, (short) -1);
1693+ for (Entry<String, Short> entry : map.entrySet()) {
1694+ list[entry.getValue()] = getStringID(entry.getKey());
1695+ }
1696+ return list;
1697+ }
1698+
1699+ //runtime cache
1700+ private static String[] stringCache;
1701+ protected static String getString(short stringID) {
1702+ if (stringID == 0)
1703+ return null;
1704+ /*
1705+ if (loadingProperties) {
1706+ return stringTable.substring(stringIDs[stringID],
1707+ stringIDs[stringID+1]);
1708+ }
1709+ */
1710+ //sync if we want it to be MT-enabled
1711+ if (stringCache[stringID] == null){
1712+ stringCache[stringID] =
1713+ new String (table_stringTable,
1714+ table_stringIDs[stringID],
1715+ table_stringIDs[stringID+1] - table_stringIDs[stringID]);
1716+ }
1717+ return stringCache[stringID];
1718+ }
1719+
1720+ private static short[] getShortArray(short shortArrayID) {
1721+ String s = getString(shortArrayID);
1722+ char[] cc = s.toCharArray();
1723+ short[] ss = new short[cc.length];
1724+ for (int i = 0; i < cc.length; i++) {
1725+ ss[i] = (short)(cc[i] & 0xffff);
1726+ }
1727+ return ss;
1728+ }
1729+
1730+ private static short getStringID(String s) {
1731+ if (s == null) {
1732+ return (short)0;
1733+ }
1734+ short pos0 = (short)stringTable.length();
1735+ stringTable.append(s);
1736+ short pos1 = (short)stringTable.length();
1737+
1738+ stringIDs[stringIDNum] = pos0;
1739+ stringIDs[stringIDNum + 1] = pos1;
1740+ stringIDNum++;
1741+ if (stringIDNum + 1 >= stringIDs.length) {
1742+ short[] tmp = new short[stringIDNum + 1000];
1743+ System.arraycopy(stringIDs, 0, tmp, 0, stringIDNum);
1744+ stringIDs = tmp;
1745+ }
1746+ return (short)(stringIDNum - 1);
1747+ }
1748+
1749+ private static short getShortArrayID(short sa[]) {
1750+ char[] cc = new char[sa.length];
1751+ for (int i = 0; i < sa.length; i ++) {
1752+ cc[i] = (char)sa[i];
1753+ }
1754+ String s = new String(cc);
1755+ return getStringID(s);
1756+ }
1757+
1758+ //utility "empty" objects
1759+ private static final int[] EMPTY_INT_ARRAY = new int[0];
1760+ private static final String[] EMPTY_STRING_ARRAY = new String[0];
1761+ private static final short[] EMPTY_SHORT_ARRAY = new short[0];
1762+ private static final String UNDEFINED_COMPONENT_FONT = "unknown";
1763+
1764+ //////////////////////////////////////////////////////////////////////////
1765+ //Convert the FontConfig data in Properties file to binary data tables //
1766+ //////////////////////////////////////////////////////////////////////////
1767+ static class PropertiesHandler {
1768+ public void load(InputStream in) throws IOException {
1769+ initLogicalNameStyle();
1770+ initHashMaps();
1771+ FontProperties fp = new FontProperties();
1772+ fp.load(in);
1773+ initBinaryTable();
1774+ }
1775+
1776+ private void initBinaryTable() {
1777+ //(0)
1778+ head = new short[HEAD_LENGTH];
1779+ head[INDEX_scriptIDs] = (short)HEAD_LENGTH;
1780+
1781+ table_scriptIDs = toList(scriptIDs);
1782+ //(1)a: scriptAllfonts scriptID/allfonts -> componentFontNameID
1783+ // b: scriptFonts scriptID -> componentFontNameID[20]
1784+ //if we have a "allfonts.script" def, then we just put
1785+ //the "-platformFontID" value in the slot, otherwise the slot
1786+ //value is "offset" which "offset" is where 20 entries located
1787+ //in the table attached.
1788+ head[INDEX_scriptFonts] = (short)(head[INDEX_scriptIDs] + table_scriptIDs.length);
1789+ int len = table_scriptIDs.length + scriptFonts.size() * 20;
1790+ table_scriptFonts = new short[len];
1791+
1792+ for (Entry<Short, Short> entry : scriptAllfonts.entrySet()) {
1793+ table_scriptFonts[entry.getKey().intValue()] = entry.getValue();
1794+ }
1795+ int off = table_scriptIDs.length;
1796+ for (Entry<Short, Short[]> entry : scriptFonts.entrySet()) {
1797+ table_scriptFonts[entry.getKey().intValue()] = (short)-off;
1798+ Short[] v = entry.getValue();
1799+ for (int i = 0; i < 20; i++) {
1800+ if (v[i] != null) {
1801+ table_scriptFonts[off++] = v[i];
1802+ } else {
1803+ table_scriptFonts[off++] = 0;
1804+ }
1805+ }
1806+ }
1807+
1808+ //(2)
1809+ head[INDEX_elcIDs] = (short)(head[INDEX_scriptFonts] + table_scriptFonts.length);
1810+ table_elcIDs = toList(elcIDs);
1811+
1812+ //(3) sequences elcID -> XXXX[1|5] -> scriptID[]
1813+ head[INDEX_sequences] = (short)(head[INDEX_elcIDs] + table_elcIDs.length);
1814+ table_sequences = new short[elcIDs.size() * NUM_FONTS];
1815+ for (Entry<Short, short[]> entry : sequences.entrySet()) {
1816+ //table_sequences[entry.getKey().intValue()] = (short)-off;
1817+ int k = entry.getKey().intValue();
1818+ short[] v = entry.getValue();
1819+ /*
1820+ System.out.println("elc=" + k + "/" + getString((short)table_elcIDs[k]));
1821+ short[] ss = getShortArray(v[0]);
1822+ for (int i = 0; i < ss.length; i++) {
1823+ System.out.println(" " + getString((short)table_scriptIDs[ss[i]]));
1824+ }
1825+ */
1826+ if (v.length == 1) {
1827+ //the "allfonts" entries
1828+ for (int i = 0; i < NUM_FONTS; i++) {
1829+ table_sequences[k * NUM_FONTS + i] = v[0];
1830+ }
1831+ } else {
1832+ for (int i = 0; i < NUM_FONTS; i++) {
1833+ table_sequences[k * NUM_FONTS + i] = v[i];
1834+ }
1835+ }
1836+ }
1837+ //(4)
1838+ head[INDEX_fontfileNameIDs] = (short)(head[INDEX_sequences] + table_sequences.length);
1839+ table_fontfileNameIDs = toList(fontfileNameIDs);
1840+
1841+ //(5)
1842+ head[INDEX_componentFontNameIDs] = (short)(head[INDEX_fontfileNameIDs] + table_fontfileNameIDs.length);
1843+ table_componentFontNameIDs = toList(componentFontNameIDs);
1844+
1845+ //(6)componentFontNameID -> filenameID
1846+ head[INDEX_filenames] = (short)(head[INDEX_componentFontNameIDs] + table_componentFontNameIDs.length);
1847+ table_filenames = new short[table_componentFontNameIDs.length];
1848+ Arrays.fill(table_filenames, (short) -1);
1849+
1850+ for (Entry<Short, Short> entry : filenames.entrySet()) {
1851+ table_filenames[entry.getKey()] = entry.getValue();
1852+ }
1853+
1854+ //(7)scriptID-> awtfontpath
1855+ //the paths are stored as scriptID -> stringID in awtfontpahts
1856+ head[INDEX_awtfontpaths] = (short)(head[INDEX_filenames] + table_filenames.length);
1857+ table_awtfontpaths = new short[table_scriptIDs.length];
1858+ for (Entry<Short, Short> entry : awtfontpaths.entrySet()) {
1859+ table_awtfontpaths[entry.getKey()] = entry.getValue();
1860+ }
1861+
1862+ //(8)exclusions
1863+ head[INDEX_exclusions] = (short)(head[INDEX_awtfontpaths] + table_awtfontpaths.length);
1864+ table_exclusions = new short[scriptIDs.size()];
1865+ for (Entry<Short, int[]> entry : exclusions.entrySet()) {
1866+ int[] exI = entry.getValue();
1867+ char[] exC = new char[exI.length * 2];
1868+ int j = 0;
1869+ for (int i = 0; i < exI.length; i++) {
1870+ exC[j++] = (char) (exI[i] >> 16);
1871+ exC[j++] = (char) (exI[i] & 0xffff);
1872+ }
1873+ table_exclusions[entry.getKey()] = getStringID(new String (exC));
1874+ }
1875+ //(9)proportionals
1876+ head[INDEX_proportionals] = (short)(head[INDEX_exclusions] + table_exclusions.length);
1877+ table_proportionals = new short[proportionals.size() * 2];
1878+ int j = 0;
1879+ for (Entry<Short, Short> entry : proportionals.entrySet()) {
1880+ table_proportionals[j++] = entry.getKey();
1881+ table_proportionals[j++] = entry.getValue();
1882+ }
1883+
1884+ //(10) see (1) for info, the only difference is "xxx.motif"
1885+ head[INDEX_scriptFontsMotif] = (short)(head[INDEX_proportionals] + table_proportionals.length);
1886+ if (scriptAllfontsMotif.size() != 0 || scriptFontsMotif.size() != 0) {
1887+ len = table_scriptIDs.length + scriptFontsMotif.size() * 20;
1888+ table_scriptFontsMotif = new short[len];
1889+
1890+ for (Entry<Short, Short> entry : scriptAllfontsMotif.entrySet()) {
1891+ table_scriptFontsMotif[entry.getKey().intValue()] =
1892+ (short)entry.getValue();
1893+ }
1894+ off = table_scriptIDs.length;
1895+ for (Entry<Short, Short[]> entry : scriptFontsMotif.entrySet()) {
1896+ table_scriptFontsMotif[entry.getKey().intValue()] = (short)-off;
1897+ Short[] v = entry.getValue();
1898+ int i = 0;
1899+ while (i < 20) {
1900+ if (v[i] != null) {
1901+ table_scriptFontsMotif[off++] = v[i];
1902+ } else {
1903+ table_scriptFontsMotif[off++] = 0;
1904+ }
1905+ i++;
1906+ }
1907+ }
1908+ } else {
1909+ table_scriptFontsMotif = EMPTY_SHORT_ARRAY;
1910+ }
1911+
1912+ //(11)short[] alphabeticSuffix
1913+ head[INDEX_alphabeticSuffix] = (short)(head[INDEX_scriptFontsMotif] + table_scriptFontsMotif.length);
1914+ table_alphabeticSuffix = new short[alphabeticSuffix.size() * 2];
1915+ j = 0;
1916+ for (Entry<Short, Short> entry : alphabeticSuffix.entrySet()) {
1917+ table_alphabeticSuffix[j++] = entry.getKey();
1918+ table_alphabeticSuffix[j++] = entry.getValue();
1919+ }
1920+
1921+ //(15)short[] fallbackScriptIDs; just put the ID in head
1922+ head[INDEX_fallbackScripts] = getShortArrayID(fallbackScriptIDs);
1923+
1924+ //(16)appendedfontpath
1925+ head[INDEX_appendedfontpath] = getStringID(appendedfontpath);
1926+
1927+ //(17)version
1928+ head[INDEX_version] = getStringID(version);
1929+
1930+ //(12)short[] StringIDs
1931+ head[INDEX_stringIDs] = (short)(head[INDEX_alphabeticSuffix] + table_alphabeticSuffix.length);
1932+ table_stringIDs = new short[stringIDNum + 1];
1933+ System.arraycopy(stringIDs, 0, table_stringIDs, 0, stringIDNum + 1);
1934+
1935+ //(13)StringTable
1936+ head[INDEX_stringTable] = (short)(head[INDEX_stringIDs] + stringIDNum + 1);
1937+ table_stringTable = stringTable.toString().toCharArray();
1938+ //(14)
1939+ head[INDEX_TABLEEND] = (short)(head[INDEX_stringTable] + stringTable.length());
1940+
1941+ //StringTable cache
1942+ stringCache = new String[table_stringIDs.length];
1943+ }
1944+
1945+ //////////////////////////////////////////////
1946+ private HashMap<String, Short> scriptIDs;
1947+ //elc -> Encoding.Language.Country
1948+ private HashMap<String, Short> elcIDs;
1949+ //componentFontNameID starts from "1", "0" reserves for "undefined"
1950+ private HashMap<String, Short> componentFontNameIDs;
1951+ private HashMap<String, Short> fontfileNameIDs;
1952+ private HashMap<String, Integer> logicalFontIDs;
1953+ private HashMap<String, Integer> fontStyleIDs;
1954+
1955+ //componentFontNameID -> fontfileNameID
1956+ private HashMap<Short, Short> filenames;
1957+
1958+ //elcID -> allfonts/logicalFont -> scriptID list
1959+ //(1)if we have a "allfonts", then the length of the
1960+ // value array is "1", otherwise it's 5, each font
1961+ // must have their own individual entry.
1962+ //scriptID list "short[]" is stored as an ID
1963+ private HashMap<Short, short[]> sequences;
1964+
1965+ //scriptID ->logicFontID/fontStyleID->componentFontNameID,
1966+ //a 20-entry array (5-name x 4-style) for each script
1967+ private HashMap<Short, Short[]> scriptFonts;
1968+
1969+ //scriptID -> componentFontNameID
1970+ private HashMap<Short, Short> scriptAllfonts;
1971+
1972+ //scriptID -> exclusionRanges[]
1973+ private HashMap<Short, int[]> exclusions;
1974+
1975+ //scriptID -> fontpath
1976+ private HashMap<Short, Short> awtfontpaths;
1977+
1978+ //fontID -> fontID
1979+ private HashMap<Short, Short> proportionals;
1980+
1981+ //scriptID -> componentFontNameID
1982+ private HashMap<Short, Short> scriptAllfontsMotif;
1983+
1984+ //scriptID ->logicFontID/fontStyleID->componentFontNameID,
1985+ private HashMap<Short, Short[]> scriptFontsMotif;
1986+
1987+ //elcID -> stringID of alphabetic/XXXX
1988+ private HashMap<Short, Short> alphabeticSuffix;
1989+
1990+ private short[] fallbackScriptIDs;
1991+ private String version;
1992+ private String appendedfontpath;
1993+
1994+ private void initLogicalNameStyle() {
1995+ logicalFontIDs = new HashMap<String, Integer>();
1996+ fontStyleIDs = new HashMap<String, Integer>();
1997+ logicalFontIDs.put("serif", 0);
1998+ logicalFontIDs.put("sansserif", 1);
1999+ logicalFontIDs.put("monospaced", 2);
2000+ logicalFontIDs.put("dialog", 3);
2001+ logicalFontIDs.put("dialoginput",4);
2002+ fontStyleIDs.put("plain", 0);
2003+ fontStyleIDs.put("bold", 1);
2004+ fontStyleIDs.put("italic", 2);
2005+ fontStyleIDs.put("bolditalic", 3);
2006+ }
2007+
2008+ private void initHashMaps() {
2009+ scriptIDs = new HashMap<String, Short>();
2010+ elcIDs = new HashMap<String, Short>();
2011+ componentFontNameIDs = new HashMap<String, Short>();
2012+ /*Init these tables to allow componentFontNameID, fontfileNameIDs
2013+ to start from "1".
2014+ */
2015+ componentFontNameIDs.put("", Short.valueOf((short)0));
2016+
2017+ fontfileNameIDs = new HashMap<String, Short>();
2018+ filenames = new HashMap<Short, Short>();
2019+ sequences = new HashMap<Short, short[]>();
2020+ scriptFonts = new HashMap<Short, Short[]>();
2021+ scriptAllfonts = new HashMap<Short, Short>();
2022+ exclusions = new HashMap<Short, int[]>();
2023+ awtfontpaths = new HashMap<Short, Short>();
2024+ proportionals = new HashMap<Short, Short>();
2025+ scriptFontsMotif = new HashMap<Short, Short[]>();
2026+ scriptAllfontsMotif = new HashMap<Short, Short>();
2027+ alphabeticSuffix = new HashMap<Short, Short>();
2028+ fallbackScriptIDs = EMPTY_SHORT_ARRAY;
2029+ /*
2030+ version
2031+ appendedfontpath
2032+ */
2033+ }
2034+
2035+ private int[] parseExclusions(String key, String exclusions) {
2036+ if (exclusions == null) {
2037+ return EMPTY_INT_ARRAY;
2038+ }
2039+ // range format is xxxx-XXXX,yyyyyy-YYYYYY,.....
2040+ int numExclusions = 1;
2041+ int pos = 0;
2042+ while ((pos = exclusions.indexOf(',', pos)) != -1) {
2043+ numExclusions++;
2044+ pos++;
2045+ }
2046+ int[] exclusionRanges = new int[numExclusions * 2];
2047+ pos = 0;
2048+ int newPos = 0;
2049+ for (int j = 0; j < numExclusions * 2; ) {
2050+ String lower, upper;
2051+ int lo = 0, up = 0;
2052+ try {
2053+ newPos = exclusions.indexOf('-', pos);
2054+ lower = exclusions.substring(pos, newPos);
2055+ pos = newPos + 1;
2056+ newPos = exclusions.indexOf(',', pos);
2057+ if (newPos == -1) {
2058+ newPos = exclusions.length();
2059+ }
2060+ upper = exclusions.substring(pos, newPos);
2061+ pos = newPos + 1;
2062+ int lowerLength = lower.length();
2063+ int upperLength = upper.length();
2064+ if (lowerLength != 4 && lowerLength != 6
2065+ || upperLength != 4 && upperLength != 6) {
2066+ throw new Exception();
2067+ }
2068+ lo = Integer.parseInt(lower, 16);
2069+ up = Integer.parseInt(upper, 16);
2070+ if (lo > up) {
2071+ throw new Exception();
2072+ }
2073+ } catch (Exception e) {
2074+ if (FontUtilities.debugFonts() &&
2075+ logger != null) {
2076+ logger.config("Failed parsing " + key +
2077+ " property of font configuration.");
2078+
2079+ }
2080+ return EMPTY_INT_ARRAY;
2081+ }
2082+ exclusionRanges[j++] = lo;
2083+ exclusionRanges[j++] = up;
2084+ }
2085+ return exclusionRanges;
2086+ }
2087+
2088+ private Short getID(HashMap<String, Short> map, String key) {
2089+ Short ret = map.get(key);
2090+ if ( ret == null) {
2091+ map.put(key, (short)map.size());
2092+ return map.get(key);
2093+ }
2094+ return ret;
2095+ }
2096+
2097+ class FontProperties extends Properties {
2098+ public synchronized Object put(Object k, Object v) {
2099+ parseProperty((String)k, (String)v);
2100+ return null;
2101+ }
2102+ }
2103+
2104+ private void parseProperty(String key, String value) {
2105+ if (key.startsWith("filename.")) {
2106+ //the only special case is "MingLiu_HKSCS" which has "_" in its
2107+ //facename, we dont want to replace the "_" with " "
2108+ key = key.substring(9);
2109+ if (!"MingLiU_HKSCS".equals(key)) {
2110+ key = key.replace('_', ' ');
2111+ }
2112+ Short faceID = getID(componentFontNameIDs, key);
2113+ Short fileID = getID(fontfileNameIDs, value);
2114+ //System.out.println("faceID=" + faceID + "/" + key + " -> "
2115+ // + "fileID=" + fileID + "/" + value);
2116+ filenames.put(faceID, fileID);
2117+ } else if (key.startsWith("exclusion.")) {
2118+ key = key.substring(10);
2119+ exclusions.put(getID(scriptIDs,key), parseExclusions(key,value));
2120+ } else if (key.startsWith("sequence.")) {
2121+ key = key.substring(9);
2122+ boolean hasDefault = false;
2123+ boolean has1252 = false;
2124+
2125+ //get the scriptID list
2126+ String[] ss = (String[])splitSequence(value).toArray(EMPTY_STRING_ARRAY);
2127+ short [] sa = new short[ss.length];
2128+ for (int i = 0; i < ss.length; i++) {
2129+ if ("alphabetic/default".equals(ss[i])) {
2130+ //System.out.println(key + " -> " + ss[i]);
2131+ ss[i] = "alphabetic";
2132+ hasDefault = true;
2133+ } else if ("alphabetic/1252".equals(ss[i])) {
2134+ //System.out.println(key + " -> " + ss[i]);
2135+ ss[i] = "alphabetic";
2136+ has1252 = true;
2137+ }
2138+ sa[i] = getID(scriptIDs, ss[i]).shortValue();
2139+ //System.out.println("scriptID=" + si[i] + "/" + ss[i]);
2140+ }
2141+ //convert the "short[] -> string -> stringID"
2142+ short scriptArrayID = getShortArrayID(sa);
2143+ Short elcID = null;
2144+ int dot = key.indexOf('.');
2145+ if (dot == -1) {
2146+ if ("fallback".equals(key)) {
2147+ fallbackScriptIDs = sa;
2148+ return;
2149+ }
2150+ if ("allfonts".equals(key)) {
2151+ elcID = getID(elcIDs, "NULL.NULL.NULL");
2152+ } else {
2153+ if (logger != null) {
2154+ logger.config("Error sequence def: <sequence." + key + ">");
2155+ }
2156+ return;
2157+ }
2158+ } else {
2159+ elcID = getID(elcIDs, key.substring(dot + 1));
2160+ //System.out.println("elcID=" + elcID + "/" + key.substring(dot + 1));
2161+ key = key.substring(0, dot);
2162+ }
2163+ short[] scriptArrayIDs = null;
2164+ if ("allfonts".equals(key)) {
2165+ scriptArrayIDs = new short[1];
2166+ scriptArrayIDs[0] = scriptArrayID;
2167+ } else {
2168+ scriptArrayIDs = sequences.get(elcID);
2169+ if (scriptArrayIDs == null) {
2170+ scriptArrayIDs = new short[5];
2171+ }
2172+ Integer fid = logicalFontIDs.get(key);
2173+ if (fid == null) {
2174+ if (logger != null) {
2175+ logger.config("Unrecognizable logicfont name " + key);
2176+ }
2177+ return;
2178+ }
2179+ //System.out.println("sequence." + key + "/" + id);
2180+ scriptArrayIDs[fid.intValue()] = scriptArrayID;
2181+ }
2182+ sequences.put(elcID, scriptArrayIDs);
2183+ if (hasDefault) {
2184+ alphabeticSuffix.put(elcID, getStringID("default"));
2185+ } else
2186+ if (has1252) {
2187+ alphabeticSuffix.put(elcID, getStringID("1252"));
2188+ }
2189+ } else if (key.startsWith("allfonts.")) {
2190+ key = key.substring(9);
2191+ if (key.endsWith(".motif")) {
2192+ key = key.substring(0, key.length() - 6);
2193+ //System.out.println("motif: all." + key + "=" + value);
2194+ scriptAllfontsMotif.put(getID(scriptIDs,key), getID(componentFontNameIDs,value));
2195+ } else {
2196+ scriptAllfonts.put(getID(scriptIDs,key), getID(componentFontNameIDs,value));
2197+ }
2198+ } else if (key.startsWith("awtfontpath.")) {
2199+ key = key.substring(12);
2200+ //System.out.println("scriptID=" + getID(scriptIDs, key) + "/" + key);
2201+ awtfontpaths.put(getID(scriptIDs, key), getStringID(value));
2202+ } else if ("version".equals(key)) {
2203+ version = value;
2204+ } else if ("appendedfontpath".equals(key)) {
2205+ appendedfontpath = value;
2206+ } else if (key.startsWith("proportional.")) {
2207+ key = key.substring(13).replace('_', ' ');
2208+ //System.out.println(key + "=" + value);
2209+ proportionals.put(getID(componentFontNameIDs, key),
2210+ getID(componentFontNameIDs, value));
2211+ } else {
2212+ //"name.style.script(.motif)", we dont care anything else
2213+ int dot1, dot2;
2214+ boolean isMotif = false;
2215+
2216+ dot1 = key.indexOf('.');
2217+ if (dot1 == -1) {
2218+ if (logger != null) {
2219+ logger.config("Failed parsing " + key +
2220+ " property of font configuration.");
2221+
2222+ }
2223+ return;
2224+ }
2225+ dot2 = key.indexOf('.', dot1 + 1);
2226+ if (dot2 == -1) {
2227+ if (logger != null) {
2228+ logger.config("Failed parsing " + key +
2229+ " property of font configuration.");
2230+
2231+ }
2232+ return;
2233+ }
2234+ if (key.endsWith(".motif")) {
2235+ key = key.substring(0, key.length() - 6);
2236+ isMotif = true;
2237+ //System.out.println("motif: " + key + "=" + value);
2238+ }
2239+ Integer nameID = logicalFontIDs.get(key.substring(0, dot1));
2240+ Integer styleID = fontStyleIDs.get(key.substring(dot1+1, dot2));
2241+ Short scriptID = getID(scriptIDs, key.substring(dot2 + 1));
2242+ if (nameID == null || styleID == null) {
2243+ if (logger != null) {
2244+ logger.config("unrecognizable logicfont name/style at " + key);
2245+ }
2246+ return;
2247+ }
2248+ Short[] pnids;
2249+ if (isMotif) {
2250+ pnids = scriptFontsMotif.get(scriptID);
2251+ } else {
2252+ pnids = scriptFonts.get(scriptID);
2253+ }
2254+ if (pnids == null) {
2255+ pnids = new Short[20];
2256+ }
2257+ pnids[nameID.intValue() * NUM_STYLES + styleID.intValue()]
2258+ = getID(componentFontNameIDs, value);
2259+ /*
2260+ System.out.println("key=" + key + "/<" + nameID + "><" + styleID
2261+ + "><" + scriptID + ">=" + value
2262+ + "/" + getID(componentFontNameIDs, value));
2263+ */
2264+ if (isMotif) {
2265+ scriptFontsMotif.put(scriptID, pnids);
2266+ } else {
2267+ scriptFonts.put(scriptID, pnids);
2268+ }
2269+ }
2270+ }
2271+ }
2272+}