303 }
304
305 /* Initialise ptrs used by JNI methods */
306 private static native void initIDs();
307
308 protected SunFontManager() {
309 AccessController.doPrivileged(new PrivilegedAction<Void>() {
310 public Void run() {
311 File badFontFile =
312 new File(jreFontDirName + File.separator + "badfonts.txt");
313 if (badFontFile.exists()) {
314 badFonts = new ArrayList<>();
315 try (FileInputStream fis = new FileInputStream(badFontFile);
316 BufferedReader br = new BufferedReader(new InputStreamReader(fis))) {
317 while (true) {
318 String name = br.readLine();
319 if (name == null) {
320 break;
321 } else {
322 if (FontUtilities.debugFonts()) {
323 FontUtilities.getLogger().warning("read bad font: " + name);
324 }
325 badFonts.add(name);
326 }
327 }
328 } catch (IOException e) {
329 }
330 }
331
332 /* Here we get the fonts in jre/lib/fonts and register
333 * them so they are always available and preferred over
334 * other fonts. This needs to be registered before the
335 * composite fonts as otherwise some native font that
336 * corresponds may be found as we don't have a way to
337 * handle two fonts of the same name, so the JRE one
338 * must be the first one registered. Pass "true" to
339 * registerFonts method as on-screen these JRE fonts
340 * always go through the JDK rasteriser.
341 */
342 if (FontUtilities.isLinux) {
343 /* Linux font configuration uses these fonts */
344 registerFontDir(jreFontDirName);
345 }
346 registerFontsInDir(jreFontDirName, true, Font2D.JRE_RANK,
347 true, false);
348
349 /* Create the font configuration and get any font path
350 * that might be specified.
351 */
352 fontConfig = createFontConfiguration();
353
354 String[] fontInfo = getDefaultPlatformFont();
355 defaultFontName = fontInfo[0];
356 if (defaultFontName == null && FontUtilities.debugFonts()) {
357 FontUtilities.getLogger().warning("defaultFontName is null");
358 }
359 defaultFontFileName = fontInfo[1];
360
361 String extraFontPath = fontConfig.getExtraFontPath();
362
363 /* In prior releases the debugging font path replaced
364 * all normally located font directories except for the
365 * JRE fonts dir. This directory is still always located
366 * and placed at the head of the path but as an
367 * augmentation to the previous behaviour the
368 * changes below allow you to additionally append to
369 * the font path by starting with append: or prepend by
370 * starting with a prepend: sign. Eg: to append
371 * -Dsun.java2d.fontpath=append:/usr/local/myfonts
372 * and to prepend
373 * -Dsun.java2d.fontpath=prepend:/usr/local/myfonts Disp
374 *
375 * If there is an appendedfontpath it in the font
376 * configuration it is used instead of searching the
377 * system for dirs.
386 * locale-specific so its almost impossible to get
387 * right, so it should be used with caution.
388 */
389 boolean prependToPath = false;
390 boolean appendToPath = false;
391 String dbgFontPath = System.getProperty("sun.java2d.fontpath");
392
393 if (dbgFontPath != null) {
394 if (dbgFontPath.startsWith("prepend:")) {
395 prependToPath = true;
396 dbgFontPath =
397 dbgFontPath.substring("prepend:".length());
398 } else if (dbgFontPath.startsWith("append:")) {
399 appendToPath = true;
400 dbgFontPath =
401 dbgFontPath.substring("append:".length());
402 }
403 }
404
405 if (FontUtilities.debugFonts()) {
406 PlatformLogger logger = FontUtilities.getLogger();
407 logger.info("JRE font directory: " + jreFontDirName);
408 logger.info("Extra font path: " + extraFontPath);
409 logger.info("Debug font path: " + dbgFontPath);
410 }
411
412 if (dbgFontPath != null) {
413 /* In debugging mode we register all the paths
414 * Caution: this is a very expensive call on Solaris:-
415 */
416 fontPath = getPlatformFontPath(noType1Font);
417
418 if (extraFontPath != null) {
419 fontPath = extraFontPath + File.pathSeparator + fontPath;
420 }
421 if (appendToPath) {
422 fontPath += File.pathSeparator + dbgFontPath;
423 } else if (prependToPath) {
424 fontPath = dbgFontPath + File.pathSeparator + fontPath;
425 } else {
426 fontPath = dbgFontPath;
427 }
428 registerFontDirs(fontPath);
429 } else if (extraFontPath != null) {
545 * its handle point to this new font.
546 * This ensures that when the altNameCache that is passed in
547 * is the global mapNameCache - ie we are running as an application -
548 * that any statically created java.awt.Font instances which already
549 * have a Font2D instance will have that re-directed to the new Font
550 * on subsequent uses. This is particularly important for "the"
551 * default font instance, or similar cases where a UI toolkit (eg
552 * Swing) has cached a java.awt.Font. Note that if Swing is using
553 * a custom composite APIs which update the standard composites have
554 * no effect - this is typically the case only when using the Windows
555 * L&F where these APIs would conflict with that L&F anyway.
556 */
557 Font2D oldFont =altNameCache.get(compositeName.toLowerCase(Locale.ENGLISH));
558 if (oldFont instanceof CompositeFont) {
559 oldFont.handle.font2D = cf;
560 }
561 altNameCache.put(compositeName.toLowerCase(Locale.ENGLISH), cf);
562 }
563
564 private void addCompositeToFontList(CompositeFont f, int rank) {
565
566 if (FontUtilities.isLogging()) {
567 FontUtilities.getLogger().info("Add to Family "+ f.familyName +
568 ", Font " + f.fullName + " rank="+rank);
569 }
570 f.setRank(rank);
571 compositeFonts.put(f.fullName, f);
572 fullNameToFont.put(f.fullName.toLowerCase(Locale.ENGLISH), f);
573
574 FontFamily family = FontFamily.getFamily(f.familyName);
575 if (family == null) {
576 family = new FontFamily(f.familyName, true, rank);
577 }
578 family.setFont(f, f.style);
579 }
580
581 /*
582 * Systems may have fonts with the same name.
583 * We want to register only one of such fonts (at least until
584 * such time as there might be APIs which can accommodate > 1).
585 * Rank is 1) font configuration fonts, 2) JRE fonts, 3) OT/TT fonts,
586 * 4) Type1 fonts, 5) native fonts.
587 *
588 * If the new font has the same name as the old font, the higher
589 * ranked font gets added, replacing the lower ranked one.
607 * If it returns a different object it means this font already exists,
608 * and you should use that one.
609 * If it returns null means this font was not registered and none
610 * in that name is registered. The caller must find a substitute
611 */
612 // MACOSX begin -- need to access this in subclass
613 protected PhysicalFont addToFontList(PhysicalFont f, int rank) {
614 // MACOSX end
615
616 String fontName = f.fullName;
617 String familyName = f.familyName;
618 if (fontName == null || fontName.isEmpty()) {
619 return null;
620 }
621 if (compositeFonts.containsKey(fontName)) {
622 /* Don't register any font that has the same name as a composite */
623 return null;
624 }
625 f.setRank(rank);
626 if (!physicalFonts.containsKey(fontName)) {
627 if (FontUtilities.isLogging()) {
628 FontUtilities.getLogger().info("Add to Family "+familyName +
629 ", Font " + fontName + " rank="+rank);
630 }
631 physicalFonts.put(fontName, f);
632 FontFamily family = FontFamily.getFamily(familyName);
633 if (family == null) {
634 family = new FontFamily(familyName, false, rank);
635 family.setFont(f, f.style);
636 } else {
637 family.setFont(f, f.style);
638 }
639 fullNameToFont.put(fontName.toLowerCase(Locale.ENGLISH), f);
640 return f;
641 } else {
642 PhysicalFont newFont = f;
643 PhysicalFont oldFont = physicalFonts.get(fontName);
644 if (oldFont == null) {
645 return null;
646 }
647 /* If the new font is of an equal or higher rank, it is a
648 * candidate to replace the current one, subject to further tests.
649 */
650 if (oldFont.getRank() >= rank) {
681 newFont instanceof TrueTypeFont) {
682 TrueTypeFont oldTTFont = (TrueTypeFont)oldFont;
683 TrueTypeFont newTTFont = (TrueTypeFont)newFont;
684 if (oldTTFont.fileSize >= newTTFont.fileSize) {
685 return oldFont;
686 }
687 } else {
688 return oldFont;
689 }
690 }
691 /* Don't replace ever JRE fonts.
692 * This test is in case a font configuration references
693 * a Lucida font, which has been mapped to a Lucida
694 * from the host O/S. The assumption here is that any
695 * such font configuration file is probably incorrect, or
696 * the host O/S version is for the use of AWT.
697 * In other words if we reach here, there's a possible
698 * problem with our choice of font configuration fonts.
699 */
700 if (oldFont.platName.startsWith(jreFontDirName)) {
701 if (FontUtilities.isLogging()) {
702 FontUtilities.getLogger()
703 .warning("Unexpected attempt to replace a JRE " +
704 " font " + fontName + " from " +
705 oldFont.platName +
706 " with " + newFont.platName);
707 }
708 return oldFont;
709 }
710
711 if (FontUtilities.isLogging()) {
712 FontUtilities.getLogger()
713 .info("Replace in Family " + familyName +
714 ",Font " + fontName + " new rank="+rank +
715 " from " + oldFont.platName +
716 " with " + newFont.platName);
717 }
718 replaceFont(oldFont, newFont);
719 physicalFonts.put(fontName, newFont);
720 fullNameToFont.put(fontName.toLowerCase(Locale.ENGLISH),
721 newFont);
722
723 FontFamily family = FontFamily.getFamily(familyName);
724 if (family == null) {
725 family = new FontFamily(familyName, false, rank);
726 family.setFont(newFont, newFont.style);
727 } else {
728 family.setFont(newFont, newFont.style);
729 }
730 return newFont;
731 } else {
732 return oldFont;
733 }
734 }
735 }
736
737 public Font2D[] getRegisteredFonts() {
885
886 public void registerDeferredFont(String fileNameKey,
887 String fullPathName,
888 String[] nativeNames,
889 int fontFormat,
890 boolean useJavaRasterizer,
891 int fontRank) {
892 FontRegistrationInfo regInfo =
893 new FontRegistrationInfo(fullPathName, nativeNames, fontFormat,
894 useJavaRasterizer, fontRank);
895 deferredFontFiles.put(fileNameKey, regInfo);
896 }
897
898
899 public synchronized
900 PhysicalFont initialiseDeferredFont(String fileNameKey) {
901
902 if (fileNameKey == null) {
903 return null;
904 }
905 if (FontUtilities.isLogging()) {
906 FontUtilities.getLogger()
907 .info("Opening deferred font file " + fileNameKey);
908 }
909
910 PhysicalFont physicalFont = null;
911 FontRegistrationInfo regInfo = deferredFontFiles.get(fileNameKey);
912 if (regInfo != null) {
913 deferredFontFiles.remove(fileNameKey);
914 physicalFont = registerFontFile(regInfo.fontFilePath,
915 regInfo.nativeNames,
916 regInfo.fontFormat,
917 regInfo.javaRasterizer,
918 regInfo.fontRank);
919
920 if (physicalFont != null) {
921 /* Store the handle, so that if a font is bad, we
922 * retrieve the substituted font.
923 */
924 initialisedFonts.put(fileNameKey, physicalFont.handle);
925 } else {
926 initialisedFonts.put(fileNameKey, FONT_HANDLE_NULL);
927 }
928 } else {
973 PhysicalFont pf = addToFontList(ttf, fontRank);
974 if (physicalFont == null) {
975 physicalFont = pf;
976 }
977 }
978 while (fn < ttf.getFontCount());
979 break;
980
981 case FONTFORMAT_TYPE1:
982 Type1Font t1f = new Type1Font(fileName, nativeNames);
983 physicalFont = addToFontList(t1f, fontRank);
984 break;
985
986 case FONTFORMAT_NATIVE:
987 NativeFont nf = new NativeFont(fileName, false);
988 physicalFont = addToFontList(nf, fontRank);
989 break;
990 default:
991
992 }
993 if (FontUtilities.isLogging()) {
994 FontUtilities.getLogger()
995 .info("Registered file " + fileName + " as font " +
996 physicalFont + " rank=" + fontRank);
997 }
998 } catch (FontFormatException ffe) {
999 if (FontUtilities.isLogging()) {
1000 FontUtilities.getLogger().warning("Unusable font: " +
1001 fileName + " " + ffe.toString());
1002 }
1003 }
1004 if (physicalFont != null &&
1005 fontFormat != FONTFORMAT_NATIVE) {
1006 registeredFonts.put(fileName, physicalFont);
1007 }
1008 return physicalFont;
1009 }
1010
1011 public void registerFonts(String[] fileNames,
1012 String[][] nativeNames,
1013 int fontCount,
1014 int fontFormat,
1015 boolean useJavaRasterizer,
1016 int fontRank, boolean defer) {
1017
1018 for (int i=0; i < fontCount; i++) {
1019 if (defer) {
1020 registerDeferredFont(fileNames[i],fileNames[i], nativeNames[i],
1021 fontFormat, useJavaRasterizer, fontRank);
1022 } else {
1024 fontFormat, useJavaRasterizer, fontRank);
1025 }
1026 }
1027 }
1028
1029 /*
1030 * This is the Physical font used when some other font on the system
1031 * can't be located. There has to be at least one font or the font
1032 * system is not useful and the graphics environment cannot sustain
1033 * the Java platform.
1034 */
1035 public PhysicalFont getDefaultPhysicalFont() {
1036 if (defaultPhysicalFont == null) {
1037 String defaultFontName = getDefaultFontFaceName();
1038 // findFont2D will load all fonts
1039 Font2D font2d = findFont2D(defaultFontName, Font.PLAIN, NO_FALLBACK);
1040 if (font2d != null) {
1041 if (font2d instanceof PhysicalFont) {
1042 defaultPhysicalFont = (PhysicalFont)font2d;
1043 } else {
1044 if (FontUtilities.isLogging()) {
1045 FontUtilities.getLogger()
1046 .warning("Font returned by findFont2D for default font name " +
1047 defaultFontName + " is not a physical font: " + font2d.getFontName(null));
1048 }
1049 }
1050 }
1051 if (defaultPhysicalFont == null) {
1052 /* Because of the findFont2D call above, if we reach here, we
1053 * know all fonts have already been loaded, just accept any
1054 * match at this point. If this fails we are in real trouble
1055 * and I don't know how to recover from there being absolutely
1056 * no fonts anywhere on the system.
1057 */
1058 defaultPhysicalFont = physicalFonts.values().stream().findFirst()
1059 .orElseThrow(()->new Error("Probable fatal error: No physical fonts found."));
1060 }
1061 }
1062 return defaultPhysicalFont;
1063 }
1064
1065 public Font2D getDefaultLogicalFont(int style) {
1066 return findFont2D("dialog", style, NO_FALLBACK);
1067 }
1068
1069 /*
1070 * return String representation of style prepended with "."
1286 resolveFontFiles(unmappedFontFiles, unmappedFontNames);
1287 }
1288
1289 /* remove from the set of names that will be returned to the
1290 * user any fonts that can't be mapped to files.
1291 */
1292 if (unmappedFontNames.size() > 0) {
1293 int sz = unmappedFontNames.size();
1294 for (int i=0; i<sz; i++) {
1295 String name = unmappedFontNames.get(i);
1296 String familyName = fontToFamilyNameMap.get(name);
1297 if (familyName != null) {
1298 ArrayList<String> family = familyToFontListMap.get(familyName);
1299 if (family != null) {
1300 if (family.size() <= 1) {
1301 familyToFontListMap.remove(familyName);
1302 }
1303 }
1304 }
1305 fontToFamilyNameMap.remove(name);
1306 if (FontUtilities.isLogging()) {
1307 FontUtilities.getLogger()
1308 .info("No file for font:" + name);
1309 }
1310 }
1311 }
1312 }
1313 }
1314
1315 /**
1316 * In some cases windows may have fonts in the fonts folder that
1317 * don't show up in the registry or in the GDI calls to enumerate fonts.
1318 * The only way to find these is to list the directory. We invoke this
1319 * only in getAllFonts/Families, so most searches for a specific
1320 * font that is satisfied by the GDI/registry calls don't take the
1321 * additional hit of listing the directory. This hit is small enough
1322 * that its not significant in these 'enumerate all the fonts' cases.
1323 * The basic approach is to cross-reference the files windows found
1324 * with the ones in the directory listing approach, and for each
1325 * in the latter list that is missing from the former list, register it.
1326 */
1327 private synchronized void checkForUnreferencedFontFiles() {
1328 if (haveCheckedUnreferencedFontFiles) {
1329 return;
1337 * versions of the names from the registry.
1338 */
1339 ArrayList<String> registryFiles = new ArrayList<>();
1340 for (String regFile : fontToFileMap.values()) {
1341 registryFiles.add(regFile.toLowerCase());
1342 }
1343
1344 /* To avoid any issues with concurrent modification, create
1345 * copies of the existing maps, add the new fonts into these
1346 * and then replace the references to the old ones with the
1347 * new maps. ConcurrentHashmap is another option but its a lot
1348 * more changes and with this exception, these maps are intended
1349 * to be static.
1350 */
1351 HashMap<String,String> fontToFileMap2 = null;
1352 HashMap<String,String> fontToFamilyNameMap2 = null;
1353 HashMap<String,ArrayList<String>> familyToFontListMap2 = null;;
1354
1355 for (String pathFile : getFontFilesFromPath(false)) {
1356 if (!registryFiles.contains(pathFile)) {
1357 if (FontUtilities.isLogging()) {
1358 FontUtilities.getLogger()
1359 .info("Found non-registry file : " + pathFile);
1360 }
1361 PhysicalFont f = registerFontFile(getPathName(pathFile));
1362 if (f == null) {
1363 continue;
1364 }
1365 if (fontToFileMap2 == null) {
1366 fontToFileMap2 = new HashMap<>(fontToFileMap);
1367 fontToFamilyNameMap2 = new HashMap<>(fontToFamilyNameMap);
1368 familyToFontListMap2 = new HashMap<>(familyToFontListMap);
1369 }
1370 String fontName = f.getFontName(null);
1371 String family = f.getFamilyName(null);
1372 String familyLC = family.toLowerCase();
1373 fontToFamilyNameMap2.put(fontName, family);
1374 fontToFileMap2.put(fontName, pathFile);
1375 ArrayList<String> fonts = familyToFontListMap2.get(familyLC);
1376 if (fonts == null) {
1377 fonts = new ArrayList<>();
1378 } else {
1379 fonts = new ArrayList<>(fonts);
1380 }
1382 familyToFontListMap2.put(familyLC, fonts);
1383 }
1384 }
1385 if (fontToFileMap2 != null) {
1386 fontToFileMap = fontToFileMap2;
1387 familyToFontListMap = familyToFontListMap2;
1388 fontToFamilyNameMap = fontToFamilyNameMap2;
1389 }
1390 }
1391
1392 private void resolveFontFiles(HashSet<String> unmappedFiles,
1393 ArrayList<String> unmappedFonts) {
1394
1395 Locale l = SunToolkit.getStartupLocale();
1396
1397 for (String file : unmappedFiles) {
1398 try {
1399 int fn = 0;
1400 TrueTypeFont ttf;
1401 String fullPath = getPathName(file);
1402 if (FontUtilities.isLogging()) {
1403 FontUtilities.getLogger()
1404 .info("Trying to resolve file " + fullPath);
1405 }
1406 do {
1407 ttf = new TrueTypeFont(fullPath, null, fn++, false);
1408 // prefer the font's locale name.
1409 String fontName = ttf.getFontName(l).toLowerCase();
1410 if (unmappedFonts.contains(fontName)) {
1411 fontToFileMap.put(fontName, file);
1412 unmappedFonts.remove(fontName);
1413 if (FontUtilities.isLogging()) {
1414 FontUtilities.getLogger()
1415 .info("Resolved absent registry entry for " +
1416 fontName + " located in " + fullPath);
1417 }
1418 }
1419 }
1420 while (fn < ttf.getFontCount());
1421 } catch (Exception e) {
1422 }
1423 }
1424 }
1425
1426 /* Hardwire the English names and expected file names of fonts
1427 * commonly used at start up. Avoiding until later even the small
1428 * cost of calling platform APIs to locate these can help.
1429 * The code that registers these fonts needs to "bail" if any
1430 * of the files do not exist, so it will verify the existence of
1431 * all non-null file names first.
1432 * They are added in to a map with nominally the first
1433 * word in the name of the family as the key. In all the cases
1434 * we are using the family name is a single word, and as is
1435 * more or less required the family name is the initial sequence
1436 * in a full name. So lookup first finds the matching description,
1437 * then registers the whole family, returning the right font.
1438 */
1439 public static class FamilyDescription {
1520 if (boldFile == null) {
1521 failure = true;
1522 }
1523 }
1524
1525 if (fd.italicFileName != null) {
1526 italicFile = getPathName(fd.italicFileName);
1527 if (italicFile == null) {
1528 failure = true;
1529 }
1530 }
1531
1532 if (fd.boldItalicFileName != null) {
1533 boldItalicFile = getPathName(fd.boldItalicFileName);
1534 if (boldItalicFile == null) {
1535 failure = true;
1536 }
1537 }
1538
1539 if (failure) {
1540 if (FontUtilities.isLogging()) {
1541 FontUtilities.getLogger().
1542 info("Hardcoded file missing looking for " + lcName);
1543 }
1544 platformFontMap.remove(firstWord);
1545 return null;
1546 }
1547
1548 /* Some of these may be null,as not all styles have to exist */
1549 final String[] files = {
1550 plainFile, boldFile, italicFile, boldItalicFile } ;
1551
1552 failure = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
1553 public Boolean run() {
1554 for (int i=0; i<files.length; i++) {
1555 if (files[i] == null) {
1556 continue;
1557 }
1558 File f = new File(files[i]);
1559 if (!f.exists()) {
1560 return Boolean.TRUE;
1561 }
1562 }
1563 return Boolean.FALSE;
1564 }
1565 });
1566
1567 if (failure) {
1568 if (FontUtilities.isLogging()) {
1569 FontUtilities.getLogger().
1570 info("Hardcoded file missing looking for " + lcName);
1571 }
1572 platformFontMap.remove(firstWord);
1573 return null;
1574 }
1575
1576 /* If we reach here we know that we have all the files we
1577 * expect, so all should be fine so long as the contents
1578 * are what we'd expect. Now on to registering the fonts.
1579 * Currently this code only looks for TrueType fonts, so format
1580 * and rank can be specified without looking at the filename.
1581 */
1582 Font2D font = null;
1583 for (int f=0;f<files.length;f++) {
1584 if (files[f] == null) {
1585 continue;
1586 }
1587 PhysicalFont pf =
1588 registerFontFile(files[f], null,
1589 FONTFORMAT_TRUETYPE, false, Font2D.TTF_RANK);
1590 if (f == styleIndex) {
1591 font = pf;
1816 String [] fontList = family.toArray(STR_ARRAY);
1817 if (fontList.length == 0) {
1818 return null;
1819 }
1820
1821 /* first check that for every font in this family we can find
1822 * a font file. The specific reason for doing this is that
1823 * in at least one case on Windows a font has the face name "David"
1824 * but the registry entry is "David Regular". That is the "unique"
1825 * name of the font but in other cases the registry contains the
1826 * "full" name. See the specifications of name ids 3 and 4 in the
1827 * TrueType 'name' table.
1828 * In general this could cause a problem that we fail to register
1829 * if we all members of a family that we may end up mapping to
1830 * the wrong font member: eg return Bold when Plain is needed.
1831 */
1832 for (int f=0;f<fontList.length;f++) {
1833 String fontNameLC = fontList[f].toLowerCase(Locale.ENGLISH);
1834 String fileName = fontToFileMap.get(fontNameLC);
1835 if (fileName == null) {
1836 if (FontUtilities.isLogging()) {
1837 FontUtilities.getLogger()
1838 .info("Platform lookup : No file for font " +
1839 fontList[f] + " in family " +familyName);
1840 }
1841 return null;
1842 }
1843 }
1844
1845 /* Currently this code only looks for TrueType fonts, so format
1846 * and rank can be specified without looking at the filename.
1847 */
1848 PhysicalFont physicalFont = null;
1849 if (fontFile != null) {
1850 physicalFont = registerFontFile(getPathName(fontFile), null,
1851 FONTFORMAT_TRUETYPE, false,
1852 Font2D.TTF_RANK);
1853 }
1854 /* Register all fonts in this family. */
1855 for (int f=0;f<fontList.length;f++) {
1856 String fontNameLC = fontList[f].toLowerCase(Locale.ENGLISH);
1857 String fileName = fontToFileMap.get(fontNameLC);
1858 if (fontFile != null && fontFile.equals(fileName)) {
1859 continue;
1860 }
1889 * A font may exist with the specified style, or it may
1890 * exist only in some other style. For non-native fonts the scaler
1891 * may be able to emulate the required style.
1892 */
1893 public Font2D findFont2D(String name, int style, int fallback) {
1894 if (name == null) return null;
1895 String lowerCaseName = name.toLowerCase(Locale.ENGLISH);
1896 String mapName = lowerCaseName + dotStyleStr(style);
1897
1898 /* If preferLocaleFonts() or preferProportionalFonts() has been
1899 * called we may be using an alternate set of composite fonts in this
1900 * app context. The presence of a pre-built name map indicates whether
1901 * this is so, and gives access to the alternate composite for the
1902 * name.
1903 */
1904 Font2D font = fontNameCache.get(mapName);
1905 if (font != null) {
1906 return font;
1907 }
1908
1909 if (FontUtilities.isLogging()) {
1910 FontUtilities.getLogger().info("Search for font: " + name);
1911 }
1912
1913 // The check below is just so that the bitmap fonts being set by
1914 // AWT and Swing thru the desktop properties do not trigger the
1915 // the load fonts case. The two bitmap fonts are now mapped to
1916 // appropriate equivalents for serif and sansserif.
1917 // Note that the cost of this comparison is only for the first
1918 // call until the map is filled.
1919 if (FontUtilities.isWindows) {
1920 if (lowerCaseName.equals("ms sans serif")) {
1921 name = "sansserif";
1922 } else if (lowerCaseName.equals("ms serif")) {
1923 name = "serif";
1924 }
1925 }
1926
1927 /* This isn't intended to support a client passing in the
1928 * string default, but if a client passes in null for the name
1929 * the java.awt.Font class internally substitutes this name.
1930 * So we need to recognise it here to prevent a loadFonts
1931 * on the unrecognised name. The only potential problem with
2003 /* The next check is perhaps one
2004 * that shouldn't be done. ie if we get this
2005 * far we have probably as close a match as we
2006 * are going to get. We could load all fonts to
2007 * see if somehow some parts of the family are
2008 * loaded but not all of it.
2009 */
2010 if (familyFont.canDoStyle(style|font.style)) {
2011 fontNameCache.put(mapName, familyFont);
2012 return familyFont;
2013 }
2014 }
2015 }
2016 }
2017 }
2018 }
2019
2020 if (FontUtilities.isWindows) {
2021
2022 font = findFontFromPlatformMap(lowerCaseName, style);
2023 if (FontUtilities.isLogging()) {
2024 FontUtilities.getLogger()
2025 .info("findFontFromPlatformMap returned " + font);
2026 }
2027 if (font != null) {
2028 fontNameCache.put(mapName, font);
2029 return font;
2030 }
2031 /* Don't want Windows to return a font from C:\Windows\Fonts
2032 * if someone has installed a font with the same name
2033 * in the JRE.
2034 */
2035 if (deferredFontFiles.size() > 0) {
2036 font = findJREDeferredFont(lowerCaseName, style);
2037 if (font != null) {
2038 fontNameCache.put(mapName, font);
2039 return font;
2040 }
2041 }
2042 font = findFontFromPlatform(lowerCaseName, style);
2043 if (font != null) {
2044 if (FontUtilities.isLogging()) {
2045 FontUtilities.getLogger()
2046 .info("Found font via platform API for request:\"" +
2047 name + "\":, style="+style+
2048 " found font: " + font);
2049 }
2050 fontNameCache.put(mapName, font);
2051 return font;
2052 }
2053 }
2054
2055 /* If reach here and no match has been located, then if there are
2056 * uninitialised deferred fonts, load as many of those as needed
2057 * to find the deferred font. If none is found through that
2058 * search continue on.
2059 * There is possibly a minor issue when more than one
2060 * deferred font implements the same font face. Since deferred
2061 * fonts are only those in font configuration files, this is a
2062 * controlled situation, the known case being Solaris euro_fonts
2063 * versions of Arial, Times New Roman, Courier New. However
2064 * the larger font will transparently replace the smaller one
2065 * - see addToFontList() - when it is needed by the composite font.
2066 */
2067 if (deferredFontFiles.size() > 0) {
2068 font = findDeferredFont(name, style);
2069 if (font != null) {
2098 if (font != null) {
2099 if (fontsAreRegistered) {
2100 fontNameCache.put(mapName, font);
2101 }
2102 return font;
2103 }
2104 }
2105 font = nameTable.get(lowerCaseName);
2106 if (font != null) {
2107 if (fontsAreRegistered) {
2108 fontNameCache.put(mapName, font);
2109 }
2110 return font;
2111 }
2112 }
2113
2114 /* If reach here and no match has been located, then if all fonts
2115 * are not yet loaded, do so, and then recurse.
2116 */
2117 if (!loadedAllFonts) {
2118 if (FontUtilities.isLogging()) {
2119 FontUtilities.getLogger()
2120 .info("Load fonts looking for:" + name);
2121 }
2122 loadFonts();
2123 loadedAllFonts = true;
2124 return findFont2D(name, style, fallback);
2125 }
2126
2127 if (!loadedAllFontFiles) {
2128 if (FontUtilities.isLogging()) {
2129 FontUtilities.getLogger()
2130 .info("Load font files looking for:" + name);
2131 }
2132 loadFontFiles();
2133 loadedAllFontFiles = true;
2134 return findFont2D(name, style, fallback);
2135 }
2136
2137 /* The primary name is the locale default - ie not US/English but
2138 * whatever is the default in this locale. This is the way it always
2139 * has been but may be surprising to some developers if "Arial Regular"
2140 * were hard-coded in their app and yet "Arial Regular" was not the
2141 * default name. Fortunately for them, as a consequence of the JDK
2142 * supporting returning names and family names for arbitrary locales,
2143 * we also need to support searching all localised names for a match.
2144 * But because this case of the name used to reference a font is not
2145 * the same as the default for this locale is rare, it makes sense to
2146 * search a much shorter list of default locale names and only go to
2147 * a longer list of names in the event that no match was found.
2148 * So add here code which searches localised names too.
2149 * As in 1.4.x this happens only after loading all fonts, which
2150 * is probably the right order.
2151 */
2169 getFontConfiguration().getFallbackFamilyName(name, null);
2170 if (compatName != null) {
2171 font = findFont2D(compatName, style, fallback);
2172 fontNameCache.put(mapName, font);
2173 return font;
2174 }
2175 } else if (lowerCaseName.equals("timesroman")) {
2176 font = findFont2D("serif", style, fallback);
2177 fontNameCache.put(mapName, font);
2178 return font;
2179 } else if (lowerCaseName.equals("helvetica")) {
2180 font = findFont2D("sansserif", style, fallback);
2181 fontNameCache.put(mapName, font);
2182 return font;
2183 } else if (lowerCaseName.equals("courier")) {
2184 font = findFont2D("monospaced", style, fallback);
2185 fontNameCache.put(mapName, font);
2186 return font;
2187 }
2188
2189 if (FontUtilities.isLogging()) {
2190 FontUtilities.getLogger().info("No font found for:" + name);
2191 }
2192
2193 switch (fallback) {
2194 case PHYSICAL_FALLBACK: return getDefaultPhysicalFont();
2195 case LOGICAL_FALLBACK: return getDefaultLogicalFont(style);
2196 default: return null;
2197 }
2198 }
2199
2200 /*
2201 * Workaround for apps which are dependent on a font metrics bug
2202 * in JDK 1.1. This is an unsupported win32 private setting.
2203 * Left in for a customer - do not remove.
2204 */
2205 public boolean usePlatformFontMetrics() {
2206 return usePlatformFontMetrics;
2207 }
2208
2209 public int getNumFonts() {
2210 return physicalFonts.size()+maxCompFont;
2211 }
2344 return null;
2345 }
2346
2347 /*
2348 * This is called when font is determined to be invalid/bad.
2349 * It designed to be called (for example) by the font scaler
2350 * when in processing a font file it is discovered to be incorrect.
2351 * This is different than the case where fonts are discovered to
2352 * be incorrect during initial verification, as such fonts are
2353 * never registered.
2354 * Handles to this font held are re-directed to a default font.
2355 * This default may not be an ideal substitute buts it better than
2356 * crashing This code assumes a PhysicalFont parameter as it doesn't
2357 * make sense for a Composite to be "bad".
2358 */
2359 public synchronized void deRegisterBadFont(Font2D font2D) {
2360 if (!(font2D instanceof PhysicalFont)) {
2361 /* We should never reach here, but just in case */
2362 return;
2363 } else {
2364 if (FontUtilities.isLogging()) {
2365 FontUtilities.getLogger()
2366 .severe("Deregister bad font: " + font2D);
2367 }
2368 replaceFont((PhysicalFont)font2D, getDefaultPhysicalFont());
2369 }
2370 }
2371
2372 /*
2373 * This encapsulates all the work that needs to be done when a
2374 * Font2D is replaced by a different Font2D.
2375 */
2376 public synchronized void replaceFont(PhysicalFont oldFont,
2377 PhysicalFont newFont) {
2378
2379 if (oldFont.handle.font2D != oldFont) {
2380 /* already done */
2381 return;
2382 }
2383
2384 /* If we try to replace the font with itself, that won't work,
2385 * so pick any alternative physical font
2386 */
2387 if (oldFont == newFont) {
2388 if (FontUtilities.isLogging()) {
2389 FontUtilities.getLogger()
2390 .severe("Can't replace bad font with itself " + oldFont);
2391 }
2392 PhysicalFont[] physFonts = getPhysicalFonts();
2393 for (int i=0; i<physFonts.length;i++) {
2394 if (physFonts[i] != newFont) {
2395 newFont = physFonts[i];
2396 break;
2397 }
2398 }
2399 if (oldFont == newFont) {
2400 if (FontUtilities.isLogging()) {
2401 FontUtilities.getLogger()
2402 .severe("This is bad. No good physicalFonts found.");
2403 }
2404 return;
2405 }
2406 }
2407
2408 /* eliminate references to this font, so it won't be located
2409 * by future callers, and will be eligible for GC when all
2410 * references are removed
2411 */
2412 oldFont.handle.font2D = newFont;
2413 physicalFonts.remove(oldFont.fullName);
2414 fullNameToFont.remove(oldFont.fullName.toLowerCase(Locale.ENGLISH));
2415 FontFamily.remove(oldFont);
2416 if (localeFullNamesToFont != null) {
2417 Map.Entry<?, ?>[] mapEntries = localeFullNamesToFont.entrySet().
2418 toArray(new Map.Entry<?, ?>[0]);
2419 /* Should I be replacing these, or just I just remove
2420 * the names from the map?
2421 */
2422 for (int i=0; i<mapEntries.length;i++) {
2423 if (mapEntries[i].getValue() == oldFont) {
2483 String[] fullNames = ttf.getAllFullNames();
2484 for (int n=0; n<fullNames.length; n++) {
2485 localeFullNamesToFont.put(fullNames[n], ttf);
2486 }
2487 FontFamily family = FontFamily.getFamily(ttf.familyName);
2488 if (family != null) {
2489 FontFamily.addLocaleNames(family, ttf.getAllFamilyNames());
2490 }
2491 }
2492 }
2493 }
2494
2495 /* This replicate the core logic of findFont2D but operates on
2496 * all the locale names. This hasn't been merged into findFont2D to
2497 * keep the logic simpler and reduce overhead, since this case is
2498 * almost never used. The main case in which it is called is when
2499 * a bogus font name is used and we need to check all possible names
2500 * before returning the default case.
2501 */
2502 private Font2D findFont2DAllLocales(String name, int style) {
2503
2504 if (FontUtilities.isLogging()) {
2505 FontUtilities.getLogger()
2506 .info("Searching localised font names for:" + name);
2507 }
2508
2509 /* If reach here and no match has been located, then if we have
2510 * not yet built the map of localeFullNamesToFont for TT fonts, do so
2511 * now. This method must be called after all fonts have been loaded.
2512 */
2513 if (localeFullNamesToFont == null) {
2514 loadLocaleNames();
2515 }
2516 String lowerCaseName = name.toLowerCase();
2517 Font2D font = null;
2518
2519 /* First see if its a family name. */
2520 FontFamily family = FontFamily.getLocaleFamily(lowerCaseName);
2521 if (family != null) {
2522 font = family.getFont(style);
2523 if (font == null) {
2524 font = family.getClosestStyle(style);
2525 }
2526 if (font != null) {
2527 return font;
2621 * a case cannot retrieve a cached metrics solely on the basis of
2622 * the Font.equals() method since it needs to also check if the Font2D
2623 * is the same.
2624 * We also use non-standard composites for Swing native L&F fonts on
2625 * Windows. In that case the policy is that the metrics reported are
2626 * based solely on the physical font in the first slot which is the
2627 * visible java.awt.Font. So in that case the metrics cache which tests
2628 * the Font does what we want. In the near future when we expand the GTK
2629 * logical font definitions we may need to revisit this if GTK reports
2630 * combined metrics instead. For now though this test can be simple.
2631 */
2632 public boolean usingAlternateCompositeFonts() {
2633 return _usingAlternateComposites;
2634 }
2635
2636 /* Modifies the behaviour of a subsequent call to preferLocaleFonts()
2637 * to use Mincho instead of Gothic for dialoginput in JA locales
2638 * on windows. Not needed on other platforms.
2639 */
2640 public synchronized void useAlternateFontforJALocales() {
2641 if (FontUtilities.isLogging()) {
2642 FontUtilities.getLogger()
2643 .info("Entered useAlternateFontforJALocales().");
2644 }
2645 if (!FontUtilities.isWindows) {
2646 return;
2647 }
2648 gAltJAFont = true;
2649 }
2650
2651 public boolean usingAlternateFontforJALocales() {
2652 return gAltJAFont;
2653 }
2654
2655 public synchronized void preferLocaleFonts() {
2656 if (FontUtilities.isLogging()) {
2657 FontUtilities.getLogger().info("Entered preferLocaleFonts().");
2658 }
2659 /* Test if re-ordering will have any effect */
2660 if (!FontConfiguration.willReorderForStartupLocale()) {
2661 return;
2662 }
2663 if (gLocalePref == true) {
2664 return;
2665 }
2666 gLocalePref = true;
2667 createCompositeFonts(fontNameCache, gLocalePref, gPropPref);
2668 _usingAlternateComposites = true;
2669 }
2670
2671 public synchronized void preferProportionalFonts() {
2672 if (FontUtilities.isLogging()) {
2673 FontUtilities.getLogger()
2674 .info("Entered preferProportionalFonts().");
2675 }
2676 /* If no proportional fonts are configured, there's no need
2677 * to take any action.
2678 */
2679 if (!FontConfiguration.hasMonoToPropMap()) {
2680 return;
2681 }
2682 if (gPropPref == true) {
2683 return;
2684 }
2685 gPropPref = true;
2686 createCompositeFonts(fontNameCache, gLocalePref, gPropPref);
2687 _usingAlternateComposites = true;
2688 }
2689
2690 private static HashSet<String> installedNames = null;
2691 private static HashSet<String> getInstalledNames() {
2692 if (installedNames == null) {
2693 Locale l = getSystemStartupLocale();
2694 SunFontManager fontManager = SunFontManager.getInstance();
2695 String[] installedFamilies =
2895 for (int i=0; i < ls.length; i++ ) {
2896 File theFile = new File(dirFile, ls[i]);
2897 String fullName = null;
2898 if (resolveSymLinks) {
2899 try {
2900 fullName = theFile.getCanonicalPath();
2901 } catch (IOException e) {
2902 }
2903 }
2904 if (fullName == null) {
2905 fullName = dirName + File.separator + ls[i];
2906 }
2907
2908 // REMIND: case compare depends on platform
2909 if (registeredFontFiles.contains(fullName)) {
2910 continue;
2911 }
2912
2913 if (badFonts != null && badFonts.contains(fullName)) {
2914 if (FontUtilities.debugFonts()) {
2915 FontUtilities.getLogger()
2916 .warning("skip bad font " + fullName);
2917 }
2918 continue; // skip this font file.
2919 }
2920
2921 registeredFontFiles.add(fullName);
2922
2923 if (FontUtilities.debugFonts()
2924 && FontUtilities.getLogger().isLoggable(PlatformLogger.Level.INFO)) {
2925 String message = "Registering font " + fullName;
2926 String[] natNames = getNativeNames(fullName, null);
2927 if (natNames == null) {
2928 message += " with no native name";
2929 } else {
2930 message += " with native name(s) " + natNames[0];
2931 for (int nn = 1; nn < natNames.length; nn++) {
2932 message += ", " + natNames[nn];
2933 }
2934 }
2935 FontUtilities.getLogger().info(message);
2936 }
2937 fontNames[fontCount] = fullName;
2938 nativeNames[fontCount++] = getNativeNames(fullName, null);
2939 }
2940 registerFonts(fontNames, nativeNames, fontCount, fontFormat,
2941 useJavaRasterizer, fontRank, defer);
2942 return;
2943 }
2944
2945 protected String[] getNativeNames(String fontFileName,
2946 String platformName) {
2947 return null;
2948 }
2949
2950 /**
2951 * Returns a file name for the physical font represented by this platform
2952 * font name. The default implementation tries to obtain the file name
2953 * from the font configuration.
2954 * Subclasses may override to provide information from other sources.
2955 */
2965 }
2966
2967 /* A call to this method should be followed by a call to
2968 * registerFontDirs(..)
2969 */
2970 public String getPlatformFontPath(boolean noType1Font) {
2971 if (fontPath == null) {
2972 fontPath = getFontPath(noType1Font);
2973 }
2974 return fontPath;
2975 }
2976
2977 protected void loadFonts() {
2978 if (discoveredAllFonts) {
2979 return;
2980 }
2981 /* Use lock specific to the font system */
2982 synchronized (this) {
2983 if (FontUtilities.debugFonts()) {
2984 Thread.dumpStack();
2985 FontUtilities.getLogger()
2986 .info("SunGraphicsEnvironment.loadFonts() called");
2987 }
2988 initialiseDeferredFonts();
2989
2990 AccessController.doPrivileged(new PrivilegedAction<Void>() {
2991 public Void run() {
2992 if (fontPath == null) {
2993 fontPath = getPlatformFontPath(noType1Font);
2994 registerFontDirs(fontPath);
2995 }
2996 if (fontPath != null) {
2997 // this will find all fonts including those already
2998 // registered. But we have checks in place to prevent
2999 // double registration.
3000 if (! gotFontsFromPlatform()) {
3001 registerFontsOnPath(fontPath, false,
3002 Font2D.UNKNOWN_RANK,
3003 false, true);
3004 loadedAllFontFiles = true;
3005 }
3006 }
3084 boolean preferPropFonts);
3085
3086 /**
3087 * Returns face name for default font, or null if
3088 * no face names are used for CompositeFontDescriptors
3089 * for this platform.
3090 */
3091 public synchronized String getDefaultFontFaceName() {
3092 return defaultFontName;
3093 }
3094
3095 public void loadFontFiles() {
3096 loadFonts();
3097 if (loadedAllFontFiles) {
3098 return;
3099 }
3100 /* Use lock specific to the font system */
3101 synchronized (this) {
3102 if (FontUtilities.debugFonts()) {
3103 Thread.dumpStack();
3104 FontUtilities.getLogger().info("loadAllFontFiles() called");
3105 }
3106 AccessController.doPrivileged(new PrivilegedAction<Void>() {
3107 public Void run() {
3108 if (fontPath == null) {
3109 fontPath = getPlatformFontPath(noType1Font);
3110 }
3111 if (fontPath != null) {
3112 // this will find all fonts including those already
3113 // registered. But we have checks in place to prevent
3114 // double registration.
3115 registerFontsOnPath(fontPath, false,
3116 Font2D.UNKNOWN_RANK,
3117 false, true);
3118 }
3119 loadedAllFontFiles = true;
3120 return null;
3121 }
3122 });
3123 }
3124 }
3125
3126 /*
3127 * This method asks the font configuration API for all platform names
3128 * used as components of composite/logical fonts and iterates over these
3129 * looking up their corresponding file name and registers these fonts.
3130 * It also ensures that the fonts are accessible via platform APIs.
3131 * The composites themselves are then registered.
3132 */
3133 private void
3134 initCompositeFonts(FontConfiguration fontConfig,
3135 ConcurrentHashMap<String, Font2D> altNameCache) {
3136
3137 if (FontUtilities.isLogging()) {
3138 FontUtilities.getLogger()
3139 .info("Initialising composite fonts");
3140 }
3141
3142 int numCoreFonts = fontConfig.getNumberCoreFonts();
3143 String[] fcFonts = fontConfig.getPlatformFontNames();
3144 for (int f=0; f<fcFonts.length; f++) {
3145 String platformFontName = fcFonts[f];
3146 String fontFileName =
3147 getFileNameFromPlatformName(platformFontName);
3148 String[] nativeNames = null;
3149 if (fontFileName == null
3150 || fontFileName.equals(platformFontName)) {
3151 /* No file located, so register using the platform name,
3152 * i.e. as a native font.
3153 */
3154 fontFileName = platformFontName;
3155 } else {
3156 if (f < numCoreFonts) {
3157 /* If platform APIs also need to access the font, add it
3158 * to a set to be registered with the platform too.
3159 * This may be used to add the parent directory to the X11
3160 * font path if its not already there. See the docs for the
3219 * fall back component fonts to the composite.
3220 */
3221 if (altNameCache != null) {
3222 SunFontManager.registerCompositeFont(
3223 descriptor.getFaceName(),
3224 componentFileNames, componentFaceNames,
3225 descriptor.getCoreComponentCount(),
3226 descriptor.getExclusionRanges(),
3227 descriptor.getExclusionRangeLimits(),
3228 true,
3229 altNameCache);
3230 } else {
3231 registerCompositeFont(descriptor.getFaceName(),
3232 componentFileNames, componentFaceNames,
3233 descriptor.getCoreComponentCount(),
3234 descriptor.getExclusionRanges(),
3235 descriptor.getExclusionRangeLimits(),
3236 true);
3237 }
3238 if (FontUtilities.debugFonts()) {
3239 FontUtilities.getLogger()
3240 .info("registered " + descriptor.getFaceName());
3241 }
3242 }
3243 }
3244
3245 /**
3246 * Notifies graphics environment that the logical font configuration
3247 * uses the given platform font name. The graphics environment may
3248 * use this for platform specific initialization.
3249 */
3250 protected void addFontToPlatformFontPath(String platformFontName) {
3251 }
3252
3253 protected void registerFontFile(String fontFileName, String[] nativeNames,
3254 int fontRank, boolean defer) {
3255 // REMIND: case compare depends on platform
3256 if (registeredFontFiles.contains(fontFileName)) {
3257 return;
3258 }
3259 int fontFormat;
3260 if (ttFilter.accept(null, fontFileName)) {
|
303 }
304
305 /* Initialise ptrs used by JNI methods */
306 private static native void initIDs();
307
308 protected SunFontManager() {
309 AccessController.doPrivileged(new PrivilegedAction<Void>() {
310 public Void run() {
311 File badFontFile =
312 new File(jreFontDirName + File.separator + "badfonts.txt");
313 if (badFontFile.exists()) {
314 badFonts = new ArrayList<>();
315 try (FileInputStream fis = new FileInputStream(badFontFile);
316 BufferedReader br = new BufferedReader(new InputStreamReader(fis))) {
317 while (true) {
318 String name = br.readLine();
319 if (name == null) {
320 break;
321 } else {
322 if (FontUtilities.debugFonts()) {
323 FontUtilities.logWarning("read bad font: " + name);
324 }
325 badFonts.add(name);
326 }
327 }
328 } catch (IOException e) {
329 }
330 }
331
332 /* Here we get the fonts in jre/lib/fonts and register
333 * them so they are always available and preferred over
334 * other fonts. This needs to be registered before the
335 * composite fonts as otherwise some native font that
336 * corresponds may be found as we don't have a way to
337 * handle two fonts of the same name, so the JRE one
338 * must be the first one registered. Pass "true" to
339 * registerFonts method as on-screen these JRE fonts
340 * always go through the JDK rasteriser.
341 */
342 if (FontUtilities.isLinux) {
343 /* Linux font configuration uses these fonts */
344 registerFontDir(jreFontDirName);
345 }
346 registerFontsInDir(jreFontDirName, true, Font2D.JRE_RANK,
347 true, false);
348
349 /* Create the font configuration and get any font path
350 * that might be specified.
351 */
352 fontConfig = createFontConfiguration();
353
354 String[] fontInfo = getDefaultPlatformFont();
355 defaultFontName = fontInfo[0];
356 if (defaultFontName == null && FontUtilities.debugFonts()) {
357 FontUtilities.logWarning("defaultFontName is null");
358 }
359 defaultFontFileName = fontInfo[1];
360
361 String extraFontPath = fontConfig.getExtraFontPath();
362
363 /* In prior releases the debugging font path replaced
364 * all normally located font directories except for the
365 * JRE fonts dir. This directory is still always located
366 * and placed at the head of the path but as an
367 * augmentation to the previous behaviour the
368 * changes below allow you to additionally append to
369 * the font path by starting with append: or prepend by
370 * starting with a prepend: sign. Eg: to append
371 * -Dsun.java2d.fontpath=append:/usr/local/myfonts
372 * and to prepend
373 * -Dsun.java2d.fontpath=prepend:/usr/local/myfonts Disp
374 *
375 * If there is an appendedfontpath it in the font
376 * configuration it is used instead of searching the
377 * system for dirs.
386 * locale-specific so its almost impossible to get
387 * right, so it should be used with caution.
388 */
389 boolean prependToPath = false;
390 boolean appendToPath = false;
391 String dbgFontPath = System.getProperty("sun.java2d.fontpath");
392
393 if (dbgFontPath != null) {
394 if (dbgFontPath.startsWith("prepend:")) {
395 prependToPath = true;
396 dbgFontPath =
397 dbgFontPath.substring("prepend:".length());
398 } else if (dbgFontPath.startsWith("append:")) {
399 appendToPath = true;
400 dbgFontPath =
401 dbgFontPath.substring("append:".length());
402 }
403 }
404
405 if (FontUtilities.debugFonts()) {
406 FontUtilities.logInfo("JRE font directory: " + jreFontDirName);
407 FontUtilities.logInfo("Extra font path: " + extraFontPath);
408 FontUtilities.logInfo("Debug font path: " + dbgFontPath);
409 }
410
411 if (dbgFontPath != null) {
412 /* In debugging mode we register all the paths
413 * Caution: this is a very expensive call on Solaris:-
414 */
415 fontPath = getPlatformFontPath(noType1Font);
416
417 if (extraFontPath != null) {
418 fontPath = extraFontPath + File.pathSeparator + fontPath;
419 }
420 if (appendToPath) {
421 fontPath += File.pathSeparator + dbgFontPath;
422 } else if (prependToPath) {
423 fontPath = dbgFontPath + File.pathSeparator + fontPath;
424 } else {
425 fontPath = dbgFontPath;
426 }
427 registerFontDirs(fontPath);
428 } else if (extraFontPath != null) {
544 * its handle point to this new font.
545 * This ensures that when the altNameCache that is passed in
546 * is the global mapNameCache - ie we are running as an application -
547 * that any statically created java.awt.Font instances which already
548 * have a Font2D instance will have that re-directed to the new Font
549 * on subsequent uses. This is particularly important for "the"
550 * default font instance, or similar cases where a UI toolkit (eg
551 * Swing) has cached a java.awt.Font. Note that if Swing is using
552 * a custom composite APIs which update the standard composites have
553 * no effect - this is typically the case only when using the Windows
554 * L&F where these APIs would conflict with that L&F anyway.
555 */
556 Font2D oldFont =altNameCache.get(compositeName.toLowerCase(Locale.ENGLISH));
557 if (oldFont instanceof CompositeFont) {
558 oldFont.handle.font2D = cf;
559 }
560 altNameCache.put(compositeName.toLowerCase(Locale.ENGLISH), cf);
561 }
562
563 private void addCompositeToFontList(CompositeFont f, int rank) {
564 FontUtilities.logInfo("Add to Family "+ f.familyName +
565 ", Font " + f.fullName + " rank="+rank);
566 f.setRank(rank);
567 compositeFonts.put(f.fullName, f);
568 fullNameToFont.put(f.fullName.toLowerCase(Locale.ENGLISH), f);
569
570 FontFamily family = FontFamily.getFamily(f.familyName);
571 if (family == null) {
572 family = new FontFamily(f.familyName, true, rank);
573 }
574 family.setFont(f, f.style);
575 }
576
577 /*
578 * Systems may have fonts with the same name.
579 * We want to register only one of such fonts (at least until
580 * such time as there might be APIs which can accommodate > 1).
581 * Rank is 1) font configuration fonts, 2) JRE fonts, 3) OT/TT fonts,
582 * 4) Type1 fonts, 5) native fonts.
583 *
584 * If the new font has the same name as the old font, the higher
585 * ranked font gets added, replacing the lower ranked one.
603 * If it returns a different object it means this font already exists,
604 * and you should use that one.
605 * If it returns null means this font was not registered and none
606 * in that name is registered. The caller must find a substitute
607 */
608 // MACOSX begin -- need to access this in subclass
609 protected PhysicalFont addToFontList(PhysicalFont f, int rank) {
610 // MACOSX end
611
612 String fontName = f.fullName;
613 String familyName = f.familyName;
614 if (fontName == null || fontName.isEmpty()) {
615 return null;
616 }
617 if (compositeFonts.containsKey(fontName)) {
618 /* Don't register any font that has the same name as a composite */
619 return null;
620 }
621 f.setRank(rank);
622 if (!physicalFonts.containsKey(fontName)) {
623 FontUtilities.logInfo("Add to Family "+familyName +
624 ", Font " + fontName + " rank="+rank);
625 physicalFonts.put(fontName, f);
626 FontFamily family = FontFamily.getFamily(familyName);
627 if (family == null) {
628 family = new FontFamily(familyName, false, rank);
629 family.setFont(f, f.style);
630 } else {
631 family.setFont(f, f.style);
632 }
633 fullNameToFont.put(fontName.toLowerCase(Locale.ENGLISH), f);
634 return f;
635 } else {
636 PhysicalFont newFont = f;
637 PhysicalFont oldFont = physicalFonts.get(fontName);
638 if (oldFont == null) {
639 return null;
640 }
641 /* If the new font is of an equal or higher rank, it is a
642 * candidate to replace the current one, subject to further tests.
643 */
644 if (oldFont.getRank() >= rank) {
675 newFont instanceof TrueTypeFont) {
676 TrueTypeFont oldTTFont = (TrueTypeFont)oldFont;
677 TrueTypeFont newTTFont = (TrueTypeFont)newFont;
678 if (oldTTFont.fileSize >= newTTFont.fileSize) {
679 return oldFont;
680 }
681 } else {
682 return oldFont;
683 }
684 }
685 /* Don't replace ever JRE fonts.
686 * This test is in case a font configuration references
687 * a Lucida font, which has been mapped to a Lucida
688 * from the host O/S. The assumption here is that any
689 * such font configuration file is probably incorrect, or
690 * the host O/S version is for the use of AWT.
691 * In other words if we reach here, there's a possible
692 * problem with our choice of font configuration fonts.
693 */
694 if (oldFont.platName.startsWith(jreFontDirName)) {
695 FontUtilities.logWarning("Unexpected attempt to replace a JRE " +
696 " font " + fontName + " from " + oldFont.platName +
697 " with " + newFont.platName);
698 return oldFont;
699 }
700
701 FontUtilities.logInfo("Replace in Family " + familyName +
702 ",Font " + fontName + " new rank="+rank +
703 " from " + oldFont.platName +
704 " with " + newFont.platName);
705 replaceFont(oldFont, newFont);
706 physicalFonts.put(fontName, newFont);
707 fullNameToFont.put(fontName.toLowerCase(Locale.ENGLISH),
708 newFont);
709
710 FontFamily family = FontFamily.getFamily(familyName);
711 if (family == null) {
712 family = new FontFamily(familyName, false, rank);
713 family.setFont(newFont, newFont.style);
714 } else {
715 family.setFont(newFont, newFont.style);
716 }
717 return newFont;
718 } else {
719 return oldFont;
720 }
721 }
722 }
723
724 public Font2D[] getRegisteredFonts() {
872
873 public void registerDeferredFont(String fileNameKey,
874 String fullPathName,
875 String[] nativeNames,
876 int fontFormat,
877 boolean useJavaRasterizer,
878 int fontRank) {
879 FontRegistrationInfo regInfo =
880 new FontRegistrationInfo(fullPathName, nativeNames, fontFormat,
881 useJavaRasterizer, fontRank);
882 deferredFontFiles.put(fileNameKey, regInfo);
883 }
884
885
886 public synchronized
887 PhysicalFont initialiseDeferredFont(String fileNameKey) {
888
889 if (fileNameKey == null) {
890 return null;
891 }
892 FontUtilities.logInfo("Opening deferred font file " + fileNameKey);
893
894 PhysicalFont physicalFont = null;
895 FontRegistrationInfo regInfo = deferredFontFiles.get(fileNameKey);
896 if (regInfo != null) {
897 deferredFontFiles.remove(fileNameKey);
898 physicalFont = registerFontFile(regInfo.fontFilePath,
899 regInfo.nativeNames,
900 regInfo.fontFormat,
901 regInfo.javaRasterizer,
902 regInfo.fontRank);
903
904 if (physicalFont != null) {
905 /* Store the handle, so that if a font is bad, we
906 * retrieve the substituted font.
907 */
908 initialisedFonts.put(fileNameKey, physicalFont.handle);
909 } else {
910 initialisedFonts.put(fileNameKey, FONT_HANDLE_NULL);
911 }
912 } else {
957 PhysicalFont pf = addToFontList(ttf, fontRank);
958 if (physicalFont == null) {
959 physicalFont = pf;
960 }
961 }
962 while (fn < ttf.getFontCount());
963 break;
964
965 case FONTFORMAT_TYPE1:
966 Type1Font t1f = new Type1Font(fileName, nativeNames);
967 physicalFont = addToFontList(t1f, fontRank);
968 break;
969
970 case FONTFORMAT_NATIVE:
971 NativeFont nf = new NativeFont(fileName, false);
972 physicalFont = addToFontList(nf, fontRank);
973 break;
974 default:
975
976 }
977 FontUtilities.logInfo("Registered file " + fileName + " as font " +
978 physicalFont + " rank=" + fontRank);
979 } catch (FontFormatException ffe) {
980 FontUtilities.logInfo("Unusable font: " + fileName + " " + ffe.toString());
981 }
982 if (physicalFont != null &&
983 fontFormat != FONTFORMAT_NATIVE) {
984 registeredFonts.put(fileName, physicalFont);
985 }
986 return physicalFont;
987 }
988
989 public void registerFonts(String[] fileNames,
990 String[][] nativeNames,
991 int fontCount,
992 int fontFormat,
993 boolean useJavaRasterizer,
994 int fontRank, boolean defer) {
995
996 for (int i=0; i < fontCount; i++) {
997 if (defer) {
998 registerDeferredFont(fileNames[i],fileNames[i], nativeNames[i],
999 fontFormat, useJavaRasterizer, fontRank);
1000 } else {
1002 fontFormat, useJavaRasterizer, fontRank);
1003 }
1004 }
1005 }
1006
1007 /*
1008 * This is the Physical font used when some other font on the system
1009 * can't be located. There has to be at least one font or the font
1010 * system is not useful and the graphics environment cannot sustain
1011 * the Java platform.
1012 */
1013 public PhysicalFont getDefaultPhysicalFont() {
1014 if (defaultPhysicalFont == null) {
1015 String defaultFontName = getDefaultFontFaceName();
1016 // findFont2D will load all fonts
1017 Font2D font2d = findFont2D(defaultFontName, Font.PLAIN, NO_FALLBACK);
1018 if (font2d != null) {
1019 if (font2d instanceof PhysicalFont) {
1020 defaultPhysicalFont = (PhysicalFont)font2d;
1021 } else {
1022 FontUtilities.logWarning("Font returned by findFont2D for default font name " +
1023 defaultFontName + " is not a physical font: " + font2d.getFontName(null));
1024 }
1025 }
1026 if (defaultPhysicalFont == null) {
1027 /* Because of the findFont2D call above, if we reach here, we
1028 * know all fonts have already been loaded, just accept any
1029 * match at this point. If this fails we are in real trouble
1030 * and I don't know how to recover from there being absolutely
1031 * no fonts anywhere on the system.
1032 */
1033 defaultPhysicalFont = physicalFonts.values().stream().findFirst()
1034 .orElseThrow(()->new Error("Probable fatal error: No physical fonts found."));
1035 }
1036 }
1037 return defaultPhysicalFont;
1038 }
1039
1040 public Font2D getDefaultLogicalFont(int style) {
1041 return findFont2D("dialog", style, NO_FALLBACK);
1042 }
1043
1044 /*
1045 * return String representation of style prepended with "."
1261 resolveFontFiles(unmappedFontFiles, unmappedFontNames);
1262 }
1263
1264 /* remove from the set of names that will be returned to the
1265 * user any fonts that can't be mapped to files.
1266 */
1267 if (unmappedFontNames.size() > 0) {
1268 int sz = unmappedFontNames.size();
1269 for (int i=0; i<sz; i++) {
1270 String name = unmappedFontNames.get(i);
1271 String familyName = fontToFamilyNameMap.get(name);
1272 if (familyName != null) {
1273 ArrayList<String> family = familyToFontListMap.get(familyName);
1274 if (family != null) {
1275 if (family.size() <= 1) {
1276 familyToFontListMap.remove(familyName);
1277 }
1278 }
1279 }
1280 fontToFamilyNameMap.remove(name);
1281 FontUtilities.logInfo("No file for font:" + name);
1282 }
1283 }
1284 }
1285 }
1286
1287 /**
1288 * In some cases windows may have fonts in the fonts folder that
1289 * don't show up in the registry or in the GDI calls to enumerate fonts.
1290 * The only way to find these is to list the directory. We invoke this
1291 * only in getAllFonts/Families, so most searches for a specific
1292 * font that is satisfied by the GDI/registry calls don't take the
1293 * additional hit of listing the directory. This hit is small enough
1294 * that its not significant in these 'enumerate all the fonts' cases.
1295 * The basic approach is to cross-reference the files windows found
1296 * with the ones in the directory listing approach, and for each
1297 * in the latter list that is missing from the former list, register it.
1298 */
1299 private synchronized void checkForUnreferencedFontFiles() {
1300 if (haveCheckedUnreferencedFontFiles) {
1301 return;
1309 * versions of the names from the registry.
1310 */
1311 ArrayList<String> registryFiles = new ArrayList<>();
1312 for (String regFile : fontToFileMap.values()) {
1313 registryFiles.add(regFile.toLowerCase());
1314 }
1315
1316 /* To avoid any issues with concurrent modification, create
1317 * copies of the existing maps, add the new fonts into these
1318 * and then replace the references to the old ones with the
1319 * new maps. ConcurrentHashmap is another option but its a lot
1320 * more changes and with this exception, these maps are intended
1321 * to be static.
1322 */
1323 HashMap<String,String> fontToFileMap2 = null;
1324 HashMap<String,String> fontToFamilyNameMap2 = null;
1325 HashMap<String,ArrayList<String>> familyToFontListMap2 = null;;
1326
1327 for (String pathFile : getFontFilesFromPath(false)) {
1328 if (!registryFiles.contains(pathFile)) {
1329 FontUtilities.logInfo("Found non-registry file : " + pathFile);
1330 PhysicalFont f = registerFontFile(getPathName(pathFile));
1331 if (f == null) {
1332 continue;
1333 }
1334 if (fontToFileMap2 == null) {
1335 fontToFileMap2 = new HashMap<>(fontToFileMap);
1336 fontToFamilyNameMap2 = new HashMap<>(fontToFamilyNameMap);
1337 familyToFontListMap2 = new HashMap<>(familyToFontListMap);
1338 }
1339 String fontName = f.getFontName(null);
1340 String family = f.getFamilyName(null);
1341 String familyLC = family.toLowerCase();
1342 fontToFamilyNameMap2.put(fontName, family);
1343 fontToFileMap2.put(fontName, pathFile);
1344 ArrayList<String> fonts = familyToFontListMap2.get(familyLC);
1345 if (fonts == null) {
1346 fonts = new ArrayList<>();
1347 } else {
1348 fonts = new ArrayList<>(fonts);
1349 }
1351 familyToFontListMap2.put(familyLC, fonts);
1352 }
1353 }
1354 if (fontToFileMap2 != null) {
1355 fontToFileMap = fontToFileMap2;
1356 familyToFontListMap = familyToFontListMap2;
1357 fontToFamilyNameMap = fontToFamilyNameMap2;
1358 }
1359 }
1360
1361 private void resolveFontFiles(HashSet<String> unmappedFiles,
1362 ArrayList<String> unmappedFonts) {
1363
1364 Locale l = SunToolkit.getStartupLocale();
1365
1366 for (String file : unmappedFiles) {
1367 try {
1368 int fn = 0;
1369 TrueTypeFont ttf;
1370 String fullPath = getPathName(file);
1371 FontUtilities.logInfo("Trying to resolve file " + fullPath);
1372 do {
1373 ttf = new TrueTypeFont(fullPath, null, fn++, false);
1374 // prefer the font's locale name.
1375 String fontName = ttf.getFontName(l).toLowerCase();
1376 if (unmappedFonts.contains(fontName)) {
1377 fontToFileMap.put(fontName, file);
1378 unmappedFonts.remove(fontName);
1379 FontUtilities.logInfo("Resolved absent registry entry for " +
1380 fontName + " located in " + fullPath);
1381 }
1382 }
1383 while (fn < ttf.getFontCount());
1384 } catch (Exception e) {
1385 }
1386 }
1387 }
1388
1389 /* Hardwire the English names and expected file names of fonts
1390 * commonly used at start up. Avoiding until later even the small
1391 * cost of calling platform APIs to locate these can help.
1392 * The code that registers these fonts needs to "bail" if any
1393 * of the files do not exist, so it will verify the existence of
1394 * all non-null file names first.
1395 * They are added in to a map with nominally the first
1396 * word in the name of the family as the key. In all the cases
1397 * we are using the family name is a single word, and as is
1398 * more or less required the family name is the initial sequence
1399 * in a full name. So lookup first finds the matching description,
1400 * then registers the whole family, returning the right font.
1401 */
1402 public static class FamilyDescription {
1483 if (boldFile == null) {
1484 failure = true;
1485 }
1486 }
1487
1488 if (fd.italicFileName != null) {
1489 italicFile = getPathName(fd.italicFileName);
1490 if (italicFile == null) {
1491 failure = true;
1492 }
1493 }
1494
1495 if (fd.boldItalicFileName != null) {
1496 boldItalicFile = getPathName(fd.boldItalicFileName);
1497 if (boldItalicFile == null) {
1498 failure = true;
1499 }
1500 }
1501
1502 if (failure) {
1503 FontUtilities.logInfo("Hardcoded file missing looking for " + lcName);
1504 platformFontMap.remove(firstWord);
1505 return null;
1506 }
1507
1508 /* Some of these may be null,as not all styles have to exist */
1509 final String[] files = {
1510 plainFile, boldFile, italicFile, boldItalicFile } ;
1511
1512 failure = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
1513 public Boolean run() {
1514 for (int i=0; i<files.length; i++) {
1515 if (files[i] == null) {
1516 continue;
1517 }
1518 File f = new File(files[i]);
1519 if (!f.exists()) {
1520 return Boolean.TRUE;
1521 }
1522 }
1523 return Boolean.FALSE;
1524 }
1525 });
1526
1527 if (failure) {
1528 FontUtilities.logInfo("Hardcoded file missing looking for " + lcName);
1529 platformFontMap.remove(firstWord);
1530 return null;
1531 }
1532
1533 /* If we reach here we know that we have all the files we
1534 * expect, so all should be fine so long as the contents
1535 * are what we'd expect. Now on to registering the fonts.
1536 * Currently this code only looks for TrueType fonts, so format
1537 * and rank can be specified without looking at the filename.
1538 */
1539 Font2D font = null;
1540 for (int f=0;f<files.length;f++) {
1541 if (files[f] == null) {
1542 continue;
1543 }
1544 PhysicalFont pf =
1545 registerFontFile(files[f], null,
1546 FONTFORMAT_TRUETYPE, false, Font2D.TTF_RANK);
1547 if (f == styleIndex) {
1548 font = pf;
1773 String [] fontList = family.toArray(STR_ARRAY);
1774 if (fontList.length == 0) {
1775 return null;
1776 }
1777
1778 /* first check that for every font in this family we can find
1779 * a font file. The specific reason for doing this is that
1780 * in at least one case on Windows a font has the face name "David"
1781 * but the registry entry is "David Regular". That is the "unique"
1782 * name of the font but in other cases the registry contains the
1783 * "full" name. See the specifications of name ids 3 and 4 in the
1784 * TrueType 'name' table.
1785 * In general this could cause a problem that we fail to register
1786 * if we all members of a family that we may end up mapping to
1787 * the wrong font member: eg return Bold when Plain is needed.
1788 */
1789 for (int f=0;f<fontList.length;f++) {
1790 String fontNameLC = fontList[f].toLowerCase(Locale.ENGLISH);
1791 String fileName = fontToFileMap.get(fontNameLC);
1792 if (fileName == null) {
1793 FontUtilities.logInfo("Platform lookup : No file for font " +
1794 fontList[f] + " in family " +familyName);
1795 return null;
1796 }
1797 }
1798
1799 /* Currently this code only looks for TrueType fonts, so format
1800 * and rank can be specified without looking at the filename.
1801 */
1802 PhysicalFont physicalFont = null;
1803 if (fontFile != null) {
1804 physicalFont = registerFontFile(getPathName(fontFile), null,
1805 FONTFORMAT_TRUETYPE, false,
1806 Font2D.TTF_RANK);
1807 }
1808 /* Register all fonts in this family. */
1809 for (int f=0;f<fontList.length;f++) {
1810 String fontNameLC = fontList[f].toLowerCase(Locale.ENGLISH);
1811 String fileName = fontToFileMap.get(fontNameLC);
1812 if (fontFile != null && fontFile.equals(fileName)) {
1813 continue;
1814 }
1843 * A font may exist with the specified style, or it may
1844 * exist only in some other style. For non-native fonts the scaler
1845 * may be able to emulate the required style.
1846 */
1847 public Font2D findFont2D(String name, int style, int fallback) {
1848 if (name == null) return null;
1849 String lowerCaseName = name.toLowerCase(Locale.ENGLISH);
1850 String mapName = lowerCaseName + dotStyleStr(style);
1851
1852 /* If preferLocaleFonts() or preferProportionalFonts() has been
1853 * called we may be using an alternate set of composite fonts in this
1854 * app context. The presence of a pre-built name map indicates whether
1855 * this is so, and gives access to the alternate composite for the
1856 * name.
1857 */
1858 Font2D font = fontNameCache.get(mapName);
1859 if (font != null) {
1860 return font;
1861 }
1862
1863 FontUtilities.logInfo("Search for font: " + name);
1864
1865 // The check below is just so that the bitmap fonts being set by
1866 // AWT and Swing thru the desktop properties do not trigger the
1867 // the load fonts case. The two bitmap fonts are now mapped to
1868 // appropriate equivalents for serif and sansserif.
1869 // Note that the cost of this comparison is only for the first
1870 // call until the map is filled.
1871 if (FontUtilities.isWindows) {
1872 if (lowerCaseName.equals("ms sans serif")) {
1873 name = "sansserif";
1874 } else if (lowerCaseName.equals("ms serif")) {
1875 name = "serif";
1876 }
1877 }
1878
1879 /* This isn't intended to support a client passing in the
1880 * string default, but if a client passes in null for the name
1881 * the java.awt.Font class internally substitutes this name.
1882 * So we need to recognise it here to prevent a loadFonts
1883 * on the unrecognised name. The only potential problem with
1955 /* The next check is perhaps one
1956 * that shouldn't be done. ie if we get this
1957 * far we have probably as close a match as we
1958 * are going to get. We could load all fonts to
1959 * see if somehow some parts of the family are
1960 * loaded but not all of it.
1961 */
1962 if (familyFont.canDoStyle(style|font.style)) {
1963 fontNameCache.put(mapName, familyFont);
1964 return familyFont;
1965 }
1966 }
1967 }
1968 }
1969 }
1970 }
1971
1972 if (FontUtilities.isWindows) {
1973
1974 font = findFontFromPlatformMap(lowerCaseName, style);
1975 FontUtilities.logInfo("findFontFromPlatformMap returned " + font);
1976
1977 if (font != null) {
1978 fontNameCache.put(mapName, font);
1979 return font;
1980 }
1981 /* Don't want Windows to return a font from C:\Windows\Fonts
1982 * if someone has installed a font with the same name
1983 * in the JRE.
1984 */
1985 if (deferredFontFiles.size() > 0) {
1986 font = findJREDeferredFont(lowerCaseName, style);
1987 if (font != null) {
1988 fontNameCache.put(mapName, font);
1989 return font;
1990 }
1991 }
1992 font = findFontFromPlatform(lowerCaseName, style);
1993 if (font != null) {
1994 FontUtilities.logInfo("Found font via platform API for request:\"" +
1995 name + "\":, style="+style+
1996 " found font: " + font);
1997 fontNameCache.put(mapName, font);
1998 return font;
1999 }
2000 }
2001
2002 /* If reach here and no match has been located, then if there are
2003 * uninitialised deferred fonts, load as many of those as needed
2004 * to find the deferred font. If none is found through that
2005 * search continue on.
2006 * There is possibly a minor issue when more than one
2007 * deferred font implements the same font face. Since deferred
2008 * fonts are only those in font configuration files, this is a
2009 * controlled situation, the known case being Solaris euro_fonts
2010 * versions of Arial, Times New Roman, Courier New. However
2011 * the larger font will transparently replace the smaller one
2012 * - see addToFontList() - when it is needed by the composite font.
2013 */
2014 if (deferredFontFiles.size() > 0) {
2015 font = findDeferredFont(name, style);
2016 if (font != null) {
2045 if (font != null) {
2046 if (fontsAreRegistered) {
2047 fontNameCache.put(mapName, font);
2048 }
2049 return font;
2050 }
2051 }
2052 font = nameTable.get(lowerCaseName);
2053 if (font != null) {
2054 if (fontsAreRegistered) {
2055 fontNameCache.put(mapName, font);
2056 }
2057 return font;
2058 }
2059 }
2060
2061 /* If reach here and no match has been located, then if all fonts
2062 * are not yet loaded, do so, and then recurse.
2063 */
2064 if (!loadedAllFonts) {
2065 FontUtilities.logInfo("Load fonts looking for:" + name);
2066 loadFonts();
2067 loadedAllFonts = true;
2068 return findFont2D(name, style, fallback);
2069 }
2070
2071 if (!loadedAllFontFiles) {
2072 FontUtilities.logInfo("Load font files looking for:" + name);
2073 loadFontFiles();
2074 loadedAllFontFiles = true;
2075 return findFont2D(name, style, fallback);
2076 }
2077
2078 /* The primary name is the locale default - ie not US/English but
2079 * whatever is the default in this locale. This is the way it always
2080 * has been but may be surprising to some developers if "Arial Regular"
2081 * were hard-coded in their app and yet "Arial Regular" was not the
2082 * default name. Fortunately for them, as a consequence of the JDK
2083 * supporting returning names and family names for arbitrary locales,
2084 * we also need to support searching all localised names for a match.
2085 * But because this case of the name used to reference a font is not
2086 * the same as the default for this locale is rare, it makes sense to
2087 * search a much shorter list of default locale names and only go to
2088 * a longer list of names in the event that no match was found.
2089 * So add here code which searches localised names too.
2090 * As in 1.4.x this happens only after loading all fonts, which
2091 * is probably the right order.
2092 */
2110 getFontConfiguration().getFallbackFamilyName(name, null);
2111 if (compatName != null) {
2112 font = findFont2D(compatName, style, fallback);
2113 fontNameCache.put(mapName, font);
2114 return font;
2115 }
2116 } else if (lowerCaseName.equals("timesroman")) {
2117 font = findFont2D("serif", style, fallback);
2118 fontNameCache.put(mapName, font);
2119 return font;
2120 } else if (lowerCaseName.equals("helvetica")) {
2121 font = findFont2D("sansserif", style, fallback);
2122 fontNameCache.put(mapName, font);
2123 return font;
2124 } else if (lowerCaseName.equals("courier")) {
2125 font = findFont2D("monospaced", style, fallback);
2126 fontNameCache.put(mapName, font);
2127 return font;
2128 }
2129
2130 FontUtilities.logInfo("No font found for:" + name);
2131
2132 switch (fallback) {
2133 case PHYSICAL_FALLBACK: return getDefaultPhysicalFont();
2134 case LOGICAL_FALLBACK: return getDefaultLogicalFont(style);
2135 default: return null;
2136 }
2137 }
2138
2139 /*
2140 * Workaround for apps which are dependent on a font metrics bug
2141 * in JDK 1.1. This is an unsupported win32 private setting.
2142 * Left in for a customer - do not remove.
2143 */
2144 public boolean usePlatformFontMetrics() {
2145 return usePlatformFontMetrics;
2146 }
2147
2148 public int getNumFonts() {
2149 return physicalFonts.size()+maxCompFont;
2150 }
2283 return null;
2284 }
2285
2286 /*
2287 * This is called when font is determined to be invalid/bad.
2288 * It designed to be called (for example) by the font scaler
2289 * when in processing a font file it is discovered to be incorrect.
2290 * This is different than the case where fonts are discovered to
2291 * be incorrect during initial verification, as such fonts are
2292 * never registered.
2293 * Handles to this font held are re-directed to a default font.
2294 * This default may not be an ideal substitute buts it better than
2295 * crashing This code assumes a PhysicalFont parameter as it doesn't
2296 * make sense for a Composite to be "bad".
2297 */
2298 public synchronized void deRegisterBadFont(Font2D font2D) {
2299 if (!(font2D instanceof PhysicalFont)) {
2300 /* We should never reach here, but just in case */
2301 return;
2302 } else {
2303 FontUtilities.logSevere("Deregister bad font: " + font2D);
2304 replaceFont((PhysicalFont)font2D, getDefaultPhysicalFont());
2305 }
2306 }
2307
2308 /*
2309 * This encapsulates all the work that needs to be done when a
2310 * Font2D is replaced by a different Font2D.
2311 */
2312 public synchronized void replaceFont(PhysicalFont oldFont,
2313 PhysicalFont newFont) {
2314
2315 if (oldFont.handle.font2D != oldFont) {
2316 /* already done */
2317 return;
2318 }
2319
2320 /* If we try to replace the font with itself, that won't work,
2321 * so pick any alternative physical font
2322 */
2323 if (oldFont == newFont) {
2324 FontUtilities.logSevere("Can't replace bad font with itself " + oldFont);
2325 PhysicalFont[] physFonts = getPhysicalFonts();
2326 for (int i=0; i<physFonts.length;i++) {
2327 if (physFonts[i] != newFont) {
2328 newFont = physFonts[i];
2329 break;
2330 }
2331 }
2332 if (oldFont == newFont) {
2333 FontUtilities.logSevere("This is bad. No good physicalFonts found.");
2334 return;
2335 }
2336 }
2337
2338 /* eliminate references to this font, so it won't be located
2339 * by future callers, and will be eligible for GC when all
2340 * references are removed
2341 */
2342 oldFont.handle.font2D = newFont;
2343 physicalFonts.remove(oldFont.fullName);
2344 fullNameToFont.remove(oldFont.fullName.toLowerCase(Locale.ENGLISH));
2345 FontFamily.remove(oldFont);
2346 if (localeFullNamesToFont != null) {
2347 Map.Entry<?, ?>[] mapEntries = localeFullNamesToFont.entrySet().
2348 toArray(new Map.Entry<?, ?>[0]);
2349 /* Should I be replacing these, or just I just remove
2350 * the names from the map?
2351 */
2352 for (int i=0; i<mapEntries.length;i++) {
2353 if (mapEntries[i].getValue() == oldFont) {
2413 String[] fullNames = ttf.getAllFullNames();
2414 for (int n=0; n<fullNames.length; n++) {
2415 localeFullNamesToFont.put(fullNames[n], ttf);
2416 }
2417 FontFamily family = FontFamily.getFamily(ttf.familyName);
2418 if (family != null) {
2419 FontFamily.addLocaleNames(family, ttf.getAllFamilyNames());
2420 }
2421 }
2422 }
2423 }
2424
2425 /* This replicate the core logic of findFont2D but operates on
2426 * all the locale names. This hasn't been merged into findFont2D to
2427 * keep the logic simpler and reduce overhead, since this case is
2428 * almost never used. The main case in which it is called is when
2429 * a bogus font name is used and we need to check all possible names
2430 * before returning the default case.
2431 */
2432 private Font2D findFont2DAllLocales(String name, int style) {
2433 FontUtilities.logInfo("Searching localised font names for:" + name);
2434
2435 /* If reach here and no match has been located, then if we have
2436 * not yet built the map of localeFullNamesToFont for TT fonts, do so
2437 * now. This method must be called after all fonts have been loaded.
2438 */
2439 if (localeFullNamesToFont == null) {
2440 loadLocaleNames();
2441 }
2442 String lowerCaseName = name.toLowerCase();
2443 Font2D font = null;
2444
2445 /* First see if its a family name. */
2446 FontFamily family = FontFamily.getLocaleFamily(lowerCaseName);
2447 if (family != null) {
2448 font = family.getFont(style);
2449 if (font == null) {
2450 font = family.getClosestStyle(style);
2451 }
2452 if (font != null) {
2453 return font;
2547 * a case cannot retrieve a cached metrics solely on the basis of
2548 * the Font.equals() method since it needs to also check if the Font2D
2549 * is the same.
2550 * We also use non-standard composites for Swing native L&F fonts on
2551 * Windows. In that case the policy is that the metrics reported are
2552 * based solely on the physical font in the first slot which is the
2553 * visible java.awt.Font. So in that case the metrics cache which tests
2554 * the Font does what we want. In the near future when we expand the GTK
2555 * logical font definitions we may need to revisit this if GTK reports
2556 * combined metrics instead. For now though this test can be simple.
2557 */
2558 public boolean usingAlternateCompositeFonts() {
2559 return _usingAlternateComposites;
2560 }
2561
2562 /* Modifies the behaviour of a subsequent call to preferLocaleFonts()
2563 * to use Mincho instead of Gothic for dialoginput in JA locales
2564 * on windows. Not needed on other platforms.
2565 */
2566 public synchronized void useAlternateFontforJALocales() {
2567 FontUtilities.logInfo("Entered useAlternateFontforJALocales().");
2568
2569 if (!FontUtilities.isWindows) {
2570 return;
2571 }
2572 gAltJAFont = true;
2573 }
2574
2575 public boolean usingAlternateFontforJALocales() {
2576 return gAltJAFont;
2577 }
2578
2579 public synchronized void preferLocaleFonts() {
2580 FontUtilities.logInfo("Entered preferLocaleFonts().");
2581
2582 /* Test if re-ordering will have any effect */
2583 if (!FontConfiguration.willReorderForStartupLocale()) {
2584 return;
2585 }
2586 if (gLocalePref == true) {
2587 return;
2588 }
2589 gLocalePref = true;
2590 createCompositeFonts(fontNameCache, gLocalePref, gPropPref);
2591 _usingAlternateComposites = true;
2592 }
2593
2594 public synchronized void preferProportionalFonts() {
2595 FontUtilities.logInfo("Entered preferProportionalFonts().");
2596
2597 /* If no proportional fonts are configured, there's no need
2598 * to take any action.
2599 */
2600 if (!FontConfiguration.hasMonoToPropMap()) {
2601 return;
2602 }
2603 if (gPropPref == true) {
2604 return;
2605 }
2606 gPropPref = true;
2607 createCompositeFonts(fontNameCache, gLocalePref, gPropPref);
2608 _usingAlternateComposites = true;
2609 }
2610
2611 private static HashSet<String> installedNames = null;
2612 private static HashSet<String> getInstalledNames() {
2613 if (installedNames == null) {
2614 Locale l = getSystemStartupLocale();
2615 SunFontManager fontManager = SunFontManager.getInstance();
2616 String[] installedFamilies =
2816 for (int i=0; i < ls.length; i++ ) {
2817 File theFile = new File(dirFile, ls[i]);
2818 String fullName = null;
2819 if (resolveSymLinks) {
2820 try {
2821 fullName = theFile.getCanonicalPath();
2822 } catch (IOException e) {
2823 }
2824 }
2825 if (fullName == null) {
2826 fullName = dirName + File.separator + ls[i];
2827 }
2828
2829 // REMIND: case compare depends on platform
2830 if (registeredFontFiles.contains(fullName)) {
2831 continue;
2832 }
2833
2834 if (badFonts != null && badFonts.contains(fullName)) {
2835 if (FontUtilities.debugFonts()) {
2836 FontUtilities.logWarning("skip bad font " + fullName);
2837 }
2838 continue; // skip this font file.
2839 }
2840
2841 registeredFontFiles.add(fullName);
2842
2843 if (FontUtilities.debugFonts()
2844 && FontUtilities.getLogger().isLoggable(PlatformLogger.Level.INFO)) {
2845 String message = "Registering font " + fullName;
2846 String[] natNames = getNativeNames(fullName, null);
2847 if (natNames == null) {
2848 message += " with no native name";
2849 } else {
2850 message += " with native name(s) " + natNames[0];
2851 for (int nn = 1; nn < natNames.length; nn++) {
2852 message += ", " + natNames[nn];
2853 }
2854 }
2855 FontUtilities.logInfo(message);
2856 }
2857 fontNames[fontCount] = fullName;
2858 nativeNames[fontCount++] = getNativeNames(fullName, null);
2859 }
2860 registerFonts(fontNames, nativeNames, fontCount, fontFormat,
2861 useJavaRasterizer, fontRank, defer);
2862 return;
2863 }
2864
2865 protected String[] getNativeNames(String fontFileName,
2866 String platformName) {
2867 return null;
2868 }
2869
2870 /**
2871 * Returns a file name for the physical font represented by this platform
2872 * font name. The default implementation tries to obtain the file name
2873 * from the font configuration.
2874 * Subclasses may override to provide information from other sources.
2875 */
2885 }
2886
2887 /* A call to this method should be followed by a call to
2888 * registerFontDirs(..)
2889 */
2890 public String getPlatformFontPath(boolean noType1Font) {
2891 if (fontPath == null) {
2892 fontPath = getFontPath(noType1Font);
2893 }
2894 return fontPath;
2895 }
2896
2897 protected void loadFonts() {
2898 if (discoveredAllFonts) {
2899 return;
2900 }
2901 /* Use lock specific to the font system */
2902 synchronized (this) {
2903 if (FontUtilities.debugFonts()) {
2904 Thread.dumpStack();
2905 FontUtilities.logInfo("SunGraphicsEnvironment.loadFonts() called");
2906 }
2907 initialiseDeferredFonts();
2908
2909 AccessController.doPrivileged(new PrivilegedAction<Void>() {
2910 public Void run() {
2911 if (fontPath == null) {
2912 fontPath = getPlatformFontPath(noType1Font);
2913 registerFontDirs(fontPath);
2914 }
2915 if (fontPath != null) {
2916 // this will find all fonts including those already
2917 // registered. But we have checks in place to prevent
2918 // double registration.
2919 if (! gotFontsFromPlatform()) {
2920 registerFontsOnPath(fontPath, false,
2921 Font2D.UNKNOWN_RANK,
2922 false, true);
2923 loadedAllFontFiles = true;
2924 }
2925 }
3003 boolean preferPropFonts);
3004
3005 /**
3006 * Returns face name for default font, or null if
3007 * no face names are used for CompositeFontDescriptors
3008 * for this platform.
3009 */
3010 public synchronized String getDefaultFontFaceName() {
3011 return defaultFontName;
3012 }
3013
3014 public void loadFontFiles() {
3015 loadFonts();
3016 if (loadedAllFontFiles) {
3017 return;
3018 }
3019 /* Use lock specific to the font system */
3020 synchronized (this) {
3021 if (FontUtilities.debugFonts()) {
3022 Thread.dumpStack();
3023 FontUtilities.logInfo("loadAllFontFiles() called");
3024 }
3025 AccessController.doPrivileged(new PrivilegedAction<Void>() {
3026 public Void run() {
3027 if (fontPath == null) {
3028 fontPath = getPlatformFontPath(noType1Font);
3029 }
3030 if (fontPath != null) {
3031 // this will find all fonts including those already
3032 // registered. But we have checks in place to prevent
3033 // double registration.
3034 registerFontsOnPath(fontPath, false,
3035 Font2D.UNKNOWN_RANK,
3036 false, true);
3037 }
3038 loadedAllFontFiles = true;
3039 return null;
3040 }
3041 });
3042 }
3043 }
3044
3045 /*
3046 * This method asks the font configuration API for all platform names
3047 * used as components of composite/logical fonts and iterates over these
3048 * looking up their corresponding file name and registers these fonts.
3049 * It also ensures that the fonts are accessible via platform APIs.
3050 * The composites themselves are then registered.
3051 */
3052 private void
3053 initCompositeFonts(FontConfiguration fontConfig,
3054 ConcurrentHashMap<String, Font2D> altNameCache) {
3055 FontUtilities.logInfo("Initialising composite fonts");
3056
3057 int numCoreFonts = fontConfig.getNumberCoreFonts();
3058 String[] fcFonts = fontConfig.getPlatformFontNames();
3059 for (int f=0; f<fcFonts.length; f++) {
3060 String platformFontName = fcFonts[f];
3061 String fontFileName =
3062 getFileNameFromPlatformName(platformFontName);
3063 String[] nativeNames = null;
3064 if (fontFileName == null
3065 || fontFileName.equals(platformFontName)) {
3066 /* No file located, so register using the platform name,
3067 * i.e. as a native font.
3068 */
3069 fontFileName = platformFontName;
3070 } else {
3071 if (f < numCoreFonts) {
3072 /* If platform APIs also need to access the font, add it
3073 * to a set to be registered with the platform too.
3074 * This may be used to add the parent directory to the X11
3075 * font path if its not already there. See the docs for the
3134 * fall back component fonts to the composite.
3135 */
3136 if (altNameCache != null) {
3137 SunFontManager.registerCompositeFont(
3138 descriptor.getFaceName(),
3139 componentFileNames, componentFaceNames,
3140 descriptor.getCoreComponentCount(),
3141 descriptor.getExclusionRanges(),
3142 descriptor.getExclusionRangeLimits(),
3143 true,
3144 altNameCache);
3145 } else {
3146 registerCompositeFont(descriptor.getFaceName(),
3147 componentFileNames, componentFaceNames,
3148 descriptor.getCoreComponentCount(),
3149 descriptor.getExclusionRanges(),
3150 descriptor.getExclusionRangeLimits(),
3151 true);
3152 }
3153 if (FontUtilities.debugFonts()) {
3154 FontUtilities.logInfo("registered " + descriptor.getFaceName());
3155 }
3156 }
3157 }
3158
3159 /**
3160 * Notifies graphics environment that the logical font configuration
3161 * uses the given platform font name. The graphics environment may
3162 * use this for platform specific initialization.
3163 */
3164 protected void addFontToPlatformFontPath(String platformFontName) {
3165 }
3166
3167 protected void registerFontFile(String fontFileName, String[] nativeNames,
3168 int fontRank, boolean defer) {
3169 // REMIND: case compare depends on platform
3170 if (registeredFontFiles.contains(fontFileName)) {
3171 return;
3172 }
3173 int fontFormat;
3174 if (ttFilter.accept(null, fontFileName)) {
|