Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/jdk17u
Path: blob/master/src/java.desktop/unix/classes/sun/font/FcFontConfiguration.java
66645 views
1
/*
2
* Copyright (c) 2008, 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.font;
27
28
import java.io.File;
29
import java.io.FileInputStream;
30
import java.io.FileOutputStream;
31
import java.io.IOException;
32
import java.net.InetAddress;
33
import java.net.UnknownHostException;
34
import java.nio.charset.Charset;
35
import java.nio.charset.StandardCharsets;
36
import java.nio.file.Files;
37
import java.util.HashMap;
38
import java.util.HashSet;
39
import java.util.Locale;
40
import java.util.Properties;
41
import java.util.Scanner;
42
import sun.awt.FcFontManager;
43
import sun.awt.FontConfiguration;
44
import sun.awt.FontDescriptor;
45
import sun.awt.SunToolkit;
46
import sun.font.CompositeFontDescriptor;
47
import sun.font.FontConfigManager.FontConfigInfo;
48
import sun.font.FontConfigManager.FcCompFont;
49
import sun.font.FontConfigManager.FontConfigFont;
50
import sun.util.logging.PlatformLogger;
51
52
public class FcFontConfiguration extends FontConfiguration {
53
54
/** Version of the cache file format understood by this code.
55
* Its part of the file name so that we can rev this at
56
* any time, even in a minor JDK update.
57
* It is stored as the value of the "version" property.
58
* This is distinct from the version of "libfontconfig" that generated
59
* the cached results, and which is the "fcversion" property in the file.
60
* {@code FontConfiguration.getVersion()} also returns a version string,
61
* and has meant the version of the fontconfiguration.properties file
62
* that was read. Since this class doesn't use such files, then what
63
* that really means is whether the methods on this class return
64
* values that are compatible with the classes that do directly read
65
* from such files. It is a compatible subset of version "1".
66
*/
67
private static final String fileVersion = "1";
68
private String fcInfoFileName = null;
69
70
private FcCompFont[] fcCompFonts = null;
71
72
public FcFontConfiguration(SunFontManager fm) {
73
super(fm);
74
init();
75
}
76
77
/* This isn't called but is needed to satisfy super-class contract. */
78
public FcFontConfiguration(SunFontManager fm,
79
boolean preferLocaleFonts,
80
boolean preferPropFonts) {
81
super(fm, preferLocaleFonts, preferPropFonts);
82
init();
83
}
84
85
@Override
86
public synchronized boolean init() {
87
if (fcCompFonts != null) {
88
return true;
89
}
90
91
setFontConfiguration();
92
readFcInfo();
93
FcFontManager fm = (FcFontManager) fontManager;
94
FontConfigManager fcm = fm.getFontConfigManager();
95
if (fcCompFonts == null) {
96
fcCompFonts = fcm.loadFontConfig();
97
if (fcCompFonts != null) {
98
try {
99
writeFcInfo();
100
} catch (Exception e) {
101
if (FontUtilities.debugFonts()) {
102
warning("Exception writing fcInfo " + e);
103
}
104
}
105
} else if (FontUtilities.debugFonts()) {
106
warning("Failed to get info from libfontconfig");
107
}
108
} else {
109
fcm.populateFontConfig(fcCompFonts);
110
}
111
112
if (fcCompFonts == null) {
113
return false; // couldn't load fontconfig.
114
}
115
116
// NB already in a privileged block from SGE
117
String javaHome = System.getProperty("java.home");
118
if (javaHome == null) {
119
throw new Error("java.home property not set");
120
}
121
String javaLib = javaHome + File.separator + "lib";
122
getInstalledFallbackFonts(javaLib);
123
124
return true;
125
}
126
127
@Override
128
public String getFallbackFamilyName(String fontName,
129
String defaultFallback) {
130
// maintain compatibility with old font.properties files, which either
131
// had aliases for TimesRoman & Co. or defined mappings for them.
132
String compatibilityName = getCompatibilityFamilyName(fontName);
133
if (compatibilityName != null) {
134
return compatibilityName;
135
}
136
return defaultFallback;
137
}
138
139
@Override
140
protected String
141
getFaceNameFromComponentFontName(String componentFontName) {
142
return null;
143
}
144
145
@Override
146
protected String
147
getFileNameFromComponentFontName(String componentFontName) {
148
return null;
149
}
150
151
@Override
152
public String getFileNameFromPlatformName(String platformName) {
153
/* Platform name is the file name, but rather than returning
154
* the arg, return null*/
155
return null;
156
}
157
158
@Override
159
protected Charset getDefaultFontCharset(String fontName) {
160
return Charset.forName("ISO8859_1");
161
}
162
163
@Override
164
protected String getEncoding(String awtFontName,
165
String characterSubsetName) {
166
return "default";
167
}
168
169
@Override
170
protected void initReorderMap() {
171
reorderMap = new HashMap<>();
172
}
173
174
@Override
175
protected FontDescriptor[] buildFontDescriptors(int fontIndex, int styleIndex) {
176
CompositeFontDescriptor[] cfi = get2DCompositeFontInfo();
177
int idx = fontIndex * NUM_STYLES + styleIndex;
178
String[] componentFaceNames = cfi[idx].getComponentFaceNames();
179
FontDescriptor[] ret = new FontDescriptor[componentFaceNames.length];
180
for (int i = 0; i < componentFaceNames.length; i++) {
181
ret[i] = new FontDescriptor(componentFaceNames[i], StandardCharsets.ISO_8859_1.newEncoder(), new int[0]);
182
}
183
184
return ret;
185
}
186
187
@Override
188
public int getNumberCoreFonts() {
189
return 1;
190
}
191
192
@Override
193
public String[] getPlatformFontNames() {
194
HashSet<String> nameSet = new HashSet<String>();
195
FcFontManager fm = (FcFontManager) fontManager;
196
FontConfigManager fcm = fm.getFontConfigManager();
197
FcCompFont[] fcCompFonts = fcm.loadFontConfig();
198
for (int i=0; i<fcCompFonts.length; i++) {
199
for (int j=0; j<fcCompFonts[i].allFonts.length; j++) {
200
nameSet.add(fcCompFonts[i].allFonts[j].fontFile);
201
}
202
}
203
return nameSet.toArray(new String[0]);
204
}
205
206
@Override
207
public String getExtraFontPath() {
208
return null;
209
}
210
211
@Override
212
public boolean needToSearchForFile(String fileName) {
213
return false;
214
}
215
216
private FontConfigFont[] getFcFontList(FcCompFont[] fcFonts,
217
String fontname, int style) {
218
219
if (fontname.equals("dialog")) {
220
fontname = "sansserif";
221
} else if (fontname.equals("dialoginput")) {
222
fontname = "monospaced";
223
}
224
for (int i=0; i<fcFonts.length; i++) {
225
if (fontname.equals(fcFonts[i].jdkName) &&
226
style == fcFonts[i].style) {
227
return fcFonts[i].allFonts;
228
}
229
}
230
return fcFonts[0].allFonts;
231
}
232
233
@Override
234
public CompositeFontDescriptor[] get2DCompositeFontInfo() {
235
236
FcFontManager fm = (FcFontManager) fontManager;
237
FontConfigManager fcm = fm.getFontConfigManager();
238
FcCompFont[] fcCompFonts = fcm.loadFontConfig();
239
240
CompositeFontDescriptor[] result =
241
new CompositeFontDescriptor[NUM_FONTS * NUM_STYLES];
242
243
for (int fontIndex = 0; fontIndex < NUM_FONTS; fontIndex++) {
244
String fontName = publicFontNames[fontIndex];
245
246
for (int styleIndex = 0; styleIndex < NUM_STYLES; styleIndex++) {
247
248
String faceName = fontName + "." + styleNames[styleIndex];
249
FontConfigFont[] fcFonts =
250
getFcFontList(fcCompFonts,
251
fontNames[fontIndex], styleIndex);
252
253
int numFonts = fcFonts.length;
254
// fall back fonts listed in the lib/fonts/fallback directory
255
if (installedFallbackFontFiles != null) {
256
numFonts += installedFallbackFontFiles.length;
257
}
258
259
String[] fileNames = new String[numFonts];
260
String[] faceNames = new String[numFonts];
261
262
int index;
263
for (index = 0; index < fcFonts.length; index++) {
264
fileNames[index] = fcFonts[index].fontFile;
265
faceNames[index] = fcFonts[index].fullName;
266
}
267
268
if (installedFallbackFontFiles != null) {
269
System.arraycopy(installedFallbackFontFiles, 0,
270
fileNames, fcFonts.length,
271
installedFallbackFontFiles.length);
272
}
273
274
result[fontIndex * NUM_STYLES + styleIndex]
275
= new CompositeFontDescriptor(
276
faceName,
277
1,
278
faceNames,
279
fileNames,
280
null, null);
281
}
282
}
283
return result;
284
}
285
286
/**
287
* Gets the OS version string from a Linux release-specific file.
288
*/
289
private String getVersionString(File f) {
290
try (Scanner sc = new Scanner(f)) {
291
return sc.findInLine("(\\d)+((\\.)(\\d)+)*");
292
} catch (Exception e) {
293
}
294
return null;
295
}
296
297
private String extractOsInfo(String s) {
298
if (s.startsWith("\"")) s = s.substring(1);
299
if (s.endsWith("\"")) s = s.substring(0, s.length()-1);
300
return s;
301
}
302
303
/**
304
* Sets the OS name and version from environment information.
305
*/
306
@Override
307
protected void setOsNameAndVersion() {
308
309
super.setOsNameAndVersion();
310
311
if (!osName.equals("Linux")) {
312
return;
313
}
314
try {
315
File f;
316
if ((f = new File("/etc/lsb-release")).canRead()) {
317
/* Ubuntu and (perhaps others) use only lsb-release.
318
* Syntax and encoding is compatible with java properties.
319
* For Ubuntu the ID is "Ubuntu".
320
*/
321
Properties props = new Properties();
322
props.load(new FileInputStream(f));
323
osName = props.getProperty("DISTRIB_ID");
324
osVersion = props.getProperty("DISTRIB_RELEASE");
325
} else if ((f = new File("/etc/redhat-release")).canRead()) {
326
osName = "RedHat";
327
osVersion = getVersionString(f);
328
} else if ((f = new File("/etc/SuSE-release")).canRead()) {
329
osName = "SuSE";
330
osVersion = getVersionString(f);
331
} else if ((f = new File("/etc/turbolinux-release")).canRead()) {
332
osName = "Turbo";
333
osVersion = getVersionString(f);
334
} else if ((f = new File("/etc/fedora-release")).canRead()) {
335
osName = "Fedora";
336
osVersion = getVersionString(f);
337
} else if ((f = new File("/etc/os-release")).canRead()) {
338
Properties props = new Properties();
339
try (FileInputStream fis = new FileInputStream(f)) {
340
props.load(fis);
341
}
342
osName = props.getProperty("NAME");
343
osVersion = props.getProperty("VERSION_ID");
344
osName = extractOsInfo(osName);
345
if (osName.equals("SLES")) osName = "SuSE";
346
osVersion = extractOsInfo(osVersion);
347
}
348
} catch (Exception e) {
349
if (FontUtilities.debugFonts()) {
350
warning("Exception identifying Linux distro.");
351
}
352
}
353
}
354
355
private File getFcInfoFile() {
356
if (fcInfoFileName == null) {
357
// NB need security permissions to get true IP address, and
358
// we should have those as the whole initialisation is in a
359
// doPrivileged block. But in this case no exception is thrown,
360
// and it returns the loop back address, and so we end up with
361
// "localhost"
362
String hostname;
363
try {
364
hostname = InetAddress.getLocalHost().getHostName();
365
} catch (UnknownHostException e) {
366
hostname = "localhost";
367
}
368
String userDir = System.getProperty("user.home");
369
String version = System.getProperty("java.version");
370
String fs = File.separator;
371
String dir = userDir+fs+".java"+fs+"fonts"+fs+version;
372
Locale locale = SunToolkit.getStartupLocale();
373
String lang = locale.getLanguage();
374
String country = locale.getCountry();
375
String name = "fcinfo-"+fileVersion+"-"+hostname+"-"+
376
osName+"-"+osVersion+"-"+lang+"-"+country+".properties";
377
fcInfoFileName = dir+fs+name;
378
}
379
return new File(fcInfoFileName);
380
}
381
382
private void writeFcInfo() {
383
Properties props = new Properties();
384
props.setProperty("version", fileVersion);
385
FcFontManager fm = (FcFontManager) fontManager;
386
FontConfigManager fcm = fm.getFontConfigManager();
387
FontConfigInfo fcInfo = fcm.getFontConfigInfo();
388
props.setProperty("fcversion", Integer.toString(fcInfo.fcVersion));
389
if (fcInfo.cacheDirs != null) {
390
for (int i=0;i<fcInfo.cacheDirs.length;i++) {
391
if (fcInfo.cacheDirs[i] != null) {
392
props.setProperty("cachedir."+i, fcInfo.cacheDirs[i]);
393
}
394
}
395
}
396
for (int i=0; i<fcCompFonts.length; i++) {
397
FcCompFont fci = fcCompFonts[i];
398
String styleKey = fci.jdkName+"."+fci.style;
399
props.setProperty(styleKey+".length",
400
Integer.toString(fci.allFonts.length));
401
for (int j=0; j<fci.allFonts.length; j++) {
402
props.setProperty(styleKey+"."+j+".file",
403
fci.allFonts[j].fontFile);
404
if (fci.allFonts[j].fullName != null) {
405
props.setProperty(styleKey+"."+j+".fullName",
406
fci.allFonts[j].fullName);
407
}
408
}
409
}
410
try {
411
/* This writes into a temp file then renames when done.
412
* Since the rename is an atomic action within the same
413
* directory no client will ever see a partially written file.
414
*/
415
File fcInfoFile = getFcInfoFile();
416
File dir = fcInfoFile.getParentFile();
417
dir.mkdirs();
418
File tempFile = Files.createTempFile(dir.toPath(), "fcinfo", null).toFile();
419
FileOutputStream fos = new FileOutputStream(tempFile);
420
props.store(fos,
421
"JDK Font Configuration Generated File: *Do Not Edit*");
422
fos.close();
423
boolean renamed = tempFile.renameTo(fcInfoFile);
424
if (!renamed && FontUtilities.debugFonts()) {
425
System.out.println("rename failed");
426
warning("Failed renaming file to "+ getFcInfoFile());
427
}
428
} catch (Exception e) {
429
if (FontUtilities.debugFonts()) {
430
warning("IOException writing to "+ getFcInfoFile());
431
}
432
}
433
}
434
435
/* We want to be able to use this cache instead of invoking
436
* fontconfig except when we can detect the system cache has changed.
437
* But there doesn't seem to be a way to find the location of
438
* the system cache.
439
*/
440
private void readFcInfo() {
441
File fcFile = getFcInfoFile();
442
if (!fcFile.exists()) {
443
if (FontUtilities.debugFonts()) {
444
warning("fontconfig info file " + fcFile.toString() + " does not exist");
445
}
446
return;
447
}
448
Properties props = new Properties();
449
try (FileInputStream fis = new FileInputStream(fcFile)) {
450
props.load(fis);
451
} catch (IOException e) {
452
if (FontUtilities.debugFonts()) {
453
warning("IOException (" + e.getCause() + ") reading from " + fcFile.toString());
454
}
455
return;
456
}
457
String version = (String)props.get("version");
458
if (version == null || !version.equals(fileVersion)) {
459
if (FontUtilities.debugFonts()) {
460
warning("fontconfig info file version mismatch (found: " + version +
461
", expected: " + fileVersion + ")");
462
}
463
return;
464
}
465
466
// If there's a new, different fontconfig installed on the
467
// system, we invalidate our fontconfig file.
468
String fcVersionStr = (String)props.get("fcversion");
469
if (fcVersionStr != null) {
470
int fcVersion;
471
try {
472
fcVersion = Integer.parseInt(fcVersionStr);
473
if (fcVersion != 0 &&
474
fcVersion != FontConfigManager.getFontConfigVersion()) {
475
if (FontUtilities.debugFonts()) {
476
warning("new, different fontconfig detected");
477
}
478
return;
479
}
480
} catch (Exception e) {
481
if (FontUtilities.debugFonts()) {
482
warning("Exception parsing version " + fcVersionStr);
483
}
484
return;
485
}
486
}
487
488
// If we can locate the fontconfig cache dirs, then compare the
489
// time stamp of those with our properties file. If we are out
490
// of date then re-generate.
491
long lastModified = fcFile.lastModified();
492
int cacheDirIndex = 0;
493
while (cacheDirIndex<4) { // should never be more than 2 anyway.
494
String dir = (String)props.get("cachedir."+cacheDirIndex);
495
if (dir == null) {
496
break;
497
}
498
File dirFile = new File(dir);
499
if (dirFile.exists() && dirFile.lastModified() > lastModified) {
500
if (FontUtilities.debugFonts()) {
501
warning("out of date cache directories detected");
502
}
503
return;
504
}
505
cacheDirIndex++;
506
}
507
508
String[] names = { "sansserif", "serif", "monospaced" };
509
String[] fcnames = { "sans", "serif", "monospace" };
510
int namesLen = names.length;
511
int numStyles = 4;
512
FcCompFont[] fci = new FcCompFont[namesLen*numStyles];
513
514
try {
515
for (int i=0; i<namesLen; i++) {
516
for (int s=0; s<numStyles; s++) {
517
int index = i*numStyles+s;
518
fci[index] = new FcCompFont();
519
String key = names[i]+"."+s;
520
fci[index].jdkName = names[i];
521
fci[index].fcFamily = fcnames[i];
522
fci[index].style = s;
523
String lenStr = (String)props.get(key+".length");
524
int nfonts = Integer.parseInt(lenStr);
525
if (nfonts <= 0) {
526
if (FontUtilities.debugFonts()) {
527
warning("bad non-positive .length entry in fontconfig file " + fcFile.toString());
528
}
529
return; // bad file
530
}
531
fci[index].allFonts = new FontConfigFont[nfonts];
532
for (int f=0; f<nfonts; f++) {
533
fci[index].allFonts[f] = new FontConfigFont();
534
String fkey = key+"."+f+".fullName";
535
String fullName = (String)props.get(fkey);
536
fci[index].allFonts[f].fullName = fullName;
537
fkey = key+"."+f+".file";
538
String file = (String)props.get(fkey);
539
if (file == null) {
540
if (FontUtilities.debugFonts()) {
541
warning("missing file value for key " + fkey + " in fontconfig file " + fcFile.toString());
542
}
543
return; // bad file
544
}
545
fci[index].allFonts[f].fontFile = file;
546
}
547
fci[index].firstFont = fci[index].allFonts[0];
548
549
}
550
}
551
fcCompFonts = fci;
552
} catch (Throwable t) {
553
if (FontUtilities.debugFonts()) {
554
warning(t.toString());
555
}
556
}
557
558
if (FontUtilities.debugFonts()) {
559
FontUtilities.logInfo("successfully parsed the fontconfig file at " + fcFile.toString());
560
}
561
}
562
563
private static void warning(String msg) {
564
PlatformLogger logger = PlatformLogger.getLogger("sun.awt.FontConfiguration");
565
logger.warning(msg);
566
}
567
}
568
569