Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.desktop/share/classes/sun/awt/FontConfiguration.java
41152 views
1
/*
2
* Copyright (c) 1996, 2021, 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.") && !charsetName.equals("default")) {
967
fc = Charset.forName(charsetName);
968
} else {
969
@SuppressWarnings("removal")
970
Class<?> fcc = AccessController.doPrivileged(new PrivilegedAction<Class<?>>() {
971
public Class<?> run() {
972
try {
973
return Class.forName(charsetName, true,
974
ClassLoader.getSystemClassLoader());
975
} catch (ClassNotFoundException e) {
976
}
977
return null;
978
}
979
});
980
981
if (fcc != null) {
982
try {
983
fc = (Charset) fcc.getDeclaredConstructor().newInstance();
984
} catch (Exception e) {
985
}
986
}
987
}
988
if (fc == null) {
989
fc = getDefaultFontCharset(fontName);
990
}
991
992
if (charsetName.equals("default")){
993
charsetRegistry.put(fontName, fc);
994
} else {
995
charsetRegistry.put(charsetName, fc);
996
}
997
return fc.newEncoder();
998
}
999
1000
protected abstract Charset getDefaultFontCharset(
1001
String fontName);
1002
1003
/* This retrieves the platform font directories (path) calculated
1004
* by setAWTFontPathSequence(String[]). The default implementation
1005
* returns null, its expected that X11 platforms may return
1006
* non-null.
1007
*/
1008
public HashSet<String> getAWTFontPathSet() {
1009
return null;
1010
}
1011
1012
////////////////////////////////////////////////////////////////////////
1013
// methods for extracting information from the fontconfig data for 2D //
1014
////////////////////////////////////////////////////////////////////////
1015
1016
/**
1017
* Returns an array of composite font descriptors for all logical font
1018
* faces.
1019
*/
1020
public CompositeFontDescriptor[] get2DCompositeFontInfo() {
1021
CompositeFontDescriptor[] result =
1022
new CompositeFontDescriptor[NUM_FONTS * NUM_STYLES];
1023
String defaultFontFile = fontManager.getDefaultFontFile();
1024
String defaultFontFaceName = fontManager.getDefaultFontFaceName();
1025
1026
for (int fontIndex = 0; fontIndex < NUM_FONTS; fontIndex++) {
1027
String fontName = publicFontNames[fontIndex];
1028
1029
// determine exclusion ranges for font
1030
// AWT uses separate exclusion range array per component font.
1031
// 2D packs all range boundaries into one array.
1032
// Both use separate entries for lower and upper boundary.
1033
int[][] exclusions = compExclusions[fontIndex];
1034
int numExclusionRanges = 0;
1035
for (int i = 0; i < exclusions.length; i++) {
1036
numExclusionRanges += exclusions[i].length;
1037
}
1038
int[] exclusionRanges = new int[numExclusionRanges];
1039
int[] exclusionRangeLimits = new int[exclusions.length];
1040
int exclusionRangeIndex = 0;
1041
int exclusionRangeLimitIndex = 0;
1042
for (int i = 0; i < exclusions.length; i++) {
1043
int[] componentRanges = exclusions[i];
1044
for (int j = 0; j < componentRanges.length; ) {
1045
int value = componentRanges[j];
1046
exclusionRanges[exclusionRangeIndex++] = componentRanges[j++];
1047
exclusionRanges[exclusionRangeIndex++] = componentRanges[j++];
1048
}
1049
exclusionRangeLimits[i] = exclusionRangeIndex;
1050
}
1051
// other info is per style
1052
for (int styleIndex = 0; styleIndex < NUM_STYLES; styleIndex++) {
1053
int maxComponentFontCount = compFontNameIDs[fontIndex][styleIndex].length;
1054
// fall back fonts listed in the lib/fonts/fallback directory
1055
if (installedFallbackFontFiles != null) {
1056
maxComponentFontCount += installedFallbackFontFiles.length;
1057
}
1058
String faceName = fontName + "." + styleNames[styleIndex];
1059
1060
// determine face names and file names of component fonts
1061
String[] componentFaceNames = new String[maxComponentFontCount];
1062
String[] componentFileNames = new String[maxComponentFontCount];
1063
1064
int index;
1065
for (index = 0; index < compFontNameIDs[fontIndex][styleIndex].length; index++) {
1066
short fontNameID = compFontNameIDs[fontIndex][styleIndex][index];
1067
short fileNameID = getComponentFileID(fontNameID);
1068
componentFaceNames[index] = getFaceNameFromComponentFontName(getComponentFontName(fontNameID));
1069
componentFileNames[index] = mapFileName(getComponentFileName(fileNameID));
1070
if (componentFileNames[index] == null ||
1071
needToSearchForFile(componentFileNames[index])) {
1072
componentFileNames[index] = getFileNameFromComponentFontName(getComponentFontName(fontNameID));
1073
}
1074
/*
1075
System.out.println(publicFontNames[fontIndex] + "." + styleNames[styleIndex] + "."
1076
+ getString(table_scriptIDs[coreScripts[index]]) + "=" + componentFileNames[index]);
1077
*/
1078
}
1079
1080
if (installedFallbackFontFiles != null) {
1081
for (int ifb=0; ifb<installedFallbackFontFiles.length; ifb++) {
1082
componentFaceNames[index] = null;
1083
componentFileNames[index] = installedFallbackFontFiles[ifb];
1084
index++;
1085
}
1086
}
1087
1088
if (index < maxComponentFontCount) {
1089
String[] newComponentFaceNames = new String[index];
1090
System.arraycopy(componentFaceNames, 0, newComponentFaceNames, 0, index);
1091
componentFaceNames = newComponentFaceNames;
1092
String[] newComponentFileNames = new String[index];
1093
System.arraycopy(componentFileNames, 0, newComponentFileNames, 0, index);
1094
componentFileNames = newComponentFileNames;
1095
}
1096
// exclusion range limit array length must match component face name
1097
// array length - native code relies on this
1098
1099
int[] clippedExclusionRangeLimits = exclusionRangeLimits;
1100
if (index != clippedExclusionRangeLimits.length) {
1101
int len = exclusionRangeLimits.length;
1102
clippedExclusionRangeLimits = new int[index];
1103
System.arraycopy(exclusionRangeLimits, 0, clippedExclusionRangeLimits, 0, len);
1104
//padding for various fallback fonts
1105
for (int i = len; i < index; i++) {
1106
clippedExclusionRangeLimits[i] = exclusionRanges.length;
1107
}
1108
}
1109
/*
1110
System.out.println(faceName + ":");
1111
for (int i = 0; i < componentFileNames.length; i++) {
1112
System.out.println(" " + componentFaceNames[i]
1113
+ " -> " + componentFileNames[i]);
1114
}
1115
*/
1116
result[fontIndex * NUM_STYLES + styleIndex]
1117
= new CompositeFontDescriptor(
1118
faceName,
1119
compCoreNum[fontIndex],
1120
componentFaceNames,
1121
componentFileNames,
1122
exclusionRanges,
1123
clippedExclusionRangeLimits);
1124
}
1125
}
1126
return result;
1127
}
1128
1129
protected abstract String getFaceNameFromComponentFontName(String componentFontName);
1130
protected abstract String getFileNameFromComponentFontName(String componentFontName);
1131
1132
/*
1133
public class 2dFont {
1134
public String platformName;
1135
public String fontfileName;
1136
}
1137
private 2dFont [] componentFonts = null;
1138
*/
1139
1140
/* Used on Linux to test if a file referenced in a font configuration
1141
* file exists in the location that is expected. If it does, no need
1142
* to search for it. If it doesn't then unless its a fallback font,
1143
* return that expensive code should be invoked to search for the font.
1144
*/
1145
HashMap<String, Boolean> existsMap;
1146
public boolean needToSearchForFile(String fileName) {
1147
if (!FontUtilities.isLinux) {
1148
return false;
1149
} else if (existsMap == null) {
1150
existsMap = new HashMap<String, Boolean>();
1151
}
1152
Boolean exists = existsMap.get(fileName);
1153
if (exists == null) {
1154
/* call getNumberCoreFonts() to ensure these are initialised, and
1155
* if this file isn't for a core component, ie, is a for a fallback
1156
* font which very typically isn't available, then can't afford
1157
* to take the start-up penalty to search for it.
1158
*/
1159
getNumberCoreFonts();
1160
if (!coreFontFileNames.contains(fileName)) {
1161
exists = Boolean.TRUE;
1162
} else {
1163
exists = Boolean.valueOf((new File(fileName)).exists());
1164
existsMap.put(fileName, exists);
1165
if (FontUtilities.debugFonts() &&
1166
exists == Boolean.FALSE) {
1167
logger.warning("Couldn't locate font file " + fileName);
1168
}
1169
}
1170
}
1171
return exists == Boolean.FALSE;
1172
}
1173
1174
private int numCoreFonts = -1;
1175
private String[] componentFonts = null;
1176
HashMap <String, String> filenamesMap = new HashMap<String, String>();
1177
HashSet <String> coreFontFileNames = new HashSet<String>();
1178
1179
/* Return the number of core fonts. Note this isn't thread safe but
1180
* a calling thread can call this and getPlatformFontNames() in either
1181
* order.
1182
*/
1183
public int getNumberCoreFonts() {
1184
if (numCoreFonts == -1) {
1185
numCoreFonts = coreFontNameIDs.size();
1186
Short[] emptyShortArray = new Short[0];
1187
Short[] core = coreFontNameIDs.toArray(emptyShortArray);
1188
Short[] fallback = fallbackFontNameIDs.toArray(emptyShortArray);
1189
1190
int numFallbackFonts = 0;
1191
int i;
1192
for (i = 0; i < fallback.length; i++) {
1193
if (coreFontNameIDs.contains(fallback[i])) {
1194
fallback[i] = null;
1195
continue;
1196
}
1197
numFallbackFonts++;
1198
}
1199
componentFonts = new String[numCoreFonts + numFallbackFonts];
1200
String filename = null;
1201
for (i = 0; i < core.length; i++) {
1202
short fontid = core[i];
1203
short fileid = getComponentFileID(fontid);
1204
componentFonts[i] = getComponentFontName(fontid);
1205
String compFileName = getComponentFileName(fileid);
1206
if (compFileName != null) {
1207
coreFontFileNames.add(compFileName);
1208
}
1209
filenamesMap.put(componentFonts[i], mapFileName(compFileName));
1210
}
1211
for (int j = 0; j < fallback.length; j++) {
1212
if (fallback[j] != null) {
1213
short fontid = fallback[j];
1214
short fileid = getComponentFileID(fontid);
1215
componentFonts[i] = getComponentFontName(fontid);
1216
filenamesMap.put(componentFonts[i],
1217
mapFileName(getComponentFileName(fileid)));
1218
i++;
1219
}
1220
}
1221
}
1222
return numCoreFonts;
1223
}
1224
1225
/* Return all platform font names used by this font configuration.
1226
* The first getNumberCoreFonts() entries are guaranteed to be the
1227
* core fonts - ie no fall back only fonts.
1228
*/
1229
public String[] getPlatformFontNames() {
1230
if (numCoreFonts == -1) {
1231
getNumberCoreFonts();
1232
}
1233
return componentFonts;
1234
}
1235
1236
/**
1237
* Returns a file name for the physical font represented by this platform font name,
1238
* if the font configuration has such information available, or null if the
1239
* information is unavailable. The file name returned is just a hint; a null return
1240
* value doesn't necessarily mean that the font is unavailable, nor does a non-null
1241
* return value guarantee that the file exists and contains the physical font.
1242
* The file name can be an absolute or a relative path name.
1243
*/
1244
public String getFileNameFromPlatformName(String platformName) {
1245
// get2DCompositeFontInfo
1246
// -> getFileNameFromComponentfontName() (W/M)
1247
// -> getFileNameFromPlatformName()
1248
// it's a waste of time on Win32, but I have to give X11 a chance to
1249
// call getFileNameFromXLFD()
1250
return filenamesMap.get(platformName);
1251
}
1252
1253
/**
1254
* Returns a configuration specific path to be appended to the font
1255
* search path.
1256
*/
1257
public String getExtraFontPath() {
1258
return getString(head[INDEX_appendedfontpath]);
1259
}
1260
1261
public String getVersion() {
1262
return getString(head[INDEX_version]);
1263
}
1264
1265
/* subclass support */
1266
protected static FontConfiguration getFontConfiguration() {
1267
return fontConfig;
1268
}
1269
1270
protected void setFontConfiguration() {
1271
fontConfig = this; /* static initialization */
1272
}
1273
1274
//////////////////////////////////////////////////////////////////////
1275
// FontConfig data tables and the index constants in binary file //
1276
//////////////////////////////////////////////////////////////////////
1277
/* The binary font configuration file begins with a short[] "head", which
1278
* contains the offsets to the starts of the individual data table which
1279
* immediately follow. The current implementation includes the tables shown
1280
* below.
1281
*
1282
* (00) table_scriptIDs :stringIDs of all defined CharacterSubsetNames
1283
* (01) table_scriptFonts :scriptID x fontIndex x styleIndex->
1284
* PlatformFontNameID mapping. Each scriptID might
1285
* have 1 or 20 entries depends on if it is defined
1286
* via a "allfonts.CharacterSubsetname" or a list of
1287
* "LogicalFontName.StyleName.CharacterSubsetName"
1288
* entries, positive entry means it's a "allfonts"
1289
* entry, a negative value means this is a offset to
1290
* a NUM_FONTS x NUM_STYLES subtable.
1291
* (02) table_elcIDs :stringIDs of all defined ELC names, string
1292
* "NULL.NULL.NULL" is used for "default"
1293
* (03) table_sequences :elcID x logicalFont -> scriptIDs table defined
1294
* by "sequence.allfonts/LogicalFontName.ELC" in
1295
* font configuration file, each "elcID" has
1296
* NUM_FONTS (5) entries in this table.
1297
* (04) table_fontfileNameIDs
1298
* :stringIDs of all defined font file names
1299
* (05) table_componentFontNameIDs
1300
* :stringIDs of all defined PlatformFontNames
1301
* (06) table_filenames :platformFontNamesID->fontfileNameID mapping
1302
* table, the index is the platformFontNamesID.
1303
* (07) table_awtfontpaths :CharacterSubsetNames->awtfontpaths mapping table,
1304
* the index is the CharacterSubsetName's stringID
1305
* and content is the stringID of awtfontpath.
1306
* (08) table_exclusions :scriptID -> exclusionRanges mapping table,
1307
* the index is the scriptID and the content is
1308
a id of an exclusionRanges int[].
1309
* (09) table_proportionals:list of pairs of PlatformFontNameIDs, stores
1310
* the replacement info defined by "proportional"
1311
* keyword.
1312
* (10) table_scriptFontsMotif
1313
* :same as (01) except this table stores the
1314
* info defined with ".motif" keyword
1315
* (11) table_alphabeticSuffix
1316
* :elcID -> stringID of alphabetic/XXXX entries
1317
* (12) table_stringIDs :The index of this table is the string ID, the
1318
* content is the "start index" of this string in
1319
* stringTable, use the start index of next entry
1320
* as the "end index".
1321
* (13) table_stringTable :The real storage of all character strings defined
1322
* /used this font configuration, need a pair of
1323
* "start" and "end" indices to access.
1324
* (14) reserved
1325
* (15) table_fallbackScripts
1326
* :stringIDs of fallback CharacterSubsetnames, stored
1327
* in the order of they are defined in sequence.fallback.
1328
* (16) table_appendedfontpath
1329
* :stringtID of the "appendedfontpath" defined.
1330
* (17) table_version :stringID of the version number of this fontconfig file.
1331
*/
1332
private static final int HEAD_LENGTH = 20;
1333
private static final int INDEX_scriptIDs = 0;
1334
private static final int INDEX_scriptFonts = 1;
1335
private static final int INDEX_elcIDs = 2;
1336
private static final int INDEX_sequences = 3;
1337
private static final int INDEX_fontfileNameIDs = 4;
1338
private static final int INDEX_componentFontNameIDs = 5;
1339
private static final int INDEX_filenames = 6;
1340
private static final int INDEX_awtfontpaths = 7;
1341
private static final int INDEX_exclusions = 8;
1342
private static final int INDEX_proportionals = 9;
1343
private static final int INDEX_scriptFontsMotif = 10;
1344
private static final int INDEX_alphabeticSuffix = 11;
1345
private static final int INDEX_stringIDs = 12;
1346
private static final int INDEX_stringTable = 13;
1347
private static final int INDEX_TABLEEND = 14;
1348
private static final int INDEX_fallbackScripts = 15;
1349
private static final int INDEX_appendedfontpath = 16;
1350
private static final int INDEX_version = 17;
1351
1352
private static short[] head;
1353
private static short[] table_scriptIDs;
1354
private static short[] table_scriptFonts;
1355
private static short[] table_elcIDs;
1356
private static short[] table_sequences;
1357
private static short[] table_fontfileNameIDs;
1358
private static short[] table_componentFontNameIDs;
1359
private static short[] table_filenames;
1360
protected static short[] table_awtfontpaths;
1361
private static short[] table_exclusions;
1362
private static short[] table_proportionals;
1363
private static short[] table_scriptFontsMotif;
1364
private static short[] table_alphabeticSuffix;
1365
private static short[] table_stringIDs;
1366
private static char[] table_stringTable;
1367
1368
/**
1369
* Checks consistencies of complied fontconfig data. This method
1370
* is called only at the build-time from
1371
* build.tools.compilefontconfig.CompileFontConfig.
1372
*/
1373
private static void sanityCheck() {
1374
int errors = 0;
1375
1376
//This method will only be called during build time, do we
1377
//need do PrivilegedAction?
1378
@SuppressWarnings("removal")
1379
String osName = java.security.AccessController.doPrivileged(
1380
new java.security.PrivilegedAction<String>() {
1381
public String run() {
1382
return System.getProperty("os.name");
1383
}
1384
});
1385
1386
//componentFontNameID starts from "1"
1387
for (int ii = 1; ii < table_filenames.length; ii++) {
1388
if (table_filenames[ii] == -1) {
1389
// The corresponding finename entry for a component
1390
// font name is mandatory on Windows, but it's
1391
// optional on Solaris and Linux.
1392
if (osName.contains("Windows")) {
1393
System.err.println("\n Error: <filename."
1394
+ getString(table_componentFontNameIDs[ii])
1395
+ "> entry is missing!!!");
1396
errors++;
1397
} else {
1398
if (verbose && !isEmpty(table_filenames)) {
1399
System.err.println("\n Note: 'filename' entry is undefined for \""
1400
+ getString(table_componentFontNameIDs[ii])
1401
+ "\"");
1402
}
1403
}
1404
}
1405
}
1406
for (int ii = 0; ii < table_scriptIDs.length; ii++) {
1407
short fid = table_scriptFonts[ii];
1408
if (fid == 0) {
1409
System.out.println("\n Error: <allfonts."
1410
+ getString(table_scriptIDs[ii])
1411
+ "> entry is missing!!!");
1412
errors++;
1413
continue;
1414
} else if (fid < 0) {
1415
fid = (short)-fid;
1416
for (int iii = 0; iii < NUM_FONTS; iii++) {
1417
for (int iij = 0; iij < NUM_STYLES; iij++) {
1418
int jj = iii * NUM_STYLES + iij;
1419
short ffid = table_scriptFonts[fid + jj];
1420
if (ffid == 0) {
1421
System.err.println("\n Error: <"
1422
+ getFontName(iii) + "."
1423
+ getStyleName(iij) + "."
1424
+ getString(table_scriptIDs[ii])
1425
+ "> entry is missing!!!");
1426
errors++;
1427
}
1428
}
1429
}
1430
}
1431
}
1432
if (errors != 0) {
1433
System.err.println("!!THERE ARE " + errors + " ERROR(S) IN "
1434
+ "THE FONTCONFIG FILE, PLEASE CHECK ITS CONTENT!!\n");
1435
System.exit(1);
1436
}
1437
}
1438
1439
private static boolean isEmpty(short[] a) {
1440
for (short s : a) {
1441
if (s != -1) {
1442
return false;
1443
}
1444
}
1445
return true;
1446
}
1447
1448
//dump the fontconfig data tables
1449
private static void dump() {
1450
System.out.println("\n----Head Table------------");
1451
for (int ii = 0; ii < HEAD_LENGTH; ii++) {
1452
System.out.println(" " + ii + " : " + head[ii]);
1453
}
1454
System.out.println("\n----scriptIDs-------------");
1455
printTable(table_scriptIDs, 0);
1456
System.out.println("\n----scriptFonts----------------");
1457
for (int ii = 0; ii < table_scriptIDs.length; ii++) {
1458
short fid = table_scriptFonts[ii];
1459
if (fid >= 0) {
1460
System.out.println(" allfonts."
1461
+ getString(table_scriptIDs[ii])
1462
+ "="
1463
+ getString(table_componentFontNameIDs[fid]));
1464
}
1465
}
1466
for (int ii = 0; ii < table_scriptIDs.length; ii++) {
1467
short fid = table_scriptFonts[ii];
1468
if (fid < 0) {
1469
fid = (short)-fid;
1470
for (int iii = 0; iii < NUM_FONTS; iii++) {
1471
for (int iij = 0; iij < NUM_STYLES; iij++) {
1472
int jj = iii * NUM_STYLES + iij;
1473
short ffid = table_scriptFonts[fid + jj];
1474
System.out.println(" "
1475
+ getFontName(iii) + "."
1476
+ getStyleName(iij) + "."
1477
+ getString(table_scriptIDs[ii])
1478
+ "="
1479
+ getString(table_componentFontNameIDs[ffid]));
1480
}
1481
}
1482
1483
}
1484
}
1485
System.out.println("\n----elcIDs----------------");
1486
printTable(table_elcIDs, 0);
1487
System.out.println("\n----sequences-------------");
1488
for (int ii = 0; ii< table_elcIDs.length; ii++) {
1489
System.out.println(" " + ii + "/" + getString(table_elcIDs[ii]));
1490
short[] ss = getShortArray(table_sequences[ii * NUM_FONTS + 0]);
1491
for (int jj = 0; jj < ss.length; jj++) {
1492
System.out.println(" " + getString(table_scriptIDs[ss[jj]]));
1493
}
1494
}
1495
System.out.println("\n----fontfileNameIDs-------");
1496
printTable(table_fontfileNameIDs, 0);
1497
1498
System.out.println("\n----componentFontNameIDs--");
1499
printTable(table_componentFontNameIDs, 1);
1500
System.out.println("\n----filenames-------------");
1501
for (int ii = 0; ii < table_filenames.length; ii++) {
1502
if (table_filenames[ii] == -1) {
1503
System.out.println(" " + ii + " : null");
1504
} else {
1505
System.out.println(" " + ii + " : "
1506
+ getString(table_fontfileNameIDs[table_filenames[ii]]));
1507
}
1508
}
1509
System.out.println("\n----awtfontpaths---------");
1510
for (int ii = 0; ii < table_awtfontpaths.length; ii++) {
1511
System.out.println(" " + getString(table_scriptIDs[ii])
1512
+ " : "
1513
+ getString(table_awtfontpaths[ii]));
1514
}
1515
System.out.println("\n----proportionals--------");
1516
for (int ii = 0; ii < table_proportionals.length; ii++) {
1517
System.out.println(" "
1518
+ getString(table_componentFontNameIDs[table_proportionals[ii++]])
1519
+ " -> "
1520
+ getString(table_componentFontNameIDs[table_proportionals[ii]]));
1521
}
1522
int i = 0;
1523
System.out.println("\n----alphabeticSuffix----");
1524
while (i < table_alphabeticSuffix.length) {
1525
System.out.println(" " + getString(table_elcIDs[table_alphabeticSuffix[i++]])
1526
+ " -> " + getString(table_alphabeticSuffix[i++]));
1527
}
1528
System.out.println("\n----String Table---------");
1529
System.out.println(" stringID: Num =" + table_stringIDs.length);
1530
System.out.println(" stringTable: Size=" + table_stringTable.length * 2);
1531
1532
System.out.println("\n----fallbackScriptIDs---");
1533
short[] fbsIDs = getShortArray(head[INDEX_fallbackScripts]);
1534
for (int ii = 0; ii < fbsIDs.length; ii++) {
1535
System.out.println(" " + getString(table_scriptIDs[fbsIDs[ii]]));
1536
}
1537
System.out.println("\n----appendedfontpath-----");
1538
System.out.println(" " + getString(head[INDEX_appendedfontpath]));
1539
System.out.println("\n----Version--------------");
1540
System.out.println(" " + getString(head[INDEX_version]));
1541
}
1542
1543
1544
//////////////////////////////////////////////////////////////////////
1545
// Data table access methods //
1546
//////////////////////////////////////////////////////////////////////
1547
1548
/* Return the fontID of the platformFontName defined in this font config
1549
* by "LogicalFontName.StyleName.CharacterSubsetName" entry or
1550
* "allfonts.CharacterSubsetName" entry in properties format fc file.
1551
*/
1552
protected static short getComponentFontID(short scriptID, int fontIndex, int styleIndex) {
1553
short fid = table_scriptFonts[scriptID];
1554
//System.out.println("fid=" + fid + "/ scriptID=" + scriptID + ", fi=" + fontIndex + ", si=" + styleIndex);
1555
if (fid >= 0) {
1556
//"allfonts"
1557
return fid;
1558
} else {
1559
return table_scriptFonts[-fid + fontIndex * NUM_STYLES + styleIndex];
1560
}
1561
}
1562
1563
/* Same as getCompoentFontID() except this method returns the fontID define by
1564
* "xxxx.motif" entry.
1565
*/
1566
protected static short getComponentFontIDMotif(short scriptID, int fontIndex, int styleIndex) {
1567
if (table_scriptFontsMotif.length == 0) {
1568
return 0;
1569
}
1570
short fid = table_scriptFontsMotif[scriptID];
1571
if (fid >= 0) {
1572
//"allfonts" > 0 or "not defined" == 0
1573
return fid;
1574
} else {
1575
return table_scriptFontsMotif[-fid + fontIndex * NUM_STYLES + styleIndex];
1576
}
1577
}
1578
1579
private static int[] getExclusionRanges(short scriptID) {
1580
short exID = table_exclusions[scriptID];
1581
if (exID == 0) {
1582
return EMPTY_INT_ARRAY;
1583
} else {
1584
char[] exChar = getString(exID).toCharArray();
1585
int[] exInt = new int[exChar.length / 2];
1586
int i = 0;
1587
for (int j = 0; j < exInt.length; j++) {
1588
exInt[j] = (exChar[i++] << 16) + (exChar[i++] & 0xffff);
1589
}
1590
return exInt;
1591
}
1592
}
1593
1594
private static boolean contains(short[] IDs, short id, int limit) {
1595
for (int i = 0; i < limit; i++) {
1596
if (IDs[i] == id) {
1597
return true;
1598
}
1599
}
1600
return false;
1601
}
1602
1603
/* Return the PlatformFontName from its fontID*/
1604
protected static String getComponentFontName(short id) {
1605
if (id < 0) {
1606
return null;
1607
}
1608
return getString(table_componentFontNameIDs[id]);
1609
}
1610
1611
private static String getComponentFileName(short id) {
1612
if (id < 0) {
1613
return null;
1614
}
1615
return getString(table_fontfileNameIDs[id]);
1616
}
1617
1618
//componentFontID -> componentFileID
1619
private static short getComponentFileID(short nameID) {
1620
return table_filenames[nameID];
1621
}
1622
1623
private static String getScriptName(short scriptID) {
1624
return getString(table_scriptIDs[scriptID]);
1625
}
1626
1627
private HashMap<String, Short> reorderScripts;
1628
protected short[] getCoreScripts(int fontIndex) {
1629
short elc = getInitELC();
1630
/*
1631
System.out.println("getCoreScripts: elc=" + elc + ", fontIndex=" + fontIndex);
1632
short[] ss = getShortArray(table_sequences[elc * NUM_FONTS + fontIndex]);
1633
for (int i = 0; i < ss.length; i++) {
1634
System.out.println(" " + getString((short)table_scriptIDs[ss[i]]));
1635
}
1636
*/
1637
short[] scripts = getShortArray(table_sequences[elc * NUM_FONTS + fontIndex]);
1638
if (preferLocaleFonts) {
1639
if (reorderScripts == null) {
1640
reorderScripts = new HashMap<String, Short>();
1641
}
1642
String[] ss = new String[scripts.length];
1643
for (int i = 0; i < ss.length; i++) {
1644
ss[i] = getScriptName(scripts[i]);
1645
reorderScripts.put(ss[i], scripts[i]);
1646
}
1647
reorderSequenceForLocale(ss);
1648
for (int i = 0; i < ss.length; i++) {
1649
scripts[i] = reorderScripts.get(ss[i]);
1650
}
1651
}
1652
return scripts;
1653
}
1654
1655
private static short[] getFallbackScripts() {
1656
return getShortArray(head[INDEX_fallbackScripts]);
1657
}
1658
1659
private static void printTable(short[] list, int start) {
1660
for (int i = start; i < list.length; i++) {
1661
System.out.println(" " + i + " : " + getString(list[i]));
1662
}
1663
}
1664
1665
private static short[] readShortTable(DataInputStream in, int len )
1666
throws IOException {
1667
if (len == 0) {
1668
return EMPTY_SHORT_ARRAY;
1669
}
1670
short[] data = new short[len];
1671
byte[] bb = new byte[len * 2];
1672
in.read(bb);
1673
int i = 0,j = 0;
1674
while (i < len) {
1675
data[i++] = (short)(bb[j++] << 8 | (bb[j++] & 0xff));
1676
}
1677
return data;
1678
}
1679
1680
private static void writeShortTable(DataOutputStream out, short[] data)
1681
throws IOException {
1682
for (short val : data) {
1683
out.writeShort(val);
1684
}
1685
}
1686
1687
private static short[] toList(HashMap<String, Short> map) {
1688
short[] list = new short[map.size()];
1689
Arrays.fill(list, (short) -1);
1690
for (Entry<String, Short> entry : map.entrySet()) {
1691
list[entry.getValue()] = getStringID(entry.getKey());
1692
}
1693
return list;
1694
}
1695
1696
//runtime cache
1697
private static String[] stringCache;
1698
protected static String getString(short stringID) {
1699
if (stringID == 0)
1700
return null;
1701
/*
1702
if (loadingProperties) {
1703
return stringTable.substring(stringIDs[stringID],
1704
stringIDs[stringID+1]);
1705
}
1706
*/
1707
//sync if we want it to be MT-enabled
1708
if (stringCache[stringID] == null){
1709
stringCache[stringID] =
1710
new String (table_stringTable,
1711
table_stringIDs[stringID],
1712
table_stringIDs[stringID+1] - table_stringIDs[stringID]);
1713
}
1714
return stringCache[stringID];
1715
}
1716
1717
private static short[] getShortArray(short shortArrayID) {
1718
String s = getString(shortArrayID);
1719
char[] cc = s.toCharArray();
1720
short[] ss = new short[cc.length];
1721
for (int i = 0; i < cc.length; i++) {
1722
ss[i] = (short)(cc[i] & 0xffff);
1723
}
1724
return ss;
1725
}
1726
1727
private static short getStringID(String s) {
1728
if (s == null) {
1729
return (short)0;
1730
}
1731
short pos0 = (short)stringTable.length();
1732
stringTable.append(s);
1733
short pos1 = (short)stringTable.length();
1734
1735
stringIDs[stringIDNum] = pos0;
1736
stringIDs[stringIDNum + 1] = pos1;
1737
stringIDNum++;
1738
if (stringIDNum + 1 >= stringIDs.length) {
1739
short[] tmp = new short[stringIDNum + 1000];
1740
System.arraycopy(stringIDs, 0, tmp, 0, stringIDNum);
1741
stringIDs = tmp;
1742
}
1743
return (short)(stringIDNum - 1);
1744
}
1745
1746
private static short getShortArrayID(short[] sa) {
1747
char[] cc = new char[sa.length];
1748
for (int i = 0; i < sa.length; i ++) {
1749
cc[i] = (char)sa[i];
1750
}
1751
String s = new String(cc);
1752
return getStringID(s);
1753
}
1754
1755
//utility "empty" objects
1756
private static final int[] EMPTY_INT_ARRAY = new int[0];
1757
private static final String[] EMPTY_STRING_ARRAY = new String[0];
1758
private static final short[] EMPTY_SHORT_ARRAY = new short[0];
1759
private static final String UNDEFINED_COMPONENT_FONT = "unknown";
1760
1761
//////////////////////////////////////////////////////////////////////////
1762
//Convert the FontConfig data in Properties file to binary data tables //
1763
//////////////////////////////////////////////////////////////////////////
1764
static class PropertiesHandler {
1765
public void load(InputStream in) throws IOException {
1766
initLogicalNameStyle();
1767
initHashMaps();
1768
FontProperties fp = new FontProperties();
1769
fp.load(in);
1770
initBinaryTable();
1771
}
1772
1773
private void initBinaryTable() {
1774
//(0)
1775
head = new short[HEAD_LENGTH];
1776
head[INDEX_scriptIDs] = (short)HEAD_LENGTH;
1777
1778
table_scriptIDs = toList(scriptIDs);
1779
//(1)a: scriptAllfonts scriptID/allfonts -> componentFontNameID
1780
// b: scriptFonts scriptID -> componentFontNameID[20]
1781
//if we have a "allfonts.script" def, then we just put
1782
//the "-platformFontID" value in the slot, otherwise the slot
1783
//value is "offset" which "offset" is where 20 entries located
1784
//in the table attached.
1785
head[INDEX_scriptFonts] = (short)(head[INDEX_scriptIDs] + table_scriptIDs.length);
1786
int len = table_scriptIDs.length + scriptFonts.size() * 20;
1787
table_scriptFonts = new short[len];
1788
1789
for (Entry<Short, Short> entry : scriptAllfonts.entrySet()) {
1790
table_scriptFonts[entry.getKey().intValue()] = entry.getValue();
1791
}
1792
int off = table_scriptIDs.length;
1793
for (Entry<Short, Short[]> entry : scriptFonts.entrySet()) {
1794
table_scriptFonts[entry.getKey().intValue()] = (short)-off;
1795
Short[] v = entry.getValue();
1796
for (int i = 0; i < 20; i++) {
1797
if (v[i] != null) {
1798
table_scriptFonts[off++] = v[i];
1799
} else {
1800
table_scriptFonts[off++] = 0;
1801
}
1802
}
1803
}
1804
1805
//(2)
1806
head[INDEX_elcIDs] = (short)(head[INDEX_scriptFonts] + table_scriptFonts.length);
1807
table_elcIDs = toList(elcIDs);
1808
1809
//(3) sequences elcID -> XXXX[1|5] -> scriptID[]
1810
head[INDEX_sequences] = (short)(head[INDEX_elcIDs] + table_elcIDs.length);
1811
table_sequences = new short[elcIDs.size() * NUM_FONTS];
1812
for (Entry<Short, short[]> entry : sequences.entrySet()) {
1813
//table_sequences[entry.getKey().intValue()] = (short)-off;
1814
int k = entry.getKey().intValue();
1815
short[] v = entry.getValue();
1816
/*
1817
System.out.println("elc=" + k + "/" + getString((short)table_elcIDs[k]));
1818
short[] ss = getShortArray(v[0]);
1819
for (int i = 0; i < ss.length; i++) {
1820
System.out.println(" " + getString((short)table_scriptIDs[ss[i]]));
1821
}
1822
*/
1823
if (v.length == 1) {
1824
//the "allfonts" entries
1825
for (int i = 0; i < NUM_FONTS; i++) {
1826
table_sequences[k * NUM_FONTS + i] = v[0];
1827
}
1828
} else {
1829
for (int i = 0; i < NUM_FONTS; i++) {
1830
table_sequences[k * NUM_FONTS + i] = v[i];
1831
}
1832
}
1833
}
1834
//(4)
1835
head[INDEX_fontfileNameIDs] = (short)(head[INDEX_sequences] + table_sequences.length);
1836
table_fontfileNameIDs = toList(fontfileNameIDs);
1837
1838
//(5)
1839
head[INDEX_componentFontNameIDs] = (short)(head[INDEX_fontfileNameIDs] + table_fontfileNameIDs.length);
1840
table_componentFontNameIDs = toList(componentFontNameIDs);
1841
1842
//(6)componentFontNameID -> filenameID
1843
head[INDEX_filenames] = (short)(head[INDEX_componentFontNameIDs] + table_componentFontNameIDs.length);
1844
table_filenames = new short[table_componentFontNameIDs.length];
1845
Arrays.fill(table_filenames, (short) -1);
1846
1847
for (Entry<Short, Short> entry : filenames.entrySet()) {
1848
table_filenames[entry.getKey()] = entry.getValue();
1849
}
1850
1851
//(7)scriptID-> awtfontpath
1852
//the paths are stored as scriptID -> stringID in awtfontpahts
1853
head[INDEX_awtfontpaths] = (short)(head[INDEX_filenames] + table_filenames.length);
1854
table_awtfontpaths = new short[table_scriptIDs.length];
1855
for (Entry<Short, Short> entry : awtfontpaths.entrySet()) {
1856
table_awtfontpaths[entry.getKey()] = entry.getValue();
1857
}
1858
1859
//(8)exclusions
1860
head[INDEX_exclusions] = (short)(head[INDEX_awtfontpaths] + table_awtfontpaths.length);
1861
table_exclusions = new short[scriptIDs.size()];
1862
for (Entry<Short, int[]> entry : exclusions.entrySet()) {
1863
int[] exI = entry.getValue();
1864
char[] exC = new char[exI.length * 2];
1865
int j = 0;
1866
for (int i = 0; i < exI.length; i++) {
1867
exC[j++] = (char) (exI[i] >> 16);
1868
exC[j++] = (char) (exI[i] & 0xffff);
1869
}
1870
table_exclusions[entry.getKey()] = getStringID(new String (exC));
1871
}
1872
//(9)proportionals
1873
head[INDEX_proportionals] = (short)(head[INDEX_exclusions] + table_exclusions.length);
1874
table_proportionals = new short[proportionals.size() * 2];
1875
int j = 0;
1876
for (Entry<Short, Short> entry : proportionals.entrySet()) {
1877
table_proportionals[j++] = entry.getKey();
1878
table_proportionals[j++] = entry.getValue();
1879
}
1880
1881
//(10) see (1) for info, the only difference is "xxx.motif"
1882
head[INDEX_scriptFontsMotif] = (short)(head[INDEX_proportionals] + table_proportionals.length);
1883
if (scriptAllfontsMotif.size() != 0 || scriptFontsMotif.size() != 0) {
1884
len = table_scriptIDs.length + scriptFontsMotif.size() * 20;
1885
table_scriptFontsMotif = new short[len];
1886
1887
for (Entry<Short, Short> entry : scriptAllfontsMotif.entrySet()) {
1888
table_scriptFontsMotif[entry.getKey().intValue()] =
1889
(short)entry.getValue();
1890
}
1891
off = table_scriptIDs.length;
1892
for (Entry<Short, Short[]> entry : scriptFontsMotif.entrySet()) {
1893
table_scriptFontsMotif[entry.getKey().intValue()] = (short)-off;
1894
Short[] v = entry.getValue();
1895
int i = 0;
1896
while (i < 20) {
1897
if (v[i] != null) {
1898
table_scriptFontsMotif[off++] = v[i];
1899
} else {
1900
table_scriptFontsMotif[off++] = 0;
1901
}
1902
i++;
1903
}
1904
}
1905
} else {
1906
table_scriptFontsMotif = EMPTY_SHORT_ARRAY;
1907
}
1908
1909
//(11)short[] alphabeticSuffix
1910
head[INDEX_alphabeticSuffix] = (short)(head[INDEX_scriptFontsMotif] + table_scriptFontsMotif.length);
1911
table_alphabeticSuffix = new short[alphabeticSuffix.size() * 2];
1912
j = 0;
1913
for (Entry<Short, Short> entry : alphabeticSuffix.entrySet()) {
1914
table_alphabeticSuffix[j++] = entry.getKey();
1915
table_alphabeticSuffix[j++] = entry.getValue();
1916
}
1917
1918
//(15)short[] fallbackScriptIDs; just put the ID in head
1919
head[INDEX_fallbackScripts] = getShortArrayID(fallbackScriptIDs);
1920
1921
//(16)appendedfontpath
1922
head[INDEX_appendedfontpath] = getStringID(appendedfontpath);
1923
1924
//(17)version
1925
head[INDEX_version] = getStringID(version);
1926
1927
//(12)short[] StringIDs
1928
head[INDEX_stringIDs] = (short)(head[INDEX_alphabeticSuffix] + table_alphabeticSuffix.length);
1929
table_stringIDs = new short[stringIDNum + 1];
1930
System.arraycopy(stringIDs, 0, table_stringIDs, 0, stringIDNum + 1);
1931
1932
//(13)StringTable
1933
head[INDEX_stringTable] = (short)(head[INDEX_stringIDs] + stringIDNum + 1);
1934
table_stringTable = stringTable.toString().toCharArray();
1935
//(14)
1936
head[INDEX_TABLEEND] = (short)(head[INDEX_stringTable] + stringTable.length());
1937
1938
//StringTable cache
1939
stringCache = new String[table_stringIDs.length];
1940
}
1941
1942
//////////////////////////////////////////////
1943
private HashMap<String, Short> scriptIDs;
1944
//elc -> Encoding.Language.Country
1945
private HashMap<String, Short> elcIDs;
1946
//componentFontNameID starts from "1", "0" reserves for "undefined"
1947
private HashMap<String, Short> componentFontNameIDs;
1948
private HashMap<String, Short> fontfileNameIDs;
1949
private HashMap<String, Integer> logicalFontIDs;
1950
private HashMap<String, Integer> fontStyleIDs;
1951
1952
//componentFontNameID -> fontfileNameID
1953
private HashMap<Short, Short> filenames;
1954
1955
//elcID -> allfonts/logicalFont -> scriptID list
1956
//(1)if we have a "allfonts", then the length of the
1957
// value array is "1", otherwise it's 5, each font
1958
// must have their own individual entry.
1959
//scriptID list "short[]" is stored as an ID
1960
private HashMap<Short, short[]> sequences;
1961
1962
//scriptID ->logicFontID/fontStyleID->componentFontNameID,
1963
//a 20-entry array (5-name x 4-style) for each script
1964
private HashMap<Short, Short[]> scriptFonts;
1965
1966
//scriptID -> componentFontNameID
1967
private HashMap<Short, Short> scriptAllfonts;
1968
1969
//scriptID -> exclusionRanges[]
1970
private HashMap<Short, int[]> exclusions;
1971
1972
//scriptID -> fontpath
1973
private HashMap<Short, Short> awtfontpaths;
1974
1975
//fontID -> fontID
1976
private HashMap<Short, Short> proportionals;
1977
1978
//scriptID -> componentFontNameID
1979
private HashMap<Short, Short> scriptAllfontsMotif;
1980
1981
//scriptID ->logicFontID/fontStyleID->componentFontNameID,
1982
private HashMap<Short, Short[]> scriptFontsMotif;
1983
1984
//elcID -> stringID of alphabetic/XXXX
1985
private HashMap<Short, Short> alphabeticSuffix;
1986
1987
private short[] fallbackScriptIDs;
1988
private String version;
1989
private String appendedfontpath;
1990
1991
private void initLogicalNameStyle() {
1992
logicalFontIDs = new HashMap<String, Integer>();
1993
fontStyleIDs = new HashMap<String, Integer>();
1994
logicalFontIDs.put("serif", 0);
1995
logicalFontIDs.put("sansserif", 1);
1996
logicalFontIDs.put("monospaced", 2);
1997
logicalFontIDs.put("dialog", 3);
1998
logicalFontIDs.put("dialoginput",4);
1999
fontStyleIDs.put("plain", 0);
2000
fontStyleIDs.put("bold", 1);
2001
fontStyleIDs.put("italic", 2);
2002
fontStyleIDs.put("bolditalic", 3);
2003
}
2004
2005
private void initHashMaps() {
2006
scriptIDs = new HashMap<String, Short>();
2007
elcIDs = new HashMap<String, Short>();
2008
componentFontNameIDs = new HashMap<String, Short>();
2009
/*Init these tables to allow componentFontNameID, fontfileNameIDs
2010
to start from "1".
2011
*/
2012
componentFontNameIDs.put("", Short.valueOf((short)0));
2013
2014
fontfileNameIDs = new HashMap<String, Short>();
2015
filenames = new HashMap<Short, Short>();
2016
sequences = new HashMap<Short, short[]>();
2017
scriptFonts = new HashMap<Short, Short[]>();
2018
scriptAllfonts = new HashMap<Short, Short>();
2019
exclusions = new HashMap<Short, int[]>();
2020
awtfontpaths = new HashMap<Short, Short>();
2021
proportionals = new HashMap<Short, Short>();
2022
scriptFontsMotif = new HashMap<Short, Short[]>();
2023
scriptAllfontsMotif = new HashMap<Short, Short>();
2024
alphabeticSuffix = new HashMap<Short, Short>();
2025
fallbackScriptIDs = EMPTY_SHORT_ARRAY;
2026
/*
2027
version
2028
appendedfontpath
2029
*/
2030
}
2031
2032
private int[] parseExclusions(String key, String exclusions) {
2033
if (exclusions == null) {
2034
return EMPTY_INT_ARRAY;
2035
}
2036
// range format is xxxx-XXXX,yyyyyy-YYYYYY,.....
2037
int numExclusions = 1;
2038
int pos = 0;
2039
while ((pos = exclusions.indexOf(',', pos)) != -1) {
2040
numExclusions++;
2041
pos++;
2042
}
2043
int[] exclusionRanges = new int[numExclusions * 2];
2044
pos = 0;
2045
int newPos = 0;
2046
for (int j = 0; j < numExclusions * 2; ) {
2047
String lower, upper;
2048
int lo = 0, up = 0;
2049
try {
2050
newPos = exclusions.indexOf('-', pos);
2051
lower = exclusions.substring(pos, newPos);
2052
pos = newPos + 1;
2053
newPos = exclusions.indexOf(',', pos);
2054
if (newPos == -1) {
2055
newPos = exclusions.length();
2056
}
2057
upper = exclusions.substring(pos, newPos);
2058
pos = newPos + 1;
2059
int lowerLength = lower.length();
2060
int upperLength = upper.length();
2061
if (lowerLength != 4 && lowerLength != 6
2062
|| upperLength != 4 && upperLength != 6) {
2063
throw new Exception();
2064
}
2065
lo = Integer.parseInt(lower, 16);
2066
up = Integer.parseInt(upper, 16);
2067
if (lo > up) {
2068
throw new Exception();
2069
}
2070
} catch (Exception e) {
2071
if (FontUtilities.debugFonts() &&
2072
logger != null) {
2073
logger.config("Failed parsing " + key +
2074
" property of font configuration.");
2075
2076
}
2077
return EMPTY_INT_ARRAY;
2078
}
2079
exclusionRanges[j++] = lo;
2080
exclusionRanges[j++] = up;
2081
}
2082
return exclusionRanges;
2083
}
2084
2085
private Short getID(HashMap<String, Short> map, String key) {
2086
Short ret = map.get(key);
2087
if ( ret == null) {
2088
map.put(key, (short)map.size());
2089
return map.get(key);
2090
}
2091
return ret;
2092
}
2093
2094
@SuppressWarnings("serial") // JDK-implementation class
2095
class FontProperties extends Properties {
2096
public synchronized Object put(Object k, Object v) {
2097
parseProperty((String)k, (String)v);
2098
return null;
2099
}
2100
}
2101
2102
private void parseProperty(String key, String value) {
2103
if (key.startsWith("filename.")) {
2104
//the only special case is "MingLiu_HKSCS" which has "_" in its
2105
//facename, we don't want to replace the "_" with " "
2106
key = key.substring(9);
2107
if (!"MingLiU_HKSCS".equals(key)) {
2108
key = key.replace('_', ' ');
2109
}
2110
Short faceID = getID(componentFontNameIDs, key);
2111
Short fileID = getID(fontfileNameIDs, value);
2112
//System.out.println("faceID=" + faceID + "/" + key + " -> "
2113
// + "fileID=" + fileID + "/" + value);
2114
filenames.put(faceID, fileID);
2115
} else if (key.startsWith("exclusion.")) {
2116
key = key.substring(10);
2117
exclusions.put(getID(scriptIDs,key), parseExclusions(key,value));
2118
} else if (key.startsWith("sequence.")) {
2119
key = key.substring(9);
2120
boolean hasDefault = false;
2121
boolean has1252 = false;
2122
2123
//get the scriptID list
2124
String[] ss = splitSequence(value).toArray(EMPTY_STRING_ARRAY);
2125
short [] sa = new short[ss.length];
2126
for (int i = 0; i < ss.length; i++) {
2127
if ("alphabetic/default".equals(ss[i])) {
2128
//System.out.println(key + " -> " + ss[i]);
2129
ss[i] = "alphabetic";
2130
hasDefault = true;
2131
} else if ("alphabetic/1252".equals(ss[i])) {
2132
//System.out.println(key + " -> " + ss[i]);
2133
ss[i] = "alphabetic";
2134
has1252 = true;
2135
}
2136
sa[i] = getID(scriptIDs, ss[i]).shortValue();
2137
//System.out.println("scriptID=" + si[i] + "/" + ss[i]);
2138
}
2139
//convert the "short[] -> string -> stringID"
2140
short scriptArrayID = getShortArrayID(sa);
2141
Short elcID = null;
2142
int dot = key.indexOf('.');
2143
if (dot == -1) {
2144
if ("fallback".equals(key)) {
2145
fallbackScriptIDs = sa;
2146
return;
2147
}
2148
if ("allfonts".equals(key)) {
2149
elcID = getID(elcIDs, "NULL.NULL.NULL");
2150
} else {
2151
if (logger != null) {
2152
logger.config("Error sequence def: <sequence." + key + ">");
2153
}
2154
return;
2155
}
2156
} else {
2157
elcID = getID(elcIDs, key.substring(dot + 1));
2158
//System.out.println("elcID=" + elcID + "/" + key.substring(dot + 1));
2159
key = key.substring(0, dot);
2160
}
2161
short[] scriptArrayIDs = null;
2162
if ("allfonts".equals(key)) {
2163
scriptArrayIDs = new short[1];
2164
scriptArrayIDs[0] = scriptArrayID;
2165
} else {
2166
scriptArrayIDs = sequences.get(elcID);
2167
if (scriptArrayIDs == null) {
2168
scriptArrayIDs = new short[5];
2169
}
2170
Integer fid = logicalFontIDs.get(key);
2171
if (fid == null) {
2172
if (logger != null) {
2173
logger.config("Unrecognizable logicfont name " + key);
2174
}
2175
return;
2176
}
2177
//System.out.println("sequence." + key + "/" + id);
2178
scriptArrayIDs[fid.intValue()] = scriptArrayID;
2179
}
2180
sequences.put(elcID, scriptArrayIDs);
2181
if (hasDefault) {
2182
alphabeticSuffix.put(elcID, getStringID("default"));
2183
} else
2184
if (has1252) {
2185
alphabeticSuffix.put(elcID, getStringID("1252"));
2186
}
2187
} else if (key.startsWith("allfonts.")) {
2188
key = key.substring(9);
2189
if (key.endsWith(".motif")) {
2190
key = key.substring(0, key.length() - 6);
2191
//System.out.println("motif: all." + key + "=" + value);
2192
scriptAllfontsMotif.put(getID(scriptIDs,key), getID(componentFontNameIDs,value));
2193
} else {
2194
scriptAllfonts.put(getID(scriptIDs,key), getID(componentFontNameIDs,value));
2195
}
2196
} else if (key.startsWith("awtfontpath.")) {
2197
key = key.substring(12);
2198
//System.out.println("scriptID=" + getID(scriptIDs, key) + "/" + key);
2199
awtfontpaths.put(getID(scriptIDs, key), getStringID(value));
2200
} else if ("version".equals(key)) {
2201
version = value;
2202
} else if ("appendedfontpath".equals(key)) {
2203
appendedfontpath = value;
2204
} else if (key.startsWith("proportional.")) {
2205
key = key.substring(13).replace('_', ' ');
2206
//System.out.println(key + "=" + value);
2207
proportionals.put(getID(componentFontNameIDs, key),
2208
getID(componentFontNameIDs, value));
2209
} else {
2210
//"name.style.script(.motif)", we don't care anything else
2211
int dot1, dot2;
2212
boolean isMotif = false;
2213
2214
dot1 = key.indexOf('.');
2215
if (dot1 == -1) {
2216
if (logger != null) {
2217
logger.config("Failed parsing " + key +
2218
" property of font configuration.");
2219
2220
}
2221
return;
2222
}
2223
dot2 = key.indexOf('.', dot1 + 1);
2224
if (dot2 == -1) {
2225
if (logger != null) {
2226
logger.config("Failed parsing " + key +
2227
" property of font configuration.");
2228
2229
}
2230
return;
2231
}
2232
if (key.endsWith(".motif")) {
2233
key = key.substring(0, key.length() - 6);
2234
isMotif = true;
2235
//System.out.println("motif: " + key + "=" + value);
2236
}
2237
Integer nameID = logicalFontIDs.get(key.substring(0, dot1));
2238
Integer styleID = fontStyleIDs.get(key.substring(dot1+1, dot2));
2239
Short scriptID = getID(scriptIDs, key.substring(dot2 + 1));
2240
if (nameID == null || styleID == null) {
2241
if (logger != null) {
2242
logger.config("unrecognizable logicfont name/style at " + key);
2243
}
2244
return;
2245
}
2246
Short[] pnids;
2247
if (isMotif) {
2248
pnids = scriptFontsMotif.get(scriptID);
2249
} else {
2250
pnids = scriptFonts.get(scriptID);
2251
}
2252
if (pnids == null) {
2253
pnids = new Short[20];
2254
}
2255
pnids[nameID.intValue() * NUM_STYLES + styleID.intValue()]
2256
= getID(componentFontNameIDs, value);
2257
/*
2258
System.out.println("key=" + key + "/<" + nameID + "><" + styleID
2259
+ "><" + scriptID + ">=" + value
2260
+ "/" + getID(componentFontNameIDs, value));
2261
*/
2262
if (isMotif) {
2263
scriptFontsMotif.put(scriptID, pnids);
2264
} else {
2265
scriptFonts.put(scriptID, pnids);
2266
}
2267
}
2268
}
2269
}
2270
}
2271
2272