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