Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/jdk17u
Path: blob/master/src/java.desktop/share/classes/sun/awt/FontConfiguration.java
66645 views
1
/*
2
* Copyright (c) 1996, 2022, 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<String, String> 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.logInfo("Creating standard Font Configuration");
87
}
88
if (FontUtilities.debugFonts() && logger == null) {
89
logger = PlatformLogger.getLogger("sun.awt.FontConfiguration");
90
}
91
fontManager = fm;
92
setOsNameAndVersion(); /* static initialization */
93
setEncoding(); /* static initialization */
94
/* Separating out the file location from the rest of the
95
* initialisation, so the caller has the option of doing
96
* something else if a suitable file isn't found.
97
*/
98
findFontConfigFile();
99
}
100
101
public synchronized boolean init() {
102
if (!inited) {
103
this.preferLocaleFonts = false;
104
this.preferPropFonts = false;
105
setFontConfiguration();
106
readFontConfigFile(fontConfigFile);
107
initFontConfig();
108
inited = true;
109
}
110
return true;
111
}
112
113
public FontConfiguration(SunFontManager fm,
114
boolean preferLocaleFonts,
115
boolean preferPropFonts) {
116
fontManager = fm;
117
if (FontUtilities.debugFonts()) {
118
FontUtilities.logInfo("Creating alternate Font Configuration");
119
}
120
this.preferLocaleFonts = preferLocaleFonts;
121
this.preferPropFonts = preferPropFonts;
122
/* fontConfig should be initialised by default constructor, and
123
* its data tables can be shared, since readFontConfigFile doesn't
124
* update any other state. Also avoid a doPrivileged block.
125
*/
126
initFontConfig();
127
}
128
129
/**
130
* Fills in this instance's osVersion and osName members. By
131
* default uses the system properties os.name and os.version;
132
* subclasses may override.
133
*/
134
protected void setOsNameAndVersion() {
135
osName = System.getProperty("os.name");
136
osVersion = System.getProperty("os.version");
137
}
138
139
private void setEncoding() {
140
encoding = Charset.defaultCharset().name();
141
startupLocale = SunToolkit.getStartupLocale();
142
}
143
144
/////////////////////////////////////////////////////////////////////
145
// methods for loading the FontConfig file //
146
/////////////////////////////////////////////////////////////////////
147
148
public boolean foundOsSpecificFile() {
149
return foundOsSpecificFile;
150
}
151
152
/* Smoke test to see if we can trust this configuration by testing if
153
* the first slot of a composite font maps to an installed file.
154
*/
155
public boolean fontFilesArePresent() {
156
init();
157
short fontNameID = compFontNameIDs[0][0][0];
158
short fileNameID = getComponentFileID(fontNameID);
159
final String fileName = mapFileName(getComponentFileName(fileNameID));
160
@SuppressWarnings("removal")
161
Boolean exists = java.security.AccessController.doPrivileged(
162
new java.security.PrivilegedAction<Boolean>() {
163
public Boolean run() {
164
try {
165
File f = new File(fileName);
166
return Boolean.valueOf(f.exists());
167
}
168
catch (Exception e) {
169
return Boolean.FALSE;
170
}
171
}
172
});
173
return exists.booleanValue();
174
}
175
176
private void findFontConfigFile() {
177
178
foundOsSpecificFile = true; // default assumption.
179
String javaHome = System.getProperty("java.home");
180
if (javaHome == null) {
181
throw new Error("java.home property not set");
182
}
183
javaLib = javaHome + File.separator + "lib";
184
String javaConfFonts = javaHome +
185
File.separator + "conf" +
186
File.separator + "fonts";
187
String userConfigFile = System.getProperty("sun.awt.fontconfig");
188
if (userConfigFile != null) {
189
fontConfigFile = new File(userConfigFile);
190
} else {
191
fontConfigFile = findFontConfigFile(javaConfFonts);
192
if (fontConfigFile == null) {
193
fontConfigFile = findFontConfigFile(javaLib);
194
}
195
}
196
}
197
198
private void readFontConfigFile(File f) {
199
/* This is invoked here as readFontConfigFile is only invoked
200
* once per VM, and always in a privileged context, thus the
201
* directory containing installed fall back fonts is accessed
202
* from this context
203
*/
204
getInstalledFallbackFonts(javaLib);
205
206
if (f != null) {
207
try {
208
FileInputStream in = new FileInputStream(f.getPath());
209
if (isProperties) {
210
loadProperties(in);
211
} else {
212
loadBinary(in);
213
}
214
in.close();
215
if (FontUtilities.debugFonts()) {
216
logger.config("Read logical font configuration from " + f);
217
}
218
} catch (IOException e) {
219
if (FontUtilities.debugFonts()) {
220
logger.config("Failed to read logical font configuration from " + f);
221
}
222
}
223
}
224
String version = getVersion();
225
if (!"1".equals(version) && FontUtilities.debugFonts()) {
226
logger.config("Unsupported fontconfig version: " + version);
227
}
228
}
229
230
protected void getInstalledFallbackFonts(String javaLib) {
231
String fallbackDirName = javaLib + File.separator +
232
"fonts" + File.separator + "fallback";
233
234
File fallbackDir = new File(fallbackDirName);
235
if (fallbackDir.exists() && fallbackDir.isDirectory()) {
236
String[] ttfs = fallbackDir.list(fontManager.getTrueTypeFilter());
237
String[] t1s = fallbackDir.list(fontManager.getType1Filter());
238
int numTTFs = (ttfs == null) ? 0 : ttfs.length;
239
int numT1s = (t1s == null) ? 0 : t1s.length;
240
int len = numTTFs + numT1s;
241
if (numTTFs + numT1s == 0) {
242
return;
243
}
244
installedFallbackFontFiles = new String[len];
245
for (int i=0; i<numTTFs; i++) {
246
installedFallbackFontFiles[i] =
247
fallbackDir + File.separator + ttfs[i];
248
}
249
for (int i=0; i<numT1s; i++) {
250
installedFallbackFontFiles[i+numTTFs] =
251
fallbackDir + File.separator + t1s[i];
252
}
253
fontManager.registerFontsInDir(fallbackDirName);
254
}
255
}
256
257
private File findImpl(String fname) {
258
File f = new File(fname + ".properties");
259
if (FontUtilities.debugFonts()) {
260
logger.info("Looking for text fontconfig file : " + f);
261
}
262
if (f.canRead()) {
263
if (FontUtilities.debugFonts()) {
264
logger.info("Found file : " + f);
265
}
266
isProperties = true;
267
return f;
268
}
269
f = new File(fname + ".bfc");
270
if (FontUtilities.debugFonts()) {
271
logger.info("Looking for binary fontconfig file : " + f);
272
}
273
if (f.canRead()) {
274
if (FontUtilities.debugFonts()) {
275
logger.info("Found file : " + f);
276
}
277
isProperties = false;
278
return f;
279
}
280
return null;
281
}
282
283
private File findFontConfigFile(String dir) {
284
if (!(new File(dir)).exists()) {
285
return null;
286
}
287
String baseName = dir + File.separator + "fontconfig";
288
File configFile;
289
String osMajorVersion = null;
290
if (osVersion != null && osName != null) {
291
configFile = findImpl(baseName + "." + osName + "." + osVersion);
292
if (configFile != null) {
293
return configFile;
294
}
295
int decimalPointIndex = osVersion.indexOf('.');
296
if (decimalPointIndex != -1) {
297
osMajorVersion = osVersion.substring(0, osVersion.indexOf('.'));
298
configFile = findImpl(baseName + "." + osName + "." + osMajorVersion);
299
if (configFile != null) {
300
return configFile;
301
}
302
}
303
}
304
if (osName != null) {
305
configFile = findImpl(baseName + "." + osName);
306
if (configFile != null) {
307
return configFile;
308
}
309
}
310
if (osVersion != null) {
311
configFile = findImpl(baseName + "." + osVersion);
312
if (configFile != null) {
313
return configFile;
314
}
315
if (osMajorVersion != null) {
316
configFile = findImpl(baseName + "." + osMajorVersion);
317
if (configFile != null) {
318
return configFile;
319
}
320
}
321
}
322
foundOsSpecificFile = false;
323
324
configFile = findImpl(baseName);
325
if (configFile != null) {
326
return configFile;
327
}
328
if (FontUtilities.debugFonts()) {
329
logger.info("Did not find a fontconfig file.");
330
}
331
return null;
332
}
333
334
/* Initialize the internal data tables from binary format font
335
* configuration file.
336
*/
337
public static void loadBinary(InputStream inStream) throws IOException {
338
DataInputStream in = new DataInputStream(inStream);
339
head = readShortTable(in, HEAD_LENGTH);
340
int[] tableSizes = new int[INDEX_TABLEEND];
341
for (int i = 0; i < INDEX_TABLEEND; i++) {
342
tableSizes[i] = head[i + 1] - head[i];
343
}
344
table_scriptIDs = readShortTable(in, tableSizes[INDEX_scriptIDs]);
345
table_scriptFonts = readShortTable(in, tableSizes[INDEX_scriptFonts]);
346
table_elcIDs = readShortTable(in, tableSizes[INDEX_elcIDs]);
347
table_sequences = readShortTable(in, tableSizes[INDEX_sequences]);
348
table_fontfileNameIDs = readShortTable(in, tableSizes[INDEX_fontfileNameIDs]);
349
table_componentFontNameIDs = readShortTable(in, tableSizes[INDEX_componentFontNameIDs]);
350
table_filenames = readShortTable(in, tableSizes[INDEX_filenames]);
351
table_awtfontpaths = readShortTable(in, tableSizes[INDEX_awtfontpaths]);
352
table_exclusions = readShortTable(in, tableSizes[INDEX_exclusions]);
353
table_proportionals = readShortTable(in, tableSizes[INDEX_proportionals]);
354
table_scriptFontsMotif = readShortTable(in, tableSizes[INDEX_scriptFontsMotif]);
355
table_alphabeticSuffix = readShortTable(in, tableSizes[INDEX_alphabeticSuffix]);
356
table_stringIDs = readShortTable(in, tableSizes[INDEX_stringIDs]);
357
358
//StringTable cache
359
stringCache = new String[table_stringIDs.length + 1];
360
361
int len = tableSizes[INDEX_stringTable];
362
byte[] bb = new byte[len * 2];
363
table_stringTable = new char[len];
364
in.read(bb);
365
int i = 0, j = 0;
366
while (i < len) {
367
table_stringTable[i++] = (char)(bb[j++] << 8 | (bb[j++] & 0xff));
368
}
369
if (verbose) {
370
dump();
371
}
372
}
373
374
/* Generate a binary format font configuration from internal data
375
* tables.
376
*/
377
public static void saveBinary(OutputStream out) throws IOException {
378
sanityCheck();
379
380
DataOutputStream dataOut = new DataOutputStream(out);
381
writeShortTable(dataOut, head);
382
writeShortTable(dataOut, table_scriptIDs);
383
writeShortTable(dataOut, table_scriptFonts);
384
writeShortTable(dataOut, table_elcIDs);
385
writeShortTable(dataOut, table_sequences);
386
writeShortTable(dataOut, table_fontfileNameIDs);
387
writeShortTable(dataOut, table_componentFontNameIDs);
388
writeShortTable(dataOut, table_filenames);
389
writeShortTable(dataOut, table_awtfontpaths);
390
writeShortTable(dataOut, table_exclusions);
391
writeShortTable(dataOut, table_proportionals);
392
writeShortTable(dataOut, table_scriptFontsMotif);
393
writeShortTable(dataOut, table_alphabeticSuffix);
394
writeShortTable(dataOut, table_stringIDs);
395
//stringTable
396
dataOut.writeChars(new String(table_stringTable));
397
out.close();
398
if (verbose) {
399
dump();
400
}
401
}
402
403
//private static boolean loadingProperties;
404
private static short stringIDNum;
405
private static short[] stringIDs;
406
private static StringBuilder stringTable;
407
408
public static void loadProperties(InputStream in) throws IOException {
409
//loadingProperties = true;
410
//StringID starts from "1", "0" is reserved for "not defined"
411
stringIDNum = 1;
412
stringIDs = new short[1000];
413
stringTable = new StringBuilder(4096);
414
415
if (verbose && logger == null) {
416
logger = PlatformLogger.getLogger("sun.awt.FontConfiguration");
417
}
418
new PropertiesHandler().load(in);
419
420
//loadingProperties = false;
421
stringIDs = null;
422
stringTable = null;
423
}
424
425
426
/////////////////////////////////////////////////////////////////////
427
// methods for initializing the FontConfig //
428
/////////////////////////////////////////////////////////////////////
429
430
/**
431
* set initLocale, initEncoding and initELC for this FontConfig object
432
* currently we just simply use the startup locale and encoding
433
*/
434
private void initFontConfig() {
435
initLocale = startupLocale;
436
initEncoding = encoding;
437
if (preferLocaleFonts && !willReorderForStartupLocale()) {
438
preferLocaleFonts = false;
439
}
440
initELC = getInitELC();
441
initAllComponentFonts();
442
}
443
444
//"ELC" stands for "Encoding.Language.Country". This method returns
445
//the ID of the matched elc setting of "initLocale" in elcIDs table.
446
//If no match is found, it returns the default ID, which is
447
//"NULL.NULL.NULL" in elcIDs table.
448
private short getInitELC() {
449
if (initELC != -1) {
450
return initELC;
451
}
452
HashMap <String, Integer> elcIDs = new HashMap<String, Integer>();
453
for (int i = 0; i < table_elcIDs.length; i++) {
454
elcIDs.put(getString(table_elcIDs[i]), i);
455
}
456
String language = initLocale.getLanguage();
457
String country = initLocale.getCountry();
458
String elc;
459
if (elcIDs.containsKey(elc=initEncoding + "." + language + "." + country)
460
|| elcIDs.containsKey(elc=initEncoding + "." + language)
461
|| elcIDs.containsKey(elc=initEncoding)) {
462
initELC = elcIDs.get(elc).shortValue();
463
} else {
464
initELC = elcIDs.get("NULL.NULL.NULL").shortValue();
465
}
466
int i = 0;
467
while (i < table_alphabeticSuffix.length) {
468
if (initELC == table_alphabeticSuffix[i]) {
469
alphabeticSuffix = getString(table_alphabeticSuffix[i + 1]);
470
return initELC;
471
}
472
i += 2;
473
}
474
return initELC;
475
}
476
477
public static boolean verbose;
478
private short initELC = -1;
479
private Locale initLocale;
480
private String initEncoding;
481
private String alphabeticSuffix;
482
483
private short[][][] compFontNameIDs = new short[NUM_FONTS][NUM_STYLES][];
484
private int[][][] compExclusions = new int[NUM_FONTS][][];
485
private int[] compCoreNum = new int[NUM_FONTS];
486
487
private Set<Short> coreFontNameIDs = new HashSet<Short>();
488
private Set<Short> fallbackFontNameIDs = new HashSet<Short>();
489
490
private void initAllComponentFonts() {
491
short[] fallbackScripts = getFallbackScripts();
492
for (int fontIndex = 0; fontIndex < NUM_FONTS; fontIndex++) {
493
short[] coreScripts = getCoreScripts(fontIndex);
494
compCoreNum[fontIndex] = coreScripts.length;
495
/*
496
System.out.println("coreScriptID=" + table_sequences[initELC * 5 + fontIndex]);
497
for (int i = 0; i < coreScripts.length; i++) {
498
System.out.println(" " + i + " :" + getString(table_scriptIDs[coreScripts[i]]));
499
}
500
*/
501
//init exclusionRanges
502
int[][] exclusions = new int[coreScripts.length][];
503
for (int i = 0; i < coreScripts.length; i++) {
504
exclusions[i] = getExclusionRanges(coreScripts[i]);
505
}
506
compExclusions[fontIndex] = exclusions;
507
//init componentFontNames
508
for (int styleIndex = 0; styleIndex < NUM_STYLES; styleIndex++) {
509
int index;
510
short[] nameIDs = new short[coreScripts.length + fallbackScripts.length];
511
//core
512
for (index = 0; index < coreScripts.length; index++) {
513
nameIDs[index] = getComponentFontID(coreScripts[index],
514
fontIndex, styleIndex);
515
if (preferLocaleFonts && localeMap != null &&
516
fontManager.usingAlternateFontforJALocales()) {
517
nameIDs[index] = remapLocaleMap(fontIndex, styleIndex,
518
coreScripts[index], nameIDs[index]);
519
}
520
if (preferPropFonts) {
521
nameIDs[index] = remapProportional(fontIndex, nameIDs[index]);
522
}
523
//System.out.println("nameid=" + nameIDs[index]);
524
coreFontNameIDs.add(nameIDs[index]);
525
}
526
//fallback
527
for (int i = 0; i < fallbackScripts.length; i++) {
528
short id = getComponentFontID(fallbackScripts[i],
529
fontIndex, styleIndex);
530
if (preferLocaleFonts && localeMap != null &&
531
fontManager.usingAlternateFontforJALocales()) {
532
id = remapLocaleMap(fontIndex, styleIndex, fallbackScripts[i], id);
533
}
534
if (preferPropFonts) {
535
id = remapProportional(fontIndex, id);
536
}
537
if (contains(nameIDs, id, index)) {
538
continue;
539
}
540
/*
541
System.out.println("fontIndex=" + fontIndex + ", styleIndex=" + styleIndex
542
+ ", fbIndex=" + i + ",fbS=" + fallbackScripts[i] + ", id=" + id);
543
*/
544
fallbackFontNameIDs.add(id);
545
nameIDs[index++] = id;
546
}
547
if (index < nameIDs.length) {
548
short[] newNameIDs = new short[index];
549
System.arraycopy(nameIDs, 0, newNameIDs, 0, index);
550
nameIDs = newNameIDs;
551
}
552
compFontNameIDs[fontIndex][styleIndex] = nameIDs;
553
}
554
}
555
}
556
557
private short remapLocaleMap(int fontIndex, int styleIndex, short scriptID, short fontID) {
558
String scriptName = getString(table_scriptIDs[scriptID]);
559
560
String value = localeMap.get(scriptName);
561
if (value == null) {
562
String fontName = fontNames[fontIndex];
563
String styleName = styleNames[styleIndex];
564
value = localeMap.get(fontName + "." + styleName + "." + scriptName);
565
}
566
if (value == null) {
567
return fontID;
568
}
569
570
for (int i = 0; i < table_componentFontNameIDs.length; i++) {
571
String name = getString(table_componentFontNameIDs[i]);
572
if (value.equalsIgnoreCase(name)) {
573
fontID = (short)i;
574
break;
575
}
576
}
577
return fontID;
578
}
579
580
public static boolean hasMonoToPropMap() {
581
return table_proportionals != null && table_proportionals.length != 0;
582
}
583
584
private short remapProportional(int fontIndex, short id) {
585
if (preferPropFonts &&
586
table_proportionals.length != 0 &&
587
fontIndex != 2 && //"monospaced"
588
fontIndex != 4) { //"dialoginput"
589
int i = 0;
590
while (i < table_proportionals.length) {
591
if (table_proportionals[i] == id) {
592
return table_proportionals[i + 1];
593
}
594
i += 2;
595
}
596
}
597
return id;
598
}
599
600
/////////////////////////////////////////////////////////////////////
601
// Methods for handling font and style names //
602
/////////////////////////////////////////////////////////////////////
603
protected static final int NUM_FONTS = 5;
604
protected static final int NUM_STYLES = 4;
605
protected static final String[] fontNames
606
= {"serif", "sansserif", "monospaced", "dialog", "dialoginput"};
607
protected static final String[] publicFontNames
608
= {Font.SERIF, Font.SANS_SERIF, Font.MONOSPACED, Font.DIALOG,
609
Font.DIALOG_INPUT};
610
protected static final String[] styleNames
611
= {"plain", "bold", "italic", "bolditalic"};
612
613
/**
614
* Checks whether the given font family name is a valid logical font name.
615
* The check is case insensitive.
616
*/
617
public static boolean isLogicalFontFamilyName(String fontName) {
618
return isLogicalFontFamilyNameLC(fontName.toLowerCase(Locale.ENGLISH));
619
}
620
621
/**
622
* Checks whether the given font family name is a valid logical font name.
623
* The check is case sensitive.
624
*/
625
public static boolean isLogicalFontFamilyNameLC(String fontName) {
626
for (int i = 0; i < fontNames.length; i++) {
627
if (fontName.equals(fontNames[i])) {
628
return true;
629
}
630
}
631
return false;
632
}
633
634
/**
635
* Checks whether the given style name is a valid logical font style name.
636
*/
637
private static boolean isLogicalFontStyleName(String styleName) {
638
for (int i = 0; i < styleNames.length; i++) {
639
if (styleName.equals(styleNames[i])) {
640
return true;
641
}
642
}
643
return false;
644
}
645
646
/**
647
* Checks whether the given font face name is a valid logical font name.
648
* The check is case insensitive.
649
*/
650
public static boolean isLogicalFontFaceName(String fontName) {
651
return isLogicalFontFaceNameLC(fontName.toLowerCase(Locale.ENGLISH));
652
}
653
654
/**
655
* Checks whether the given font face name is a valid logical font name.
656
* The check is case sensitive.
657
*/
658
public static boolean isLogicalFontFaceNameLC(String fontName) {
659
int period = fontName.indexOf('.');
660
if (period >= 0) {
661
String familyName = fontName.substring(0, period);
662
String styleName = fontName.substring(period + 1);
663
return isLogicalFontFamilyName(familyName) &&
664
isLogicalFontStyleName(styleName);
665
} else {
666
return isLogicalFontFamilyName(fontName);
667
}
668
}
669
670
protected static int getFontIndex(String fontName) {
671
return getArrayIndex(fontNames, fontName);
672
}
673
674
protected static int getStyleIndex(String styleName) {
675
return getArrayIndex(styleNames, styleName);
676
}
677
678
private static int getArrayIndex(String[] names, String name) {
679
for (int i = 0; i < names.length; i++) {
680
if (name.equals(names[i])) {
681
return i;
682
}
683
}
684
assert false;
685
return 0;
686
}
687
688
protected static int getStyleIndex(int style) {
689
switch (style) {
690
case Font.PLAIN:
691
return 0;
692
case Font.BOLD:
693
return 1;
694
case Font.ITALIC:
695
return 2;
696
case Font.BOLD | Font.ITALIC:
697
return 3;
698
default:
699
return 0;
700
}
701
}
702
703
protected static String getFontName(int fontIndex) {
704
return fontNames[fontIndex];
705
}
706
707
protected static String getStyleName(int styleIndex) {
708
return styleNames[styleIndex];
709
}
710
711
/**
712
* Returns the font face name for the given logical font
713
* family name and style.
714
* The style argument is interpreted as in java.awt.Font.Font.
715
*/
716
public static String getLogicalFontFaceName(String familyName, int style) {
717
assert isLogicalFontFamilyName(familyName);
718
return familyName.toLowerCase(Locale.ENGLISH) + "." + getStyleString(style);
719
}
720
721
/**
722
* Returns the string typically used in properties files
723
* for the given style.
724
* The style argument is interpreted as in java.awt.Font.Font.
725
*/
726
public static String getStyleString(int style) {
727
return getStyleName(getStyleIndex(style));
728
}
729
730
/**
731
* Returns a fallback name for the given font name. For a few known
732
* font names, matching logical font names are returned. For all
733
* other font names, defaultFallback is returned.
734
* defaultFallback differs between AWT and 2D.
735
*/
736
public abstract String getFallbackFamilyName(String fontName, String defaultFallback);
737
738
/**
739
* Returns the 1.1 equivalent for some old 1.0 font family names for
740
* which we need to maintain compatibility in some configurations.
741
* Returns null for other font names.
742
*/
743
protected String getCompatibilityFamilyName(String fontName) {
744
fontName = fontName.toLowerCase(Locale.ENGLISH);
745
if (fontName.equals("timesroman")) {
746
return "serif";
747
} else if (fontName.equals("helvetica")) {
748
return "sansserif";
749
} else if (fontName.equals("courier")) {
750
return "monospaced";
751
}
752
return null;
753
}
754
755
protected static String[] installedFallbackFontFiles = null;
756
757
/**
758
* Maps a file name given in the font configuration file
759
* to a format appropriate for the platform.
760
*/
761
protected String mapFileName(String fileName) {
762
return fileName;
763
}
764
765
//////////////////////////////////////////////////////////////////////
766
// reordering //
767
//////////////////////////////////////////////////////////////////////
768
769
/* Mappings from file encoding to font config name for font supporting
770
* the corresponding language. This is filled in by initReorderMap()
771
*/
772
protected HashMap<String, Object> reorderMap = null;
773
774
/* Platform-specific mappings */
775
protected abstract void initReorderMap();
776
777
/* Move item at index "src" to "dst", shuffling all values in
778
* between down
779
*/
780
private void shuffle(String[] seq, int src, int dst) {
781
if (dst >= src) {
782
return;
783
}
784
String tmp = seq[src];
785
for (int i=src; i>dst; i--) {
786
seq[i] = seq[i-1];
787
}
788
seq[dst] = tmp;
789
}
790
791
/* Called to determine if there's a re-order sequence for this locale/
792
* encoding. If there's none then the caller can "bail" and avoid
793
* unnecessary work
794
*/
795
public static boolean willReorderForStartupLocale() {
796
return getReorderSequence() != null;
797
}
798
799
private static Object getReorderSequence() {
800
if (fontConfig.reorderMap == null) {
801
fontConfig.initReorderMap();
802
}
803
HashMap<String, Object> reorderMap = fontConfig.reorderMap;
804
805
/* Find the most specific mapping */
806
String language = startupLocale.getLanguage();
807
String country = startupLocale.getCountry();
808
Object val = reorderMap.get(encoding + "." + language + "." + country);
809
if (val == null) {
810
val = reorderMap.get(encoding + "." + language);
811
}
812
if (val == null) {
813
val = reorderMap.get(encoding);
814
}
815
return val;
816
}
817
818
/* This method reorders the sequence such that the matches for the
819
* file encoding are moved ahead of other elements.
820
* If an encoding uses more than one font, they are all moved up.
821
*/
822
private void reorderSequenceForLocale(String[] seq) {
823
Object val = getReorderSequence();
824
if (val instanceof String) {
825
for (int i=0; i< seq.length; i++) {
826
if (seq[i].equals(val)) {
827
shuffle(seq, i, 0);
828
return;
829
}
830
}
831
} else if (val instanceof String[]) {
832
String[] fontLangs = (String[])val;
833
for (int l=0; l<fontLangs.length;l++) {
834
for (int i=0; i<seq.length;i++) {
835
if (seq[i].equals(fontLangs[l])) {
836
shuffle(seq, i, l);
837
}
838
}
839
}
840
}
841
}
842
843
private static Vector<String> splitSequence(String sequence) {
844
//String.split would be more convenient, but incurs big performance penalty
845
Vector<String> parts = new Vector<>();
846
int start = 0;
847
int end;
848
while ((end = sequence.indexOf(',', start)) >= 0) {
849
parts.add(sequence.substring(start, end));
850
start = end + 1;
851
}
852
if (sequence.length() > start) {
853
parts.add(sequence.substring(start, sequence.length()));
854
}
855
return parts;
856
}
857
858
protected String[] split(String sequence) {
859
Vector<String> v = splitSequence(sequence);
860
return v.toArray(new String[0]);
861
}
862
863
////////////////////////////////////////////////////////////////////////
864
// Methods for extracting information from the fontconfig data for AWT//
865
////////////////////////////////////////////////////////////////////////
866
private Hashtable<String, Charset> charsetRegistry = new Hashtable<>(5);
867
868
/**
869
* Returns FontDescriptors describing the physical fonts used for the
870
* given logical font name and style. The font name is interpreted
871
* in a case insensitive way.
872
* The style argument is interpreted as in java.awt.Font.Font.
873
*/
874
public FontDescriptor[] getFontDescriptors(String fontName, int style) {
875
assert isLogicalFontFamilyName(fontName);
876
fontName = fontName.toLowerCase(Locale.ENGLISH);
877
int fontIndex = getFontIndex(fontName);
878
int styleIndex = getStyleIndex(style);
879
return getFontDescriptors(fontIndex, styleIndex);
880
}
881
private FontDescriptor[][][] fontDescriptors =
882
new FontDescriptor[NUM_FONTS][NUM_STYLES][];
883
884
private FontDescriptor[] getFontDescriptors(int fontIndex, int styleIndex) {
885
FontDescriptor[] descriptors = fontDescriptors[fontIndex][styleIndex];
886
if (descriptors == null) {
887
descriptors = buildFontDescriptors(fontIndex, styleIndex);
888
fontDescriptors[fontIndex][styleIndex] = descriptors;
889
}
890
return descriptors;
891
}
892
893
protected FontDescriptor[] buildFontDescriptors(int fontIndex, int styleIndex) {
894
String fontName = fontNames[fontIndex];
895
String styleName = styleNames[styleIndex];
896
897
short[] scriptIDs = getCoreScripts(fontIndex);
898
short[] nameIDs = compFontNameIDs[fontIndex][styleIndex];
899
String[] sequence = new String[scriptIDs.length];
900
String[] names = new String[scriptIDs.length];
901
for (int i = 0; i < sequence.length; i++) {
902
names[i] = getComponentFontName(nameIDs[i]);
903
sequence[i] = getScriptName(scriptIDs[i]);
904
if (alphabeticSuffix != null && "alphabetic".equals(sequence[i])) {
905
sequence[i] = sequence[i] + "/" + alphabeticSuffix;
906
}
907
}
908
int[][] fontExclusionRanges = compExclusions[fontIndex];
909
910
FontDescriptor[] descriptors = new FontDescriptor[names.length];
911
912
for (int i = 0; i < names.length; i++) {
913
String awtFontName;
914
String encoding;
915
916
awtFontName = makeAWTFontName(names[i], sequence[i]);
917
918
// look up character encoding
919
encoding = getEncoding(names[i], sequence[i]);
920
if (encoding == null) {
921
encoding = "default";
922
}
923
CharsetEncoder enc
924
= getFontCharsetEncoder(encoding.trim(), awtFontName);
925
926
// we already have the exclusion ranges
927
int[] exclusionRanges = fontExclusionRanges[i];
928
929
// create descriptor
930
descriptors[i] = new FontDescriptor(awtFontName, enc, exclusionRanges);
931
}
932
return descriptors;
933
}
934
935
/**
936
* Returns the AWT font name for the given platform font name and
937
* character subset.
938
*/
939
protected String makeAWTFontName(String platformFontName,
940
String characterSubsetName) {
941
return platformFontName;
942
}
943
944
/**
945
* Returns the java.io name of the platform character encoding for the
946
* given AWT font name and character subset. May return "default"
947
* to indicate that getDefaultFontCharset should be called to obtain
948
* a charset encoder.
949
*/
950
protected abstract String getEncoding(String awtFontName,
951
String characterSubsetName);
952
953
private CharsetEncoder getFontCharsetEncoder(final String charsetName,
954
String fontName) {
955
956
Charset fc = null;
957
if (charsetName.equals("default")) {
958
fc = charsetRegistry.get(fontName);
959
} else {
960
fc = charsetRegistry.get(charsetName);
961
}
962
if (fc != null) {
963
return fc.newEncoder();
964
}
965
966
if (!charsetName.startsWith("sun.awt.") &&
967
!charsetName.equals("default") &&
968
!charsetName.startsWith("sun.font.")) {
969
fc = Charset.forName(charsetName);
970
} else {
971
@SuppressWarnings("removal")
972
Class<?> fcc = AccessController.doPrivileged(new PrivilegedAction<Class<?>>() {
973
public Class<?> run() {
974
try {
975
return Class.forName(charsetName, true,
976
ClassLoader.getSystemClassLoader());
977
} catch (ClassNotFoundException e) {
978
}
979
return null;
980
}
981
});
982
983
if (fcc != null) {
984
try {
985
fc = (Charset) fcc.getDeclaredConstructor().newInstance();
986
} catch (Exception e) {
987
}
988
}
989
}
990
if (fc == null) {
991
fc = getDefaultFontCharset(fontName);
992
}
993
994
if (charsetName.equals("default")){
995
charsetRegistry.put(fontName, fc);
996
} else {
997
charsetRegistry.put(charsetName, fc);
998
}
999
return fc.newEncoder();
1000
}
1001
1002
protected abstract Charset getDefaultFontCharset(
1003
String fontName);
1004
1005
/* This retrieves the platform font directories (path) calculated
1006
* by setAWTFontPathSequence(String[]). The default implementation
1007
* returns null, its expected that X11 platforms may return
1008
* non-null.
1009
*/
1010
public HashSet<String> getAWTFontPathSet() {
1011
return null;
1012
}
1013
1014
////////////////////////////////////////////////////////////////////////
1015
// methods for extracting information from the fontconfig data for 2D //
1016
////////////////////////////////////////////////////////////////////////
1017
1018
/**
1019
* Returns an array of composite font descriptors for all logical font
1020
* faces.
1021
*/
1022
public CompositeFontDescriptor[] get2DCompositeFontInfo() {
1023
CompositeFontDescriptor[] result =
1024
new CompositeFontDescriptor[NUM_FONTS * NUM_STYLES];
1025
String defaultFontFile = fontManager.getDefaultFontFile();
1026
String defaultFontFaceName = fontManager.getDefaultFontFaceName();
1027
1028
for (int fontIndex = 0; fontIndex < NUM_FONTS; fontIndex++) {
1029
String fontName = publicFontNames[fontIndex];
1030
1031
// determine exclusion ranges for font
1032
// AWT uses separate exclusion range array per component font.
1033
// 2D packs all range boundaries into one array.
1034
// Both use separate entries for lower and upper boundary.
1035
int[][] exclusions = compExclusions[fontIndex];
1036
int numExclusionRanges = 0;
1037
for (int i = 0; i < exclusions.length; i++) {
1038
numExclusionRanges += exclusions[i].length;
1039
}
1040
int[] exclusionRanges = new int[numExclusionRanges];
1041
int[] exclusionRangeLimits = new int[exclusions.length];
1042
int exclusionRangeIndex = 0;
1043
int exclusionRangeLimitIndex = 0;
1044
for (int i = 0; i < exclusions.length; i++) {
1045
int[] componentRanges = exclusions[i];
1046
for (int j = 0; j < componentRanges.length; ) {
1047
int value = componentRanges[j];
1048
exclusionRanges[exclusionRangeIndex++] = componentRanges[j++];
1049
exclusionRanges[exclusionRangeIndex++] = componentRanges[j++];
1050
}
1051
exclusionRangeLimits[i] = exclusionRangeIndex;
1052
}
1053
// other info is per style
1054
for (int styleIndex = 0; styleIndex < NUM_STYLES; styleIndex++) {
1055
int maxComponentFontCount = compFontNameIDs[fontIndex][styleIndex].length;
1056
// fall back fonts listed in the lib/fonts/fallback directory
1057
if (installedFallbackFontFiles != null) {
1058
maxComponentFontCount += installedFallbackFontFiles.length;
1059
}
1060
String faceName = fontName + "." + styleNames[styleIndex];
1061
1062
// determine face names and file names of component fonts
1063
String[] componentFaceNames = new String[maxComponentFontCount];
1064
String[] componentFileNames = new String[maxComponentFontCount];
1065
1066
int index;
1067
for (index = 0; index < compFontNameIDs[fontIndex][styleIndex].length; index++) {
1068
short fontNameID = compFontNameIDs[fontIndex][styleIndex][index];
1069
short fileNameID = getComponentFileID(fontNameID);
1070
componentFaceNames[index] = getFaceNameFromComponentFontName(getComponentFontName(fontNameID));
1071
componentFileNames[index] = mapFileName(getComponentFileName(fileNameID));
1072
if (componentFileNames[index] == null ||
1073
needToSearchForFile(componentFileNames[index])) {
1074
componentFileNames[index] = getFileNameFromComponentFontName(getComponentFontName(fontNameID));
1075
}
1076
/*
1077
System.out.println(publicFontNames[fontIndex] + "." + styleNames[styleIndex] + "."
1078
+ getString(table_scriptIDs[coreScripts[index]]) + "=" + componentFileNames[index]);
1079
*/
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
@SuppressWarnings("removal")
1381
String osName = java.security.AccessController.doPrivileged(
1382
new java.security.PrivilegedAction<String>() {
1383
public String run() {
1384
return System.getProperty("os.name");
1385
}
1386
});
1387
1388
//componentFontNameID starts from "1"
1389
for (int ii = 1; ii < table_filenames.length; ii++) {
1390
if (table_filenames[ii] == -1) {
1391
// The corresponding finename entry for a component
1392
// font name is mandatory on Windows, but it's
1393
// optional on Solaris and Linux.
1394
if (osName.contains("Windows")) {
1395
System.err.println("\n Error: <filename."
1396
+ getString(table_componentFontNameIDs[ii])
1397
+ "> entry is missing!!!");
1398
errors++;
1399
} else {
1400
if (verbose && !isEmpty(table_filenames)) {
1401
System.err.println("\n Note: 'filename' entry is undefined for \""
1402
+ getString(table_componentFontNameIDs[ii])
1403
+ "\"");
1404
}
1405
}
1406
}
1407
}
1408
for (int ii = 0; ii < table_scriptIDs.length; ii++) {
1409
short fid = table_scriptFonts[ii];
1410
if (fid == 0) {
1411
System.out.println("\n Error: <allfonts."
1412
+ getString(table_scriptIDs[ii])
1413
+ "> entry is missing!!!");
1414
errors++;
1415
continue;
1416
} else if (fid < 0) {
1417
fid = (short)-fid;
1418
for (int iii = 0; iii < NUM_FONTS; iii++) {
1419
for (int iij = 0; iij < NUM_STYLES; iij++) {
1420
int jj = iii * NUM_STYLES + iij;
1421
short ffid = table_scriptFonts[fid + jj];
1422
if (ffid == 0) {
1423
System.err.println("\n Error: <"
1424
+ getFontName(iii) + "."
1425
+ getStyleName(iij) + "."
1426
+ getString(table_scriptIDs[ii])
1427
+ "> entry is missing!!!");
1428
errors++;
1429
}
1430
}
1431
}
1432
}
1433
}
1434
if (errors != 0) {
1435
System.err.println("!!THERE ARE " + errors + " ERROR(S) IN "
1436
+ "THE FONTCONFIG FILE, PLEASE CHECK ITS CONTENT!!\n");
1437
System.exit(1);
1438
}
1439
}
1440
1441
private static boolean isEmpty(short[] a) {
1442
for (short s : a) {
1443
if (s != -1) {
1444
return false;
1445
}
1446
}
1447
return true;
1448
}
1449
1450
//dump the fontconfig data tables
1451
private static void dump() {
1452
System.out.println("\n----Head Table------------");
1453
for (int ii = 0; ii < HEAD_LENGTH; ii++) {
1454
System.out.println(" " + ii + " : " + head[ii]);
1455
}
1456
System.out.println("\n----scriptIDs-------------");
1457
printTable(table_scriptIDs, 0);
1458
System.out.println("\n----scriptFonts----------------");
1459
for (int ii = 0; ii < table_scriptIDs.length; ii++) {
1460
short fid = table_scriptFonts[ii];
1461
if (fid >= 0) {
1462
System.out.println(" allfonts."
1463
+ getString(table_scriptIDs[ii])
1464
+ "="
1465
+ getString(table_componentFontNameIDs[fid]));
1466
}
1467
}
1468
for (int ii = 0; ii < table_scriptIDs.length; ii++) {
1469
short fid = table_scriptFonts[ii];
1470
if (fid < 0) {
1471
fid = (short)-fid;
1472
for (int iii = 0; iii < NUM_FONTS; iii++) {
1473
for (int iij = 0; iij < NUM_STYLES; iij++) {
1474
int jj = iii * NUM_STYLES + iij;
1475
short ffid = table_scriptFonts[fid + jj];
1476
System.out.println(" "
1477
+ getFontName(iii) + "."
1478
+ getStyleName(iij) + "."
1479
+ getString(table_scriptIDs[ii])
1480
+ "="
1481
+ getString(table_componentFontNameIDs[ffid]));
1482
}
1483
}
1484
1485
}
1486
}
1487
System.out.println("\n----elcIDs----------------");
1488
printTable(table_elcIDs, 0);
1489
System.out.println("\n----sequences-------------");
1490
for (int ii = 0; ii< table_elcIDs.length; ii++) {
1491
System.out.println(" " + ii + "/" + getString(table_elcIDs[ii]));
1492
short[] ss = getShortArray(table_sequences[ii * NUM_FONTS + 0]);
1493
for (int jj = 0; jj < ss.length; jj++) {
1494
System.out.println(" " + getString(table_scriptIDs[ss[jj]]));
1495
}
1496
}
1497
System.out.println("\n----fontfileNameIDs-------");
1498
printTable(table_fontfileNameIDs, 0);
1499
1500
System.out.println("\n----componentFontNameIDs--");
1501
printTable(table_componentFontNameIDs, 1);
1502
System.out.println("\n----filenames-------------");
1503
for (int ii = 0; ii < table_filenames.length; ii++) {
1504
if (table_filenames[ii] == -1) {
1505
System.out.println(" " + ii + " : null");
1506
} else {
1507
System.out.println(" " + ii + " : "
1508
+ getString(table_fontfileNameIDs[table_filenames[ii]]));
1509
}
1510
}
1511
System.out.println("\n----awtfontpaths---------");
1512
for (int ii = 0; ii < table_awtfontpaths.length; ii++) {
1513
System.out.println(" " + getString(table_scriptIDs[ii])
1514
+ " : "
1515
+ getString(table_awtfontpaths[ii]));
1516
}
1517
System.out.println("\n----proportionals--------");
1518
for (int ii = 0; ii < table_proportionals.length; ii++) {
1519
System.out.println(" "
1520
+ getString(table_componentFontNameIDs[table_proportionals[ii++]])
1521
+ " -> "
1522
+ getString(table_componentFontNameIDs[table_proportionals[ii]]));
1523
}
1524
int i = 0;
1525
System.out.println("\n----alphabeticSuffix----");
1526
while (i < table_alphabeticSuffix.length) {
1527
System.out.println(" " + getString(table_elcIDs[table_alphabeticSuffix[i++]])
1528
+ " -> " + getString(table_alphabeticSuffix[i++]));
1529
}
1530
System.out.println("\n----String Table---------");
1531
System.out.println(" stringID: Num =" + table_stringIDs.length);
1532
System.out.println(" stringTable: Size=" + table_stringTable.length * 2);
1533
1534
System.out.println("\n----fallbackScriptIDs---");
1535
short[] fbsIDs = getShortArray(head[INDEX_fallbackScripts]);
1536
for (int ii = 0; ii < fbsIDs.length; ii++) {
1537
System.out.println(" " + getString(table_scriptIDs[fbsIDs[ii]]));
1538
}
1539
System.out.println("\n----appendedfontpath-----");
1540
System.out.println(" " + getString(head[INDEX_appendedfontpath]));
1541
System.out.println("\n----Version--------------");
1542
System.out.println(" " + getString(head[INDEX_version]));
1543
}
1544
1545
1546
//////////////////////////////////////////////////////////////////////
1547
// Data table access methods //
1548
//////////////////////////////////////////////////////////////////////
1549
1550
/* Return the fontID of the platformFontName defined in this font config
1551
* by "LogicalFontName.StyleName.CharacterSubsetName" entry or
1552
* "allfonts.CharacterSubsetName" entry in properties format fc file.
1553
*/
1554
protected static short getComponentFontID(short scriptID, int fontIndex, int styleIndex) {
1555
short fid = table_scriptFonts[scriptID];
1556
//System.out.println("fid=" + fid + "/ scriptID=" + scriptID + ", fi=" + fontIndex + ", si=" + styleIndex);
1557
if (fid >= 0) {
1558
//"allfonts"
1559
return fid;
1560
} else {
1561
return table_scriptFonts[-fid + fontIndex * NUM_STYLES + styleIndex];
1562
}
1563
}
1564
1565
/* Same as getCompoentFontID() except this method returns the fontID define by
1566
* "xxxx.motif" entry.
1567
*/
1568
protected static short getComponentFontIDMotif(short scriptID, int fontIndex, int styleIndex) {
1569
if (table_scriptFontsMotif.length == 0) {
1570
return 0;
1571
}
1572
short fid = table_scriptFontsMotif[scriptID];
1573
if (fid >= 0) {
1574
//"allfonts" > 0 or "not defined" == 0
1575
return fid;
1576
} else {
1577
return table_scriptFontsMotif[-fid + fontIndex * NUM_STYLES + styleIndex];
1578
}
1579
}
1580
1581
private static int[] getExclusionRanges(short scriptID) {
1582
short exID = table_exclusions[scriptID];
1583
if (exID == 0) {
1584
return EMPTY_INT_ARRAY;
1585
} else {
1586
char[] exChar = getString(exID).toCharArray();
1587
int[] exInt = new int[exChar.length / 2];
1588
int i = 0;
1589
for (int j = 0; j < exInt.length; j++) {
1590
exInt[j] = (exChar[i++] << 16) + (exChar[i++] & 0xffff);
1591
}
1592
return exInt;
1593
}
1594
}
1595
1596
private static boolean contains(short[] IDs, short id, int limit) {
1597
for (int i = 0; i < limit; i++) {
1598
if (IDs[i] == id) {
1599
return true;
1600
}
1601
}
1602
return false;
1603
}
1604
1605
/* Return the PlatformFontName from its fontID*/
1606
protected static String getComponentFontName(short id) {
1607
if (id < 0) {
1608
return null;
1609
}
1610
return getString(table_componentFontNameIDs[id]);
1611
}
1612
1613
private static String getComponentFileName(short id) {
1614
if (id < 0) {
1615
return null;
1616
}
1617
return getString(table_fontfileNameIDs[id]);
1618
}
1619
1620
//componentFontID -> componentFileID
1621
private static short getComponentFileID(short nameID) {
1622
return table_filenames[nameID];
1623
}
1624
1625
private static String getScriptName(short scriptID) {
1626
return getString(table_scriptIDs[scriptID]);
1627
}
1628
1629
private HashMap<String, Short> reorderScripts;
1630
protected short[] getCoreScripts(int fontIndex) {
1631
short elc = getInitELC();
1632
/*
1633
System.out.println("getCoreScripts: elc=" + elc + ", fontIndex=" + fontIndex);
1634
short[] ss = getShortArray(table_sequences[elc * NUM_FONTS + fontIndex]);
1635
for (int i = 0; i < ss.length; i++) {
1636
System.out.println(" " + getString((short)table_scriptIDs[ss[i]]));
1637
}
1638
*/
1639
short[] scripts = getShortArray(table_sequences[elc * NUM_FONTS + fontIndex]);
1640
if (preferLocaleFonts) {
1641
if (reorderScripts == null) {
1642
reorderScripts = new HashMap<String, Short>();
1643
}
1644
String[] ss = new String[scripts.length];
1645
for (int i = 0; i < ss.length; i++) {
1646
ss[i] = getScriptName(scripts[i]);
1647
reorderScripts.put(ss[i], scripts[i]);
1648
}
1649
reorderSequenceForLocale(ss);
1650
for (int i = 0; i < ss.length; i++) {
1651
scripts[i] = reorderScripts.get(ss[i]);
1652
}
1653
}
1654
return scripts;
1655
}
1656
1657
private static short[] getFallbackScripts() {
1658
return getShortArray(head[INDEX_fallbackScripts]);
1659
}
1660
1661
private static void printTable(short[] list, int start) {
1662
for (int i = start; i < list.length; i++) {
1663
System.out.println(" " + i + " : " + getString(list[i]));
1664
}
1665
}
1666
1667
private static short[] readShortTable(DataInputStream in, int len )
1668
throws IOException {
1669
if (len == 0) {
1670
return EMPTY_SHORT_ARRAY;
1671
}
1672
short[] data = new short[len];
1673
byte[] bb = new byte[len * 2];
1674
in.read(bb);
1675
int i = 0,j = 0;
1676
while (i < len) {
1677
data[i++] = (short)(bb[j++] << 8 | (bb[j++] & 0xff));
1678
}
1679
return data;
1680
}
1681
1682
private static void writeShortTable(DataOutputStream out, short[] data)
1683
throws IOException {
1684
for (short val : data) {
1685
out.writeShort(val);
1686
}
1687
}
1688
1689
private static short[] toList(HashMap<String, Short> map) {
1690
short[] list = new short[map.size()];
1691
Arrays.fill(list, (short) -1);
1692
for (Entry<String, Short> entry : map.entrySet()) {
1693
list[entry.getValue()] = getStringID(entry.getKey());
1694
}
1695
return list;
1696
}
1697
1698
//runtime cache
1699
private static String[] stringCache;
1700
protected static String getString(short stringID) {
1701
if (stringID == 0)
1702
return null;
1703
/*
1704
if (loadingProperties) {
1705
return stringTable.substring(stringIDs[stringID],
1706
stringIDs[stringID+1]);
1707
}
1708
*/
1709
//sync if we want it to be MT-enabled
1710
if (stringCache[stringID] == null){
1711
stringCache[stringID] =
1712
new String (table_stringTable,
1713
table_stringIDs[stringID],
1714
table_stringIDs[stringID+1] - table_stringIDs[stringID]);
1715
}
1716
return stringCache[stringID];
1717
}
1718
1719
private static short[] getShortArray(short shortArrayID) {
1720
String s = getString(shortArrayID);
1721
char[] cc = s.toCharArray();
1722
short[] ss = new short[cc.length];
1723
for (int i = 0; i < cc.length; i++) {
1724
ss[i] = (short)(cc[i] & 0xffff);
1725
}
1726
return ss;
1727
}
1728
1729
private static short getStringID(String s) {
1730
if (s == null) {
1731
return (short)0;
1732
}
1733
short pos0 = (short)stringTable.length();
1734
stringTable.append(s);
1735
short pos1 = (short)stringTable.length();
1736
1737
stringIDs[stringIDNum] = pos0;
1738
stringIDs[stringIDNum + 1] = pos1;
1739
stringIDNum++;
1740
if (stringIDNum + 1 >= stringIDs.length) {
1741
short[] tmp = new short[stringIDNum + 1000];
1742
System.arraycopy(stringIDs, 0, tmp, 0, stringIDNum);
1743
stringIDs = tmp;
1744
}
1745
return (short)(stringIDNum - 1);
1746
}
1747
1748
private static short getShortArrayID(short[] sa) {
1749
char[] cc = new char[sa.length];
1750
for (int i = 0; i < sa.length; i ++) {
1751
cc[i] = (char)sa[i];
1752
}
1753
String s = new String(cc);
1754
return getStringID(s);
1755
}
1756
1757
//utility "empty" objects
1758
private static final int[] EMPTY_INT_ARRAY = new int[0];
1759
private static final String[] EMPTY_STRING_ARRAY = new String[0];
1760
private static final short[] EMPTY_SHORT_ARRAY = new short[0];
1761
private static final String UNDEFINED_COMPONENT_FONT = "unknown";
1762
1763
//////////////////////////////////////////////////////////////////////////
1764
//Convert the FontConfig data in Properties file to binary data tables //
1765
//////////////////////////////////////////////////////////////////////////
1766
static class PropertiesHandler {
1767
public void load(InputStream in) throws IOException {
1768
initLogicalNameStyle();
1769
initHashMaps();
1770
FontProperties fp = new FontProperties();
1771
fp.load(in);
1772
initBinaryTable();
1773
}
1774
1775
private void initBinaryTable() {
1776
//(0)
1777
head = new short[HEAD_LENGTH];
1778
head[INDEX_scriptIDs] = (short)HEAD_LENGTH;
1779
1780
table_scriptIDs = toList(scriptIDs);
1781
//(1)a: scriptAllfonts scriptID/allfonts -> componentFontNameID
1782
// b: scriptFonts scriptID -> componentFontNameID[20]
1783
//if we have a "allfonts.script" def, then we just put
1784
//the "-platformFontID" value in the slot, otherwise the slot
1785
//value is "offset" which "offset" is where 20 entries located
1786
//in the table attached.
1787
head[INDEX_scriptFonts] = (short)(head[INDEX_scriptIDs] + table_scriptIDs.length);
1788
int len = table_scriptIDs.length + scriptFonts.size() * 20;
1789
table_scriptFonts = new short[len];
1790
1791
for (Entry<Short, Short> entry : scriptAllfonts.entrySet()) {
1792
table_scriptFonts[entry.getKey().intValue()] = entry.getValue();
1793
}
1794
int off = table_scriptIDs.length;
1795
for (Entry<Short, Short[]> entry : scriptFonts.entrySet()) {
1796
table_scriptFonts[entry.getKey().intValue()] = (short)-off;
1797
Short[] v = entry.getValue();
1798
for (int i = 0; i < 20; i++) {
1799
if (v[i] != null) {
1800
table_scriptFonts[off++] = v[i];
1801
} else {
1802
table_scriptFonts[off++] = 0;
1803
}
1804
}
1805
}
1806
1807
//(2)
1808
head[INDEX_elcIDs] = (short)(head[INDEX_scriptFonts] + table_scriptFonts.length);
1809
table_elcIDs = toList(elcIDs);
1810
1811
//(3) sequences elcID -> XXXX[1|5] -> scriptID[]
1812
head[INDEX_sequences] = (short)(head[INDEX_elcIDs] + table_elcIDs.length);
1813
table_sequences = new short[elcIDs.size() * NUM_FONTS];
1814
for (Entry<Short, short[]> entry : sequences.entrySet()) {
1815
//table_sequences[entry.getKey().intValue()] = (short)-off;
1816
int k = entry.getKey().intValue();
1817
short[] v = entry.getValue();
1818
/*
1819
System.out.println("elc=" + k + "/" + getString((short)table_elcIDs[k]));
1820
short[] ss = getShortArray(v[0]);
1821
for (int i = 0; i < ss.length; i++) {
1822
System.out.println(" " + getString((short)table_scriptIDs[ss[i]]));
1823
}
1824
*/
1825
if (v.length == 1) {
1826
//the "allfonts" entries
1827
for (int i = 0; i < NUM_FONTS; i++) {
1828
table_sequences[k * NUM_FONTS + i] = v[0];
1829
}
1830
} else {
1831
for (int i = 0; i < NUM_FONTS; i++) {
1832
table_sequences[k * NUM_FONTS + i] = v[i];
1833
}
1834
}
1835
}
1836
//(4)
1837
head[INDEX_fontfileNameIDs] = (short)(head[INDEX_sequences] + table_sequences.length);
1838
table_fontfileNameIDs = toList(fontfileNameIDs);
1839
1840
//(5)
1841
head[INDEX_componentFontNameIDs] = (short)(head[INDEX_fontfileNameIDs] + table_fontfileNameIDs.length);
1842
table_componentFontNameIDs = toList(componentFontNameIDs);
1843
1844
//(6)componentFontNameID -> filenameID
1845
head[INDEX_filenames] = (short)(head[INDEX_componentFontNameIDs] + table_componentFontNameIDs.length);
1846
table_filenames = new short[table_componentFontNameIDs.length];
1847
Arrays.fill(table_filenames, (short) -1);
1848
1849
for (Entry<Short, Short> entry : filenames.entrySet()) {
1850
table_filenames[entry.getKey()] = entry.getValue();
1851
}
1852
1853
//(7)scriptID-> awtfontpath
1854
//the paths are stored as scriptID -> stringID in awtfontpahts
1855
head[INDEX_awtfontpaths] = (short)(head[INDEX_filenames] + table_filenames.length);
1856
table_awtfontpaths = new short[table_scriptIDs.length];
1857
for (Entry<Short, Short> entry : awtfontpaths.entrySet()) {
1858
table_awtfontpaths[entry.getKey()] = entry.getValue();
1859
}
1860
1861
//(8)exclusions
1862
head[INDEX_exclusions] = (short)(head[INDEX_awtfontpaths] + table_awtfontpaths.length);
1863
table_exclusions = new short[scriptIDs.size()];
1864
for (Entry<Short, int[]> entry : exclusions.entrySet()) {
1865
int[] exI = entry.getValue();
1866
char[] exC = new char[exI.length * 2];
1867
int j = 0;
1868
for (int i = 0; i < exI.length; i++) {
1869
exC[j++] = (char) (exI[i] >> 16);
1870
exC[j++] = (char) (exI[i] & 0xffff);
1871
}
1872
table_exclusions[entry.getKey()] = getStringID(new String (exC));
1873
}
1874
//(9)proportionals
1875
head[INDEX_proportionals] = (short)(head[INDEX_exclusions] + table_exclusions.length);
1876
table_proportionals = new short[proportionals.size() * 2];
1877
int j = 0;
1878
for (Entry<Short, Short> entry : proportionals.entrySet()) {
1879
table_proportionals[j++] = entry.getKey();
1880
table_proportionals[j++] = entry.getValue();
1881
}
1882
1883
//(10) see (1) for info, the only difference is "xxx.motif"
1884
head[INDEX_scriptFontsMotif] = (short)(head[INDEX_proportionals] + table_proportionals.length);
1885
if (scriptAllfontsMotif.size() != 0 || scriptFontsMotif.size() != 0) {
1886
len = table_scriptIDs.length + scriptFontsMotif.size() * 20;
1887
table_scriptFontsMotif = new short[len];
1888
1889
for (Entry<Short, Short> entry : scriptAllfontsMotif.entrySet()) {
1890
table_scriptFontsMotif[entry.getKey().intValue()] =
1891
(short)entry.getValue();
1892
}
1893
off = table_scriptIDs.length;
1894
for (Entry<Short, Short[]> entry : scriptFontsMotif.entrySet()) {
1895
table_scriptFontsMotif[entry.getKey().intValue()] = (short)-off;
1896
Short[] v = entry.getValue();
1897
int i = 0;
1898
while (i < 20) {
1899
if (v[i] != null) {
1900
table_scriptFontsMotif[off++] = v[i];
1901
} else {
1902
table_scriptFontsMotif[off++] = 0;
1903
}
1904
i++;
1905
}
1906
}
1907
} else {
1908
table_scriptFontsMotif = EMPTY_SHORT_ARRAY;
1909
}
1910
1911
//(11)short[] alphabeticSuffix
1912
head[INDEX_alphabeticSuffix] = (short)(head[INDEX_scriptFontsMotif] + table_scriptFontsMotif.length);
1913
table_alphabeticSuffix = new short[alphabeticSuffix.size() * 2];
1914
j = 0;
1915
for (Entry<Short, Short> entry : alphabeticSuffix.entrySet()) {
1916
table_alphabeticSuffix[j++] = entry.getKey();
1917
table_alphabeticSuffix[j++] = entry.getValue();
1918
}
1919
1920
//(15)short[] fallbackScriptIDs; just put the ID in head
1921
head[INDEX_fallbackScripts] = getShortArrayID(fallbackScriptIDs);
1922
1923
//(16)appendedfontpath
1924
head[INDEX_appendedfontpath] = getStringID(appendedfontpath);
1925
1926
//(17)version
1927
head[INDEX_version] = getStringID(version);
1928
1929
//(12)short[] StringIDs
1930
head[INDEX_stringIDs] = (short)(head[INDEX_alphabeticSuffix] + table_alphabeticSuffix.length);
1931
table_stringIDs = new short[stringIDNum + 1];
1932
System.arraycopy(stringIDs, 0, table_stringIDs, 0, stringIDNum + 1);
1933
1934
//(13)StringTable
1935
head[INDEX_stringTable] = (short)(head[INDEX_stringIDs] + stringIDNum + 1);
1936
table_stringTable = stringTable.toString().toCharArray();
1937
//(14)
1938
head[INDEX_TABLEEND] = (short)(head[INDEX_stringTable] + stringTable.length());
1939
1940
//StringTable cache
1941
stringCache = new String[table_stringIDs.length];
1942
}
1943
1944
//////////////////////////////////////////////
1945
private HashMap<String, Short> scriptIDs;
1946
//elc -> Encoding.Language.Country
1947
private HashMap<String, Short> elcIDs;
1948
//componentFontNameID starts from "1", "0" reserves for "undefined"
1949
private HashMap<String, Short> componentFontNameIDs;
1950
private HashMap<String, Short> fontfileNameIDs;
1951
private HashMap<String, Integer> logicalFontIDs;
1952
private HashMap<String, Integer> fontStyleIDs;
1953
1954
//componentFontNameID -> fontfileNameID
1955
private HashMap<Short, Short> filenames;
1956
1957
//elcID -> allfonts/logicalFont -> scriptID list
1958
//(1)if we have a "allfonts", then the length of the
1959
// value array is "1", otherwise it's 5, each font
1960
// must have their own individual entry.
1961
//scriptID list "short[]" is stored as an ID
1962
private HashMap<Short, short[]> sequences;
1963
1964
//scriptID ->logicFontID/fontStyleID->componentFontNameID,
1965
//a 20-entry array (5-name x 4-style) for each script
1966
private HashMap<Short, Short[]> scriptFonts;
1967
1968
//scriptID -> componentFontNameID
1969
private HashMap<Short, Short> scriptAllfonts;
1970
1971
//scriptID -> exclusionRanges[]
1972
private HashMap<Short, int[]> exclusions;
1973
1974
//scriptID -> fontpath
1975
private HashMap<Short, Short> awtfontpaths;
1976
1977
//fontID -> fontID
1978
private HashMap<Short, Short> proportionals;
1979
1980
//scriptID -> componentFontNameID
1981
private HashMap<Short, Short> scriptAllfontsMotif;
1982
1983
//scriptID ->logicFontID/fontStyleID->componentFontNameID,
1984
private HashMap<Short, Short[]> scriptFontsMotif;
1985
1986
//elcID -> stringID of alphabetic/XXXX
1987
private HashMap<Short, Short> alphabeticSuffix;
1988
1989
private short[] fallbackScriptIDs;
1990
private String version;
1991
private String appendedfontpath;
1992
1993
private void initLogicalNameStyle() {
1994
logicalFontIDs = new HashMap<String, Integer>();
1995
fontStyleIDs = new HashMap<String, Integer>();
1996
logicalFontIDs.put("serif", 0);
1997
logicalFontIDs.put("sansserif", 1);
1998
logicalFontIDs.put("monospaced", 2);
1999
logicalFontIDs.put("dialog", 3);
2000
logicalFontIDs.put("dialoginput",4);
2001
fontStyleIDs.put("plain", 0);
2002
fontStyleIDs.put("bold", 1);
2003
fontStyleIDs.put("italic", 2);
2004
fontStyleIDs.put("bolditalic", 3);
2005
}
2006
2007
private void initHashMaps() {
2008
scriptIDs = new HashMap<String, Short>();
2009
elcIDs = new HashMap<String, Short>();
2010
componentFontNameIDs = new HashMap<String, Short>();
2011
/*Init these tables to allow componentFontNameID, fontfileNameIDs
2012
to start from "1".
2013
*/
2014
componentFontNameIDs.put("", Short.valueOf((short)0));
2015
2016
fontfileNameIDs = new HashMap<String, Short>();
2017
filenames = new HashMap<Short, Short>();
2018
sequences = new HashMap<Short, short[]>();
2019
scriptFonts = new HashMap<Short, Short[]>();
2020
scriptAllfonts = new HashMap<Short, Short>();
2021
exclusions = new HashMap<Short, int[]>();
2022
awtfontpaths = new HashMap<Short, Short>();
2023
proportionals = new HashMap<Short, Short>();
2024
scriptFontsMotif = new HashMap<Short, Short[]>();
2025
scriptAllfontsMotif = new HashMap<Short, Short>();
2026
alphabeticSuffix = new HashMap<Short, Short>();
2027
fallbackScriptIDs = EMPTY_SHORT_ARRAY;
2028
/*
2029
version
2030
appendedfontpath
2031
*/
2032
}
2033
2034
private int[] parseExclusions(String key, String exclusions) {
2035
if (exclusions == null) {
2036
return EMPTY_INT_ARRAY;
2037
}
2038
// range format is xxxx-XXXX,yyyyyy-YYYYYY,.....
2039
int numExclusions = 1;
2040
int pos = 0;
2041
while ((pos = exclusions.indexOf(',', pos)) != -1) {
2042
numExclusions++;
2043
pos++;
2044
}
2045
int[] exclusionRanges = new int[numExclusions * 2];
2046
pos = 0;
2047
int newPos = 0;
2048
for (int j = 0; j < numExclusions * 2; ) {
2049
String lower, upper;
2050
int lo = 0, up = 0;
2051
try {
2052
newPos = exclusions.indexOf('-', pos);
2053
lower = exclusions.substring(pos, newPos);
2054
pos = newPos + 1;
2055
newPos = exclusions.indexOf(',', pos);
2056
if (newPos == -1) {
2057
newPos = exclusions.length();
2058
}
2059
upper = exclusions.substring(pos, newPos);
2060
pos = newPos + 1;
2061
int lowerLength = lower.length();
2062
int upperLength = upper.length();
2063
if (lowerLength != 4 && lowerLength != 6
2064
|| upperLength != 4 && upperLength != 6) {
2065
throw new Exception();
2066
}
2067
lo = Integer.parseInt(lower, 16);
2068
up = Integer.parseInt(upper, 16);
2069
if (lo > up) {
2070
throw new Exception();
2071
}
2072
} catch (Exception e) {
2073
if (FontUtilities.debugFonts() &&
2074
logger != null) {
2075
logger.config("Failed parsing " + key +
2076
" property of font configuration.");
2077
2078
}
2079
return EMPTY_INT_ARRAY;
2080
}
2081
exclusionRanges[j++] = lo;
2082
exclusionRanges[j++] = up;
2083
}
2084
return exclusionRanges;
2085
}
2086
2087
private Short getID(HashMap<String, Short> map, String key) {
2088
Short ret = map.get(key);
2089
if ( ret == null) {
2090
map.put(key, (short)map.size());
2091
return map.get(key);
2092
}
2093
return ret;
2094
}
2095
2096
@SuppressWarnings("serial") // JDK-implementation class
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 don't 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 = 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 don't 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
}
2273
2274