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
26package sun.awt;
27
28import java.awt.Font;
29import java.io.DataInputStream;
30import java.io.DataOutputStream;
31import java.io.File;
32import java.io.FileInputStream;
33import java.io.InputStream;
34import java.io.IOException;
35import java.io.OutputStream;
36import java.nio.charset.Charset;
37import java.nio.charset.CharsetEncoder;
38import java.security.AccessController;
39import java.security.PrivilegedAction;
40import java.util.Arrays;
41import java.util.HashMap;
42import java.util.HashSet;
43import java.util.Hashtable;
44import java.util.Locale;
45import java.util.Map.Entry;
46import java.util.Properties;
47import java.util.Set;
48import java.util.Vector;
49import sun.font.CompositeFontDescriptor;
50import sun.font.SunFontManager;
51import sun.font.FontManagerFactory;
52import sun.font.FontUtilities;
53import 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 */
60public 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}