1 /* 2 * Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.font; 27 28 import java.awt.Font; 29 import java.awt.FontFormatException; 30 import java.io.BufferedReader; 31 import java.io.File; 32 import java.io.FileInputStream; 33 import java.io.FilenameFilter; 34 import java.io.IOException; 35 import java.io.InputStreamReader; 36 import java.security.AccessController; 37 import java.security.PrivilegedAction; 38 import java.util.ArrayList; 39 import java.util.HashMap; 40 import java.util.HashSet; 41 import java.util.Hashtable; 42 import java.util.List; 43 import java.util.Locale; 44 import java.util.Map; 45 import java.util.NoSuchElementException; 46 import java.util.StringTokenizer; 47 import java.util.TreeMap; 48 import java.util.Vector; 49 import java.util.concurrent.ConcurrentHashMap; 50 51 import javax.swing.plaf.FontUIResource; 52 53 import sun.awt.FontConfiguration; 54 import sun.awt.SunToolkit; 55 import sun.awt.util.ThreadGroupUtils; 56 import sun.java2d.FontSupport; 57 import sun.util.logging.PlatformLogger; 58 59 /** 60 * The base implementation of the {@link FontManager} interface. It implements 61 * the platform independent, shared parts of OpenJDK's FontManager 62 * implementations. The platform specific parts are declared as abstract 63 * methods that have to be implemented by specific implementations. 64 */ 65 public abstract class SunFontManager implements FontSupport, FontManagerForSGE { 66 67 private static class TTFilter implements FilenameFilter { accept(File dir,String name)68 public boolean accept(File dir,String name) { 69 /* all conveniently have the same suffix length */ 70 int offset = name.length()-4; 71 if (offset <= 0) { /* must be at least A.ttf */ 72 return false; 73 } else { 74 return(name.startsWith(".ttf", offset) || 75 name.startsWith(".TTF", offset) || 76 name.startsWith(".ttc", offset) || 77 name.startsWith(".TTC", offset) || 78 name.startsWith(".otf", offset) || 79 name.startsWith(".OTF", offset)); 80 } 81 } 82 } 83 84 private static class T1Filter implements FilenameFilter { accept(File dir,String name)85 public boolean accept(File dir,String name) { 86 if (noType1Font) { 87 return false; 88 } 89 /* all conveniently have the same suffix length */ 90 int offset = name.length()-4; 91 if (offset <= 0) { /* must be at least A.pfa */ 92 return false; 93 } else { 94 return(name.startsWith(".pfa", offset) || 95 name.startsWith(".pfb", offset) || 96 name.startsWith(".PFA", offset) || 97 name.startsWith(".PFB", offset)); 98 } 99 } 100 } 101 102 private static class TTorT1Filter implements FilenameFilter { accept(File dir, String name)103 public boolean accept(File dir, String name) { 104 105 /* all conveniently have the same suffix length */ 106 int offset = name.length()-4; 107 if (offset <= 0) { /* must be at least A.ttf or A.pfa */ 108 return false; 109 } else { 110 boolean isTT = 111 name.startsWith(".ttf", offset) || 112 name.startsWith(".TTF", offset) || 113 name.startsWith(".ttc", offset) || 114 name.startsWith(".TTC", offset) || 115 name.startsWith(".otf", offset) || 116 name.startsWith(".OTF", offset); 117 if (isTT) { 118 return true; 119 } else if (noType1Font) { 120 return false; 121 } else { 122 return(name.startsWith(".pfa", offset) || 123 name.startsWith(".pfb", offset) || 124 name.startsWith(".PFA", offset) || 125 name.startsWith(".PFB", offset)); 126 } 127 } 128 } 129 } 130 131 private static Font2DHandle FONT_HANDLE_NULL = new Font2DHandle(null); 132 133 public static final int FONTFORMAT_NONE = -1; 134 public static final int FONTFORMAT_TRUETYPE = 0; 135 public static final int FONTFORMAT_TYPE1 = 1; 136 public static final int FONTFORMAT_TTC = 2; 137 public static final int FONTFORMAT_COMPOSITE = 3; 138 public static final int FONTFORMAT_NATIVE = 4; 139 140 /* Pool of 20 font file channels chosen because some UTF-8 locale 141 * composite fonts can use up to 16 platform fonts (including the 142 * Lucida fall back). This should prevent channel thrashing when 143 * dealing with one of these fonts. 144 * The pool array stores the fonts, rather than directly referencing 145 * the channels, as the font needs to do the open/close work. 146 */ 147 // MACOSX begin -- need to access these in subclass 148 protected static final int CHANNELPOOLSIZE = 20; 149 protected FileFont[] fontFileCache = new FileFont[CHANNELPOOLSIZE]; 150 // MACOSX end 151 private int lastPoolIndex = 0; 152 153 /* Need to implement a simple linked list scheme for fast 154 * traversal and lookup. 155 * Also want to "fast path" dialog so there's minimal overhead. 156 */ 157 /* There are at exactly 20 composite fonts: 5 faces (but some are not 158 * usually different), in 4 styles. The array may be auto-expanded 159 * later if more are needed, eg for user-defined composites or locale 160 * variants. 161 */ 162 private int maxCompFont = 0; 163 private CompositeFont [] compFonts = new CompositeFont[20]; 164 private ConcurrentHashMap<String, CompositeFont> 165 compositeFonts = new ConcurrentHashMap<>(); 166 private ConcurrentHashMap<String, PhysicalFont> 167 physicalFonts = new ConcurrentHashMap<>(); 168 private ConcurrentHashMap<String, PhysicalFont> 169 registeredFonts = new ConcurrentHashMap<>(); 170 171 /* given a full name find the Font. Remind: there's duplication 172 * here in that this contains the content of compositeFonts + 173 * physicalFonts. 174 */ 175 // MACOSX begin -- need to access this in subclass 176 protected ConcurrentHashMap<String, Font2D> 177 fullNameToFont = new ConcurrentHashMap<>(); 178 // MACOSX end 179 180 /* TrueType fonts have localised names. Support searching all 181 * of these before giving up on a name. 182 */ 183 private HashMap<String, TrueTypeFont> localeFullNamesToFont; 184 185 private PhysicalFont defaultPhysicalFont; 186 187 static boolean longAddresses; 188 private boolean loaded1dot0Fonts = false; 189 boolean loadedAllFonts = false; 190 boolean loadedAllFontFiles = false; 191 String[] jreOtherFontFiles; 192 boolean noOtherJREFontFiles = false; // initial assumption. 193 194 public static String jreLibDirName; 195 public static String jreFontDirName; 196 private static HashSet<String> missingFontFiles = null; 197 private String defaultFontName; 198 private String defaultFontFileName; 199 protected HashSet<String> registeredFontFiles = new HashSet<>(); 200 201 private ArrayList<String> badFonts; 202 /* fontPath is the location of all fonts on the system, excluding the 203 * JRE's own font directory but including any path specified using the 204 * sun.java2d.fontpath property. Together with that property, it is 205 * initialised by the getPlatformFontPath() method 206 * This call must be followed by a call to registerFontDirs(fontPath) 207 * once any extra debugging path has been appended. 208 */ 209 protected String fontPath; 210 private FontConfiguration fontConfig; 211 /* discoveredAllFonts is set to true when all fonts on the font path are 212 * discovered. This usually also implies opening, validating and 213 * registering, but an implementation may be optimized to avold this. 214 * So see also "loadedAllFontFiles" 215 */ 216 private boolean discoveredAllFonts = false; 217 218 /* No need to keep consing up new instances - reuse a singleton. 219 * The trade-off is that these objects don't get GC'd. 220 */ 221 private static final FilenameFilter ttFilter = new TTFilter(); 222 private static final FilenameFilter t1Filter = new T1Filter(); 223 224 private Font[] allFonts; 225 private String[] allFamilies; // cache for default locale only 226 private Locale lastDefaultLocale; 227 228 public static boolean noType1Font; 229 230 /* Used to indicate required return type from toArray(..); */ 231 private static String[] STR_ARRAY = new String[0]; 232 233 /** 234 * Deprecated, unsupported hack - actually invokes a bug! 235 * Left in for a customer, don't remove. 236 */ 237 private boolean usePlatformFontMetrics = false; 238 239 /** 240 * Returns the global SunFontManager instance. This is similar to 241 * {@link FontManagerFactory#getInstance()} but it returns a 242 * SunFontManager instance instead. This is only used in internal classes 243 * where we can safely assume that a SunFontManager is to be used. 244 * 245 * @return the global SunFontManager instance 246 */ getInstance()247 public static SunFontManager getInstance() { 248 FontManager fm = FontManagerFactory.getInstance(); 249 return (SunFontManager) fm; 250 } 251 getTrueTypeFilter()252 public FilenameFilter getTrueTypeFilter() { 253 return ttFilter; 254 } 255 getType1Filter()256 public FilenameFilter getType1Filter() { 257 return t1Filter; 258 } 259 260 /* After we reach MAXSOFTREFCNT, use weak refs for created fonts. 261 * This means that a small number of created fonts as used in a UI app 262 * will not be eagerly collected, but an app that create many will 263 * have them collected more frequently to reclaim storage. 264 */ 265 private static int maxSoftRefCnt = 10; 266 267 static { initStatic()268 initStatic(); 269 } 270 271 @SuppressWarnings("removal") initStatic()272 private static void initStatic() { 273 AccessController.doPrivileged(new PrivilegedAction<Void>() { 274 public Void run() { 275 FontManagerNativeLibrary.load(); 276 277 // JNI throws an exception if a class/method/field is not found, 278 // so there's no need to do anything explicit here. 279 initIDs(); 280 281 switch (StrikeCache.nativeAddressSize) { 282 case 8: longAddresses = true; break; 283 case 4: longAddresses = false; break; 284 default: throw new RuntimeException("Unexpected address size"); 285 } 286 287 noType1Font = "true".equals(System.getProperty("sun.java2d.noType1Font")); 288 jreLibDirName = System.getProperty("java.home","") + File.separator + "lib"; 289 jreFontDirName = jreLibDirName + File.separator + "fonts"; 290 291 maxSoftRefCnt = Integer.getInteger("sun.java2d.font.maxSoftRefs", 10); 292 return null; 293 } 294 }); 295 } 296 297 /** 298 * If the module image layout changes the location of JDK fonts, 299 * this will be updated to reflect that. 300 */ getJDKFontDir()301 public static final String getJDKFontDir() { 302 return jreFontDirName; 303 } 304 getEUDCFont()305 public TrueTypeFont getEUDCFont() { 306 // Overridden in Windows. 307 return null; 308 } 309 310 /* Initialise ptrs used by JNI methods */ initIDs()311 private static native void initIDs(); 312 313 @SuppressWarnings("removal") SunFontManager()314 protected SunFontManager() { 315 AccessController.doPrivileged(new PrivilegedAction<Void>() { 316 public Void run() { 317 File badFontFile = 318 new File(jreFontDirName + File.separator + "badfonts.txt"); 319 if (badFontFile.exists()) { 320 badFonts = new ArrayList<>(); 321 try (FileInputStream fis = new FileInputStream(badFontFile); 322 BufferedReader br = new BufferedReader(new InputStreamReader(fis))) { 323 while (true) { 324 String name = br.readLine(); 325 if (name == null) { 326 break; 327 } else { 328 if (FontUtilities.debugFonts()) { 329 FontUtilities.logWarning("read bad font: " + name); 330 } 331 badFonts.add(name); 332 } 333 } 334 } catch (IOException e) { 335 } 336 } 337 338 /* Here we get the fonts in jre/lib/fonts and register 339 * them so they are always available and preferred over 340 * other fonts. This needs to be registered before the 341 * composite fonts as otherwise some native font that 342 * corresponds may be found as we don't have a way to 343 * handle two fonts of the same name, so the JRE one 344 * must be the first one registered. Pass "true" to 345 * registerFonts method as on-screen these JRE fonts 346 * always go through the JDK rasteriser. 347 */ 348 if (FontUtilities.isLinux || FontUtilities.isBSD) { 349 /* Linux font configuration uses these fonts */ 350 registerFontDir(jreFontDirName); 351 } 352 registerFontsInDir(jreFontDirName, true, Font2D.JRE_RANK, 353 true, false); 354 355 /* Create the font configuration and get any font path 356 * that might be specified. 357 */ 358 fontConfig = createFontConfiguration(); 359 360 String[] fontInfo = getDefaultPlatformFont(); 361 defaultFontName = fontInfo[0]; 362 if (defaultFontName == null && FontUtilities.debugFonts()) { 363 FontUtilities.logWarning("defaultFontName is null"); 364 } 365 defaultFontFileName = fontInfo[1]; 366 367 String extraFontPath = fontConfig.getExtraFontPath(); 368 369 /* In prior releases the debugging font path replaced 370 * all normally located font directories except for the 371 * JRE fonts dir. This directory is still always located 372 * and placed at the head of the path but as an 373 * augmentation to the previous behaviour the 374 * changes below allow you to additionally append to 375 * the font path by starting with append: or prepend by 376 * starting with a prepend: sign. Eg: to append 377 * -Dsun.java2d.fontpath=append:/usr/local/myfonts 378 * and to prepend 379 * -Dsun.java2d.fontpath=prepend:/usr/local/myfonts Disp 380 * 381 * If there is an appendedfontpath it in the font 382 * configuration it is used instead of searching the 383 * system for dirs. 384 * The behaviour of append and prepend is then similar 385 * to the normal case. ie it goes after what 386 * you prepend and * before what you append. If the 387 * sun.java2d.fontpath property is used, but it 388 * neither the append or prepend syntaxes is used then 389 * as except for the JRE dir the path is replaced and it 390 * is up to you to make sure that all the right 391 * directories are located. This is platform and 392 * locale-specific so its almost impossible to get 393 * right, so it should be used with caution. 394 */ 395 boolean prependToPath = false; 396 boolean appendToPath = false; 397 String dbgFontPath = System.getProperty("sun.java2d.fontpath"); 398 399 if (dbgFontPath != null) { 400 if (dbgFontPath.startsWith("prepend:")) { 401 prependToPath = true; 402 dbgFontPath = 403 dbgFontPath.substring("prepend:".length()); 404 } else if (dbgFontPath.startsWith("append:")) { 405 appendToPath = true; 406 dbgFontPath = 407 dbgFontPath.substring("append:".length()); 408 } 409 } 410 411 if (FontUtilities.debugFonts()) { 412 FontUtilities.logInfo("JRE font directory: " + jreFontDirName); 413 FontUtilities.logInfo("Extra font path: " + extraFontPath); 414 FontUtilities.logInfo("Debug font path: " + dbgFontPath); 415 } 416 417 if (dbgFontPath != null) { 418 /* In debugging mode we register all the paths 419 * Caution: this is a very expensive call on Solaris:- 420 */ 421 fontPath = getPlatformFontPath(noType1Font); 422 423 if (extraFontPath != null) { 424 fontPath = extraFontPath + File.pathSeparator + fontPath; 425 } 426 if (appendToPath) { 427 fontPath += File.pathSeparator + dbgFontPath; 428 } else if (prependToPath) { 429 fontPath = dbgFontPath + File.pathSeparator + fontPath; 430 } else { 431 fontPath = dbgFontPath; 432 } 433 registerFontDirs(fontPath); 434 } else if (extraFontPath != null) { 435 /* If the font configuration contains an 436 * "appendedfontpath" entry, it is interpreted as a 437 * set of locations that should always be registered. 438 * It may be additional to locations normally found 439 * for that place, or it may be locations that need 440 * to have all their paths registered to locate all 441 * the needed platform names. 442 * This is typically when the same .TTF file is 443 * referenced from multiple font.dir files and all 444 * of these must be read to find all the native 445 * (XLFD) names for the font, so that X11 font APIs 446 * can be used for as many code points as possible. 447 */ 448 registerFontDirs(extraFontPath); 449 } 450 451 initCompositeFonts(fontConfig, null); 452 453 return null; 454 } 455 }); 456 457 boolean platformFont = AccessController.doPrivileged( 458 new PrivilegedAction<Boolean>() { 459 public Boolean run() { 460 String prop = System.getProperty("java2d.font.usePlatformFont"); 461 String env = System.getenv("JAVA2D_USEPLATFORMFONT"); 462 return "true".equals(prop) || env != null; 463 } 464 }); 465 466 if (platformFont) { 467 usePlatformFontMetrics = true; 468 System.out.println("Enabling platform font metrics for win32. This is an unsupported option."); 469 System.out.println("This yields incorrect composite font metrics as reported by 1.1.x releases."); 470 System.out.println("It is appropriate only for use by applications which do not use any Java 2"); 471 System.out.println("functionality. This property will be removed in a later release."); 472 } 473 } 474 getNewComposite(String family, int style, Font2DHandle handle)475 public Font2DHandle getNewComposite(String family, int style, 476 Font2DHandle handle) { 477 478 if (!(handle.font2D instanceof CompositeFont)) { 479 return handle; 480 } 481 482 CompositeFont oldComp = (CompositeFont)handle.font2D; 483 PhysicalFont oldFont = oldComp.getSlotFont(0); 484 485 if (family == null) { 486 family = oldFont.getFamilyName(null); 487 } 488 if (style == -1) { 489 style = oldComp.getStyle(); 490 } 491 492 Font2D newFont = findFont2D(family, style, NO_FALLBACK); 493 if (!(newFont instanceof PhysicalFont)) { 494 newFont = oldFont; 495 } 496 PhysicalFont physicalFont = (PhysicalFont)newFont; 497 CompositeFont dialog2D = 498 (CompositeFont)findFont2D("dialog", style, NO_FALLBACK); 499 if (dialog2D == null) { /* shouldn't happen */ 500 return handle; 501 } 502 CompositeFont compFont = new CompositeFont(physicalFont, dialog2D); 503 Font2DHandle newHandle = new Font2DHandle(compFont); 504 return newHandle; 505 } 506 registerCompositeFont(String compositeName, String[] componentFileNames, String[] componentNames, int numMetricsSlots, int[] exclusionRanges, int[] exclusionMaxIndex, boolean defer)507 protected void registerCompositeFont(String compositeName, 508 String[] componentFileNames, 509 String[] componentNames, 510 int numMetricsSlots, 511 int[] exclusionRanges, 512 int[] exclusionMaxIndex, 513 boolean defer) { 514 515 CompositeFont cf = new CompositeFont(compositeName, 516 componentFileNames, 517 componentNames, 518 numMetricsSlots, 519 exclusionRanges, 520 exclusionMaxIndex, defer, this); 521 addCompositeToFontList(cf, Font2D.FONT_CONFIG_RANK); 522 synchronized (compFonts) { 523 compFonts[maxCompFont++] = cf; 524 } 525 } 526 527 /* This variant is used only when the application specifies 528 * a variant of composite fonts which prefers locale specific or 529 * proportional fonts. 530 */ registerCompositeFont(String compositeName, String[] componentFileNames, String[] componentNames, int numMetricsSlots, int[] exclusionRanges, int[] exclusionMaxIndex, boolean defer, ConcurrentHashMap<String, Font2D> altNameCache)531 protected static void registerCompositeFont(String compositeName, 532 String[] componentFileNames, 533 String[] componentNames, 534 int numMetricsSlots, 535 int[] exclusionRanges, 536 int[] exclusionMaxIndex, 537 boolean defer, 538 ConcurrentHashMap<String, Font2D> 539 altNameCache) { 540 541 CompositeFont cf = new CompositeFont(compositeName, 542 componentFileNames, 543 componentNames, 544 numMetricsSlots, 545 exclusionRanges, 546 exclusionMaxIndex, defer, 547 SunFontManager.getInstance()); 548 549 /* if the cache has an existing composite for this case, make 550 * its handle point to this new font. 551 * This ensures that when the altNameCache that is passed in 552 * is the global mapNameCache - ie we are running as an application - 553 * that any statically created java.awt.Font instances which already 554 * have a Font2D instance will have that re-directed to the new Font 555 * on subsequent uses. This is particularly important for "the" 556 * default font instance, or similar cases where a UI toolkit (eg 557 * Swing) has cached a java.awt.Font. Note that if Swing is using 558 * a custom composite APIs which update the standard composites have 559 * no effect - this is typically the case only when using the Windows 560 * L&F where these APIs would conflict with that L&F anyway. 561 */ 562 Font2D oldFont =altNameCache.get(compositeName.toLowerCase(Locale.ENGLISH)); 563 if (oldFont instanceof CompositeFont) { 564 oldFont.handle.font2D = cf; 565 } 566 altNameCache.put(compositeName.toLowerCase(Locale.ENGLISH), cf); 567 } 568 addCompositeToFontList(CompositeFont f, int rank)569 private void addCompositeToFontList(CompositeFont f, int rank) { 570 if (FontUtilities.isLogging()) { 571 FontUtilities.logInfo("Add to Family " + f.familyName + 572 ", Font " + f.fullName + " rank=" + rank); 573 } 574 f.setRank(rank); 575 compositeFonts.put(f.fullName, f); 576 fullNameToFont.put(f.fullName.toLowerCase(Locale.ENGLISH), f); 577 578 FontFamily family = FontFamily.getFamily(f.familyName); 579 if (family == null) { 580 family = new FontFamily(f.familyName, true, rank); 581 } 582 family.setFont(f, f.style); 583 } 584 585 /* 586 * Systems may have fonts with the same name. 587 * We want to register only one of such fonts (at least until 588 * such time as there might be APIs which can accommodate > 1). 589 * Rank is 1) font configuration fonts, 2) JRE fonts, 3) OT/TT fonts, 590 * 4) Type1 fonts, 5) native fonts. 591 * 592 * If the new font has the same name as the old font, the higher 593 * ranked font gets added, replacing the lower ranked one. 594 * If the fonts are of equal rank, then make a special case of 595 * font configuration rank fonts, which are on closer inspection, 596 * OT/TT fonts such that the larger font is registered. This is 597 * a heuristic since a font may be "larger" in the sense of more 598 * code points, or be a larger "file" because it has more bitmaps. 599 * So it is possible that using filesize may lead to less glyphs, and 600 * using glyphs may lead to lower quality display. Probably number 601 * of glyphs is the ideal, but filesize is information we already 602 * have and is good enough for the known cases. 603 * Also don't want to register fonts that match JRE font families 604 * but are coming from a source other than the JRE. 605 * This will ensure that we will algorithmically style the JRE 606 * plain font and get the same set of glyphs for all styles. 607 * 608 * Note that this method returns a value 609 * if it returns the same object as its argument that means this 610 * font was newly registered. 611 * If it returns a different object it means this font already exists, 612 * and you should use that one. 613 * If it returns null means this font was not registered and none 614 * in that name is registered. The caller must find a substitute 615 */ 616 // MACOSX begin -- need to access this in subclass addToFontList(PhysicalFont f, int rank)617 protected PhysicalFont addToFontList(PhysicalFont f, int rank) { 618 // MACOSX end 619 620 String fontName = f.fullName; 621 String familyName = f.familyName; 622 if (fontName == null || fontName.isEmpty()) { 623 return null; 624 } 625 if (compositeFonts.containsKey(fontName)) { 626 /* Don't register any font that has the same name as a composite */ 627 return null; 628 } 629 f.setRank(rank); 630 if (!physicalFonts.containsKey(fontName)) { 631 if (FontUtilities.isLogging()) { 632 FontUtilities.logInfo("Add to Family " + familyName + 633 ", Font " + fontName + " rank=" + rank); 634 } 635 physicalFonts.put(fontName, f); 636 FontFamily family = FontFamily.getFamily(familyName); 637 if (family == null) { 638 family = new FontFamily(familyName, false, rank); 639 family.setFont(f, f.style); 640 } else { 641 family.setFont(f, f.style); 642 } 643 fullNameToFont.put(fontName.toLowerCase(Locale.ENGLISH), f); 644 return f; 645 } else { 646 PhysicalFont newFont = f; 647 PhysicalFont oldFont = physicalFonts.get(fontName); 648 if (oldFont == null) { 649 return null; 650 } 651 /* If the new font is of an equal or higher rank, it is a 652 * candidate to replace the current one, subject to further tests. 653 */ 654 if (oldFont.getRank() >= rank) { 655 656 /* All fonts initialise their mapper when first 657 * used. If the mapper is non-null then this font 658 * has been accessed at least once. In that case 659 * do not replace it. This may be overly stringent, 660 * but its probably better not to replace a font that 661 * someone is already using without a compelling reason. 662 * Additionally the primary case where it is known 663 * this behaviour is important is in certain composite 664 * fonts, and since all the components of a given 665 * composite are usually initialised together this 666 * is unlikely. For this to be a problem, there would 667 * have to be a case where two different composites used 668 * different versions of the same-named font, and they 669 * were initialised and used at separate times. 670 * In that case we continue on and allow the new font to 671 * be installed, but replaceFont will continue to allow 672 * the original font to be used in Composite fonts. 673 */ 674 if (oldFont.mapper != null && rank > Font2D.FONT_CONFIG_RANK) { 675 return oldFont; 676 } 677 678 /* Normally we require a higher rank to replace a font, 679 * but as a special case, if the two fonts are the same rank, 680 * and are instances of TrueTypeFont we want the 681 * more complete (larger) one. 682 */ 683 if (oldFont.getRank() == rank) { 684 if (oldFont instanceof TrueTypeFont && 685 newFont instanceof TrueTypeFont) { 686 TrueTypeFont oldTTFont = (TrueTypeFont)oldFont; 687 TrueTypeFont newTTFont = (TrueTypeFont)newFont; 688 if (oldTTFont.fileSize >= newTTFont.fileSize) { 689 return oldFont; 690 } 691 } else { 692 return oldFont; 693 } 694 } 695 /* Don't replace ever JRE fonts. 696 * This test is in case a font configuration references 697 * a Lucida font, which has been mapped to a Lucida 698 * from the host O/S. The assumption here is that any 699 * such font configuration file is probably incorrect, or 700 * the host O/S version is for the use of AWT. 701 * In other words if we reach here, there's a possible 702 * problem with our choice of font configuration fonts. 703 */ 704 if (oldFont.platName.startsWith(jreFontDirName)) { 705 if (FontUtilities.isLogging()) { 706 FontUtilities.logWarning("Unexpected attempt to replace a JRE " + 707 " font " + fontName + " from " + oldFont.platName + 708 " with " + newFont.platName); 709 } 710 return oldFont; 711 } 712 713 if (FontUtilities.isLogging()) { 714 FontUtilities.logInfo("Replace in Family " + familyName + 715 ",Font " + fontName + " new rank="+rank + 716 " from " + oldFont.platName + 717 " with " + newFont.platName); 718 } 719 replaceFont(oldFont, newFont); 720 physicalFonts.put(fontName, newFont); 721 fullNameToFont.put(fontName.toLowerCase(Locale.ENGLISH), 722 newFont); 723 724 FontFamily family = FontFamily.getFamily(familyName); 725 if (family == null) { 726 family = new FontFamily(familyName, false, rank); 727 family.setFont(newFont, newFont.style); 728 } else { 729 family.setFont(newFont, newFont.style); 730 } 731 return newFont; 732 } else { 733 return oldFont; 734 } 735 } 736 } 737 getRegisteredFonts()738 public Font2D[] getRegisteredFonts() { 739 PhysicalFont[] physFonts = getPhysicalFonts(); 740 int mcf = maxCompFont; /* for MT-safety */ 741 Font2D[] regFonts = new Font2D[physFonts.length+mcf]; 742 System.arraycopy(compFonts, 0, regFonts, 0, mcf); 743 System.arraycopy(physFonts, 0, regFonts, mcf, physFonts.length); 744 return regFonts; 745 } 746 getPhysicalFonts()747 protected PhysicalFont[] getPhysicalFonts() { 748 return physicalFonts.values().toArray(new PhysicalFont[0]); 749 } 750 751 752 /* The class FontRegistrationInfo is used when a client says not 753 * to register a font immediately. This mechanism is used to defer 754 * initialisation of all the components of composite fonts at JRE 755 * start-up. The CompositeFont class is "aware" of this and when it 756 * is first used it asks for the registration of its components. 757 * Also in the event that any physical font is requested the 758 * deferred fonts are initialised before triggering a search of the 759 * system. 760 * Two maps are used. One to track the deferred fonts. The 761 * other to track the fonts that have been initialised through this 762 * mechanism. 763 */ 764 765 private static final class FontRegistrationInfo { 766 767 String fontFilePath; 768 String[] nativeNames; 769 int fontFormat; 770 boolean javaRasterizer; 771 int fontRank; 772 FontRegistrationInfo(String fontPath, String[] names, int format, boolean useJavaRasterizer, int rank)773 FontRegistrationInfo(String fontPath, String[] names, int format, 774 boolean useJavaRasterizer, int rank) { 775 this.fontFilePath = fontPath; 776 this.nativeNames = names; 777 this.fontFormat = format; 778 this.javaRasterizer = useJavaRasterizer; 779 this.fontRank = rank; 780 } 781 } 782 783 private final ConcurrentHashMap<String, FontRegistrationInfo> 784 deferredFontFiles = new ConcurrentHashMap<>(); 785 private final ConcurrentHashMap<String, Font2DHandle> 786 initialisedFonts = new ConcurrentHashMap<>(); 787 788 /* Remind: possibly enhance initialiseDeferredFonts() to be 789 * optionally given a name and a style and it could stop when it 790 * finds that font - but this would be a problem if two of the 791 * fonts reference the same font face name (cf the Solaris 792 * euro fonts). 793 */ initialiseDeferredFonts()794 protected synchronized void initialiseDeferredFonts() { 795 for (String fileName : deferredFontFiles.keySet()) { 796 initialiseDeferredFont(fileName); 797 } 798 } 799 registerDeferredJREFonts(String jreDir)800 protected synchronized void registerDeferredJREFonts(String jreDir) { 801 for (FontRegistrationInfo info : deferredFontFiles.values()) { 802 if (info.fontFilePath != null && 803 info.fontFilePath.startsWith(jreDir)) { 804 initialiseDeferredFont(info.fontFilePath); 805 } 806 } 807 } 808 isDeferredFont(String fileName)809 public boolean isDeferredFont(String fileName) { 810 return deferredFontFiles.containsKey(fileName); 811 } 812 findJREDeferredFont(String name, int style)813 PhysicalFont findJREDeferredFont(String name, int style) { 814 815 /* Iterate over the deferred font files looking for any in the 816 * jre directory that we didn't recognise, open each of these. 817 * In almost all installations this will quickly fall through 818 * because jreOtherFontFiles will be empty. 819 * noOtherJREFontFiles is used so we can skip this block as soon 820 * as its determined that it's not needed - almost always after the 821 * very first time through. 822 */ 823 if (noOtherJREFontFiles) { 824 return null; 825 } 826 synchronized (jreFontDirName) { 827 if (jreOtherFontFiles == null) { 828 HashSet<String> otherFontFiles = new HashSet<>(); 829 for (String deferredFile : deferredFontFiles.keySet()) { 830 File file = new File(deferredFile); 831 String dir = file.getParent(); 832 /* skip names which aren't absolute, aren't in the JRE 833 * directory, or are known Lucida fonts. 834 */ 835 if (dir == null || !dir.equals(jreFontDirName)) { 836 continue; 837 } 838 otherFontFiles.add(deferredFile); 839 } 840 jreOtherFontFiles = otherFontFiles.toArray(STR_ARRAY); 841 if (jreOtherFontFiles.length == 0) { 842 noOtherJREFontFiles = true; 843 } 844 } 845 846 for (int i=0; i<jreOtherFontFiles.length;i++) { 847 String fileName = jreOtherFontFiles[i]; 848 if (fileName == null) { 849 continue; 850 } 851 jreOtherFontFiles[i] = null; 852 PhysicalFont physicalFont = initialiseDeferredFont(fileName); 853 if (physicalFont != null && 854 (physicalFont.getFontName(null).equalsIgnoreCase(name) || 855 physicalFont.getFamilyName(null).equalsIgnoreCase(name)) 856 && physicalFont.style == style) { 857 return physicalFont; 858 } 859 } 860 } 861 862 return null; 863 } 864 findOtherDeferredFont(String name, int style)865 private PhysicalFont findOtherDeferredFont(String name, int style) { 866 for (String fileName : deferredFontFiles.keySet()) { 867 PhysicalFont physicalFont = initialiseDeferredFont(fileName); 868 if (physicalFont != null && 869 (physicalFont.getFontName(null).equalsIgnoreCase(name) || 870 physicalFont.getFamilyName(null).equalsIgnoreCase(name)) && 871 physicalFont.style == style) { 872 return physicalFont; 873 } 874 } 875 return null; 876 } 877 findDeferredFont(String name, int style)878 private PhysicalFont findDeferredFont(String name, int style) { 879 PhysicalFont physicalFont = findJREDeferredFont(name, style); 880 if (physicalFont != null) { 881 return physicalFont; 882 } else { 883 return findOtherDeferredFont(name, style); 884 } 885 } 886 registerDeferredFont(String fileNameKey, String fullPathName, String[] nativeNames, int fontFormat, boolean useJavaRasterizer, int fontRank)887 public void registerDeferredFont(String fileNameKey, 888 String fullPathName, 889 String[] nativeNames, 890 int fontFormat, 891 boolean useJavaRasterizer, 892 int fontRank) { 893 FontRegistrationInfo regInfo = 894 new FontRegistrationInfo(fullPathName, nativeNames, fontFormat, 895 useJavaRasterizer, fontRank); 896 deferredFontFiles.put(fileNameKey, regInfo); 897 } 898 899 900 public synchronized initialiseDeferredFont(String fileNameKey)901 PhysicalFont initialiseDeferredFont(String fileNameKey) { 902 903 if (fileNameKey == null) { 904 return null; 905 } 906 if (FontUtilities.isLogging()) { 907 FontUtilities.logInfo("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 { 929 Font2DHandle handle = initialisedFonts.get(fileNameKey); 930 if (handle == null) { 931 /* Probably shouldn't happen, but just in case */ 932 initialisedFonts.put(fileNameKey, FONT_HANDLE_NULL); 933 } else { 934 physicalFont = (PhysicalFont)(handle.font2D); 935 } 936 } 937 return physicalFont; 938 } 939 isRegisteredFontFile(String name)940 public boolean isRegisteredFontFile(String name) { 941 return registeredFonts.containsKey(name); 942 } 943 getRegisteredFontFile(String name)944 public PhysicalFont getRegisteredFontFile(String name) { 945 return registeredFonts.get(name); 946 } 947 948 /* Note that the return value from this method is not always 949 * derived from this file, and may be null. See addToFontList for 950 * some explanation of this. 951 */ registerFontFile(String fileName, String[] nativeNames, int fontFormat, boolean useJavaRasterizer, int fontRank)952 public PhysicalFont registerFontFile(String fileName, 953 String[] nativeNames, 954 int fontFormat, 955 boolean useJavaRasterizer, 956 int fontRank) { 957 958 PhysicalFont regFont = registeredFonts.get(fileName); 959 if (regFont != null) { 960 return regFont; 961 } 962 963 PhysicalFont physicalFont = null; 964 try { 965 switch (fontFormat) { 966 967 case FONTFORMAT_TRUETYPE: 968 int fn = 0; 969 TrueTypeFont ttf; 970 do { 971 ttf = new TrueTypeFont(fileName, nativeNames, fn++, 972 useJavaRasterizer); 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.logInfo("Registered file " + fileName + " as font " + 995 physicalFont + " rank=" + fontRank); 996 } 997 } catch (FontFormatException ffe) { 998 if (FontUtilities.isLogging()) { 999 FontUtilities.logInfo("Unusable font: " + fileName + " " + ffe.toString()); 1000 } 1001 } 1002 if (physicalFont != null && 1003 fontFormat != FONTFORMAT_NATIVE) { 1004 registeredFonts.put(fileName, physicalFont); 1005 } 1006 return physicalFont; 1007 } 1008 registerFonts(String[] fileNames, String[][] nativeNames, int fontCount, int fontFormat, boolean useJavaRasterizer, int fontRank, boolean defer)1009 public void registerFonts(String[] fileNames, 1010 String[][] nativeNames, 1011 int fontCount, 1012 int fontFormat, 1013 boolean useJavaRasterizer, 1014 int fontRank, boolean defer) { 1015 1016 for (int i=0; i < fontCount; i++) { 1017 if (defer) { 1018 registerDeferredFont(fileNames[i],fileNames[i], nativeNames[i], 1019 fontFormat, useJavaRasterizer, fontRank); 1020 } else { 1021 registerFontFile(fileNames[i], nativeNames[i], 1022 fontFormat, useJavaRasterizer, fontRank); 1023 } 1024 } 1025 } 1026 1027 /* 1028 * This is the Physical font used when some other font on the system 1029 * can't be located. There has to be at least one font or the font 1030 * system is not useful and the graphics environment cannot sustain 1031 * the Java platform. 1032 */ getDefaultPhysicalFont()1033 public PhysicalFont getDefaultPhysicalFont() { 1034 if (defaultPhysicalFont == null) { 1035 String defaultFontName = getDefaultFontFaceName(); 1036 // findFont2D will load all fonts 1037 Font2D font2d = findFont2D(defaultFontName, Font.PLAIN, NO_FALLBACK); 1038 if (font2d != null) { 1039 if (font2d instanceof PhysicalFont) { 1040 defaultPhysicalFont = (PhysicalFont)font2d; 1041 } else { 1042 if (FontUtilities.isLogging()) { 1043 FontUtilities.logWarning("Font returned by findFont2D for default font name " + 1044 defaultFontName + " is not a physical font: " + font2d.getFontName(null)); 1045 } 1046 } 1047 } 1048 if (defaultPhysicalFont == null) { 1049 /* Because of the findFont2D call above, if we reach here, we 1050 * know all fonts have already been loaded, just accept any 1051 * match at this point. If this fails we are in real trouble 1052 * and I don't know how to recover from there being absolutely 1053 * no fonts anywhere on the system. 1054 */ 1055 defaultPhysicalFont = physicalFonts.values().stream().findFirst() 1056 .orElseThrow(()->new Error("Probable fatal error: No physical fonts found.")); 1057 } 1058 } 1059 return defaultPhysicalFont; 1060 } 1061 getDefaultLogicalFont(int style)1062 public Font2D getDefaultLogicalFont(int style) { 1063 return findFont2D("dialog", style, NO_FALLBACK); 1064 } 1065 1066 /* 1067 * return String representation of style prepended with "." 1068 * This is useful for performance to avoid unnecessary string operations. 1069 */ dotStyleStr(int num)1070 private static String dotStyleStr(int num) { 1071 switch(num){ 1072 case Font.BOLD: 1073 return ".bold"; 1074 case Font.ITALIC: 1075 return ".italic"; 1076 case Font.ITALIC | Font.BOLD: 1077 return ".bolditalic"; 1078 default: 1079 return ".plain"; 1080 } 1081 } 1082 1083 /* This is implemented only on windows and is called from code that 1084 * executes only on windows. This isn't pretty but its not a precedent 1085 * in this file. This very probably should be cleaned up at some point. 1086 */ 1087 protected void populateFontFileNameMap(HashMap<String,String> fontToFileMap, HashMap<String,String> fontToFamilyNameMap, HashMap<String,ArrayList<String>> familyToFontListMap, Locale locale)1088 populateFontFileNameMap(HashMap<String,String> fontToFileMap, 1089 HashMap<String,String> fontToFamilyNameMap, 1090 HashMap<String,ArrayList<String>> 1091 familyToFontListMap, 1092 Locale locale) { 1093 } 1094 1095 /* Obtained from Platform APIs (windows only) 1096 * Map from lower-case font full name to basename of font file. 1097 * Eg "arial bold" -> ARIALBD.TTF. 1098 * For TTC files, there is a mapping for each font in the file. 1099 */ 1100 private HashMap<String,String> fontToFileMap = null; 1101 1102 /* Obtained from Platform APIs (windows only) 1103 * Map from lower-case font full name to the name of its font family 1104 * Eg "arial bold" -> "Arial" 1105 */ 1106 private HashMap<String,String> fontToFamilyNameMap = null; 1107 1108 /* Obtained from Platform APIs (windows only) 1109 * Map from a lower-case family name to a list of full names of 1110 * the member fonts, eg: 1111 * "arial" -> ["Arial", "Arial Bold", "Arial Italic","Arial Bold Italic"] 1112 */ 1113 private HashMap<String,ArrayList<String>> familyToFontListMap= null; 1114 1115 /* The directories which contain platform fonts */ 1116 private String[] pathDirs = null; 1117 1118 private boolean haveCheckedUnreferencedFontFiles; 1119 1120 @SuppressWarnings("removal") getFontFilesFromPath(boolean noType1)1121 private String[] getFontFilesFromPath(boolean noType1) { 1122 final FilenameFilter filter; 1123 if (noType1) { 1124 filter = ttFilter; 1125 } else { 1126 filter = new TTorT1Filter(); 1127 } 1128 return AccessController.doPrivileged(new PrivilegedAction<String[]>() { 1129 public String[] run() { 1130 if (pathDirs.length == 1) { 1131 File dir = new File(pathDirs[0]); 1132 String[] files = dir.list(filter); 1133 if (files == null) { 1134 return new String[0]; 1135 } 1136 for (int f=0; f<files.length; f++) { 1137 files[f] = files[f].toLowerCase(); 1138 } 1139 return files; 1140 } else { 1141 ArrayList<String> fileList = new ArrayList<>(); 1142 for (int i = 0; i< pathDirs.length; i++) { 1143 File dir = new File(pathDirs[i]); 1144 String[] files = dir.list(filter); 1145 if (files == null) { 1146 continue; 1147 } 1148 for (int f = 0; f < files.length ; f++) { 1149 fileList.add(files[f].toLowerCase()); 1150 } 1151 } 1152 return fileList.toArray(STR_ARRAY); 1153 } 1154 } 1155 }); 1156 } 1157 1158 /* This is needed since some windows registry names don't match 1159 * the font names. 1160 * - UPC styled font names have a double space, but the 1161 * registry entry mapping to a file doesn't. 1162 * - Marlett is in a hidden file not listed in the registry 1163 * - The registry advertises that the file david.ttf contains a 1164 * font with the full name "David Regular" when in fact its 1165 * just "David". 1166 * Directly fix up these known cases as this is faster. 1167 * If a font which doesn't match these known cases has no file, 1168 * it may be a font that has been temporarily added to the known set 1169 * or it may be an installed font with a missing registry entry. 1170 * Installed fonts are those in the windows font directories. 1171 * Make a best effort attempt to locate these. 1172 * We obtain the list of TrueType fonts in these directories and 1173 * filter out all the font files we already know about from the registry. 1174 * What remains may be "bad" fonts, duplicate fonts, or perhaps the 1175 * missing font(s) we are looking for. 1176 * Open each of these files to find out. 1177 */ 1178 private void resolveWindowsFonts() { 1179 1180 ArrayList<String> unmappedFontNames = null; 1181 for (String font : fontToFamilyNameMap.keySet()) { 1182 String file = fontToFileMap.get(font); 1183 if (file == null) { 1184 if (font.indexOf(" ") > 0) { 1185 String newName = font.replaceFirst(" ", " "); 1186 file = fontToFileMap.get(newName); 1187 /* If this name exists and isn't for a valid name 1188 * replace the mapping to the file with this font 1189 */ 1190 if (file != null && 1191 !fontToFamilyNameMap.containsKey(newName)) { 1192 fontToFileMap.remove(newName); 1193 fontToFileMap.put(font, file); 1194 } 1195 } else if (font.equals("marlett")) { 1196 fontToFileMap.put(font, "marlett.ttf"); 1197 } else if (font.equals("david")) { 1198 file = fontToFileMap.get("david regular"); 1199 if (file != null) { 1200 fontToFileMap.remove("david regular"); 1201 fontToFileMap.put("david", file); 1202 } 1203 } else { 1204 if (unmappedFontNames == null) { 1205 unmappedFontNames = new ArrayList<>(); 1206 } 1207 unmappedFontNames.add(font); 1208 } 1209 } 1210 } 1211 1212 if (unmappedFontNames != null) { 1213 HashSet<String> unmappedFontFiles = new HashSet<>(); 1214 1215 /* Every font key in fontToFileMap ought to correspond to a 1216 * font key in fontToFamilyNameMap. Entries that don't seem 1217 * to correspond are likely fonts that were named differently 1218 * by GDI than in the registry. One known cause of this is when 1219 * Windows has had its regional settings changed so that from 1220 * GDI we get a localised (eg Chinese or Japanese) name for the 1221 * font, but the registry retains the English version of the name 1222 * that corresponded to the "install" locale for windows. 1223 * Since we are in this code block because there are unmapped 1224 * font names, we can look to find unused font->file mappings 1225 * and then open the files to read the names. We don't generally 1226 * want to open font files, as its a performance hit, but this 1227 * occurs only for a small number of fonts on specific system 1228 * configs - ie is believed that a "true" Japanese windows would 1229 * have JA names in the registry too. 1230 * Clone fontToFileMap and remove from the clone all keys which 1231 * match a fontToFamilyNameMap key. What remains maps to the 1232 * files we want to open to find the fonts GDI returned. 1233 * A font in such a file is added to the fontToFileMap after 1234 * checking its one of the unmappedFontNames we are looking for. 1235 * The original name that didn't map is removed from fontToFileMap 1236 * so essentially this "fixes up" fontToFileMap to use the same 1237 * name as GDI. 1238 * Also note that typically the fonts for which this occurs in 1239 * CJK locales are TTC fonts and not all fonts in a TTC may have 1240 * localised names. Eg MSGOTHIC.TTC contains 3 fonts and one of 1241 * them "MS UI Gothic" has no JA name whereas the other two do. 1242 * So not every font in these files is unmapped or new. 1243 */ 1244 @SuppressWarnings("unchecked") 1245 HashMap<String,String> ffmapCopy = 1246 (HashMap<String,String>)(fontToFileMap.clone()); 1247 for (String key : fontToFamilyNameMap.keySet()) { 1248 ffmapCopy.remove(key); 1249 } 1250 for (String key : ffmapCopy.keySet()) { 1251 unmappedFontFiles.add(ffmapCopy.get(key)); 1252 fontToFileMap.remove(key); 1253 } 1254 1255 resolveFontFiles(unmappedFontFiles, unmappedFontNames); 1256 1257 /* If there are still unmapped font names, this means there's 1258 * something that wasn't in the registry. We need to get all 1259 * the font files directly and look at the ones that weren't 1260 * found in the registry. 1261 */ 1262 if (unmappedFontNames.size() > 0) { 1263 1264 /* getFontFilesFromPath() returns all lower case names. 1265 * To compare we also need lower case 1266 * versions of the names from the registry. 1267 */ 1268 ArrayList<String> registryFiles = new ArrayList<>(); 1269 1270 for (String regFile : fontToFileMap.values()) { 1271 registryFiles.add(regFile.toLowerCase()); 1272 } 1273 /* We don't look for Type1 files here as windows will 1274 * not enumerate these, so aren't useful in reconciling 1275 * GDI's unmapped files. We do find these later when 1276 * we enumerate all fonts. 1277 */ 1278 for (String pathFile : getFontFilesFromPath(true)) { 1279 if (!registryFiles.contains(pathFile)) { 1280 unmappedFontFiles.add(pathFile); 1281 } 1282 } 1283 1284 resolveFontFiles(unmappedFontFiles, unmappedFontNames); 1285 } 1286 1287 /* remove from the set of names that will be returned to the 1288 * user any fonts that can't be mapped to files. 1289 */ 1290 if (unmappedFontNames.size() > 0) { 1291 int sz = unmappedFontNames.size(); 1292 for (int i=0; i<sz; i++) { 1293 String name = unmappedFontNames.get(i); 1294 String familyName = fontToFamilyNameMap.get(name); 1295 if (familyName != null) { 1296 ArrayList<String> family = familyToFontListMap.get(familyName); 1297 if (family != null) { 1298 if (family.size() <= 1) { 1299 familyToFontListMap.remove(familyName); 1300 } 1301 } 1302 } 1303 fontToFamilyNameMap.remove(name); 1304 if (FontUtilities.isLogging()) { 1305 FontUtilities.logInfo("No file for font:" + name); 1306 } 1307 } 1308 } 1309 } 1310 } 1311 1312 /** 1313 * In some cases windows may have fonts in the fonts folder that 1314 * don't show up in the registry or in the GDI calls to enumerate fonts. 1315 * The only way to find these is to list the directory. We invoke this 1316 * only in getAllFonts/Families, so most searches for a specific 1317 * font that is satisfied by the GDI/registry calls don't take the 1318 * additional hit of listing the directory. This hit is small enough 1319 * that its not significant in these 'enumerate all the fonts' cases. 1320 * The basic approach is to cross-reference the files windows found 1321 * with the ones in the directory listing approach, and for each 1322 * in the latter list that is missing from the former list, register it. 1323 */ 1324 private synchronized void checkForUnreferencedFontFiles() { 1325 if (haveCheckedUnreferencedFontFiles) { 1326 return; 1327 } 1328 haveCheckedUnreferencedFontFiles = true; 1329 if (!FontUtilities.isWindows) { 1330 return; 1331 } 1332 /* getFontFilesFromPath() returns all lower case names. 1333 * To compare we also need lower case 1334 * versions of the names from the registry. 1335 */ 1336 ArrayList<String> registryFiles = new ArrayList<>(); 1337 for (String regFile : fontToFileMap.values()) { 1338 registryFiles.add(regFile.toLowerCase()); 1339 } 1340 1341 /* To avoid any issues with concurrent modification, create 1342 * copies of the existing maps, add the new fonts into these 1343 * and then replace the references to the old ones with the 1344 * new maps. ConcurrentHashmap is another option but its a lot 1345 * more changes and with this exception, these maps are intended 1346 * to be static. 1347 */ 1348 HashMap<String,String> fontToFileMap2 = null; 1349 HashMap<String,String> fontToFamilyNameMap2 = null; 1350 HashMap<String,ArrayList<String>> familyToFontListMap2 = null;; 1351 1352 for (String pathFile : getFontFilesFromPath(false)) { 1353 if (!registryFiles.contains(pathFile)) { 1354 if (FontUtilities.isLogging()) { 1355 FontUtilities.logInfo("Found non-registry file : " + pathFile); 1356 } 1357 PhysicalFont f = registerFontFile(getPathName(pathFile)); 1358 if (f == null) { 1359 continue; 1360 } 1361 if (fontToFileMap2 == null) { 1362 fontToFileMap2 = new HashMap<>(fontToFileMap); 1363 fontToFamilyNameMap2 = new HashMap<>(fontToFamilyNameMap); 1364 familyToFontListMap2 = new HashMap<>(familyToFontListMap); 1365 } 1366 String fontName = f.getFontName(null); 1367 String family = f.getFamilyName(null); 1368 String familyLC = family.toLowerCase(); 1369 fontToFamilyNameMap2.put(fontName, family); 1370 fontToFileMap2.put(fontName, pathFile); 1371 ArrayList<String> fonts = familyToFontListMap2.get(familyLC); 1372 if (fonts == null) { 1373 fonts = new ArrayList<>(); 1374 } else { 1375 fonts = new ArrayList<>(fonts); 1376 } 1377 fonts.add(fontName); 1378 familyToFontListMap2.put(familyLC, fonts); 1379 } 1380 } 1381 if (fontToFileMap2 != null) { 1382 fontToFileMap = fontToFileMap2; 1383 familyToFontListMap = familyToFontListMap2; 1384 fontToFamilyNameMap = fontToFamilyNameMap2; 1385 } 1386 } 1387 1388 private void resolveFontFiles(HashSet<String> unmappedFiles, 1389 ArrayList<String> unmappedFonts) { 1390 1391 Locale l = SunToolkit.getStartupLocale(); 1392 1393 for (String file : unmappedFiles) { 1394 try { 1395 int fn = 0; 1396 TrueTypeFont ttf; 1397 String fullPath = getPathName(file); 1398 if (FontUtilities.isLogging()) { 1399 FontUtilities.logInfo("Trying to resolve file " + fullPath); 1400 } 1401 do { 1402 ttf = new TrueTypeFont(fullPath, null, fn++, false); 1403 // prefer the font's locale name. 1404 String fontName = ttf.getFontName(l).toLowerCase(); 1405 if (unmappedFonts.contains(fontName)) { 1406 fontToFileMap.put(fontName, file); 1407 unmappedFonts.remove(fontName); 1408 if (FontUtilities.isLogging()) { 1409 FontUtilities.logInfo("Resolved absent registry entry for " + 1410 fontName + " located in " + fullPath); 1411 } 1412 } 1413 } 1414 while (fn < ttf.getFontCount()); 1415 } catch (Exception e) { 1416 } 1417 } 1418 } 1419 1420 /* Hardwire the English names and expected file names of fonts 1421 * commonly used at start up. Avoiding until later even the small 1422 * cost of calling platform APIs to locate these can help. 1423 * The code that registers these fonts needs to "bail" if any 1424 * of the files do not exist, so it will verify the existence of 1425 * all non-null file names first. 1426 * They are added in to a map with nominally the first 1427 * word in the name of the family as the key. In all the cases 1428 * we are using the family name is a single word, and as is 1429 * more or less required the family name is the initial sequence 1430 * in a full name. So lookup first finds the matching description, 1431 * then registers the whole family, returning the right font. 1432 */ 1433 public static class FamilyDescription { 1434 public String familyName; 1435 public String plainFullName; 1436 public String boldFullName; 1437 public String italicFullName; 1438 public String boldItalicFullName; 1439 public String plainFileName; 1440 public String boldFileName; 1441 public String italicFileName; 1442 public String boldItalicFileName; 1443 boolean failed; 1444 } 1445 1446 static volatile HashMap<String, FamilyDescription> platformFontMap; 1447 1448 /** 1449 * default implementation does nothing. 1450 */ 1451 public HashMap<String, FamilyDescription> populateHardcodedFileNameMap() { 1452 return new HashMap<>(0); 1453 } 1454 1455 @SuppressWarnings("removal") 1456 Font2D findFontFromPlatformMap(String lcName, int style) { 1457 HashMap<String, FamilyDescription> platformFontMap = SunFontManager.platformFontMap; 1458 if (platformFontMap == null) { 1459 platformFontMap = populateHardcodedFileNameMap(); 1460 SunFontManager.platformFontMap = platformFontMap; 1461 } 1462 1463 if (platformFontMap == null || platformFontMap.size() == 0) { 1464 return null; 1465 } 1466 1467 int spaceIndex = lcName.indexOf(' '); 1468 String firstWord = lcName; 1469 if (spaceIndex > 0) { 1470 firstWord = lcName.substring(0, spaceIndex); 1471 } 1472 1473 FamilyDescription fd = platformFontMap.get(firstWord); 1474 if (fd == null || fd.failed) { 1475 return null; 1476 } 1477 /* Once we've established that its at least the first word, 1478 * we need to dig deeper to make sure its a match for either 1479 * a full name, or the family name, to make sure its not 1480 * a request for some other font that just happens to start 1481 * with the same first word. 1482 */ 1483 int styleIndex = -1; 1484 if (lcName.equalsIgnoreCase(fd.plainFullName)) { 1485 styleIndex = 0; 1486 } else if (lcName.equalsIgnoreCase(fd.boldFullName)) { 1487 styleIndex = 1; 1488 } else if (lcName.equalsIgnoreCase(fd.italicFullName)) { 1489 styleIndex = 2; 1490 } else if (lcName.equalsIgnoreCase(fd.boldItalicFullName)) { 1491 styleIndex = 3; 1492 } 1493 if (styleIndex == -1 && !lcName.equalsIgnoreCase(fd.familyName)) { 1494 return null; 1495 } 1496 1497 String plainFile = null, boldFile = null, 1498 italicFile = null, boldItalicFile = null; 1499 1500 boolean failure = false; 1501 /* In a terminal server config, its possible that getPathName() 1502 * will return null, if the file doesn't exist, hence the null 1503 * checks on return. But in the normal client config we need to 1504 * follow this up with a check to see if all the files really 1505 * exist for the non-null paths. 1506 */ 1507 getPlatformFontDirs(noType1Font); 1508 1509 if (fd.plainFileName != null) { 1510 plainFile = getPathName(fd.plainFileName); 1511 if (plainFile == null) { 1512 failure = true; 1513 } 1514 } 1515 1516 if (fd.boldFileName != null) { 1517 boldFile = getPathName(fd.boldFileName); 1518 if (boldFile == null) { 1519 failure = true; 1520 } 1521 } 1522 1523 if (fd.italicFileName != null) { 1524 italicFile = getPathName(fd.italicFileName); 1525 if (italicFile == null) { 1526 failure = true; 1527 } 1528 } 1529 1530 if (fd.boldItalicFileName != null) { 1531 boldItalicFile = getPathName(fd.boldItalicFileName); 1532 if (boldItalicFile == null) { 1533 failure = true; 1534 } 1535 } 1536 1537 if (failure) { 1538 if (FontUtilities.isLogging()) { 1539 FontUtilities.logInfo("Hardcoded file missing looking for " + lcName); 1540 } 1541 fd.failed = true; 1542 return null; 1543 } 1544 1545 /* Some of these may be null,as not all styles have to exist */ 1546 final String[] files = { 1547 plainFile, boldFile, italicFile, boldItalicFile } ; 1548 1549 failure = AccessController.doPrivileged(new PrivilegedAction<Boolean>() { 1550 public Boolean run() { 1551 for (int i=0; i<files.length; i++) { 1552 if (files[i] == null) { 1553 continue; 1554 } 1555 File f = new File(files[i]); 1556 if (!f.exists()) { 1557 return Boolean.TRUE; 1558 } 1559 } 1560 return Boolean.FALSE; 1561 } 1562 }); 1563 1564 if (failure) { 1565 if (FontUtilities.isLogging()) { 1566 FontUtilities.logInfo("Hardcoded file missing looking for " + lcName); 1567 } 1568 fd.failed = true; 1569 return null; 1570 } 1571 1572 /* If we reach here we know that we have all the files we 1573 * expect, so all should be fine so long as the contents 1574 * are what we'd expect. Now on to registering the fonts. 1575 * Currently this code only looks for TrueType fonts, so format 1576 * and rank can be specified without looking at the filename. 1577 */ 1578 Font2D font = null; 1579 for (int f=0;f<files.length;f++) { 1580 if (files[f] == null) { 1581 continue; 1582 } 1583 PhysicalFont pf = 1584 registerFontFile(files[f], null, 1585 FONTFORMAT_TRUETYPE, false, Font2D.TTF_RANK); 1586 if (f == styleIndex) { 1587 font = pf; 1588 } 1589 } 1590 1591 1592 /* Two general cases need a bit more work here. 1593 * 1) If font is null, then it was perhaps a request for a 1594 * non-existent font, such as "Tahoma Italic", or a family name - 1595 * where family and full name of the plain font differ. 1596 * Fall back to finding the closest one in the family. 1597 * This could still fail if a client specified "Segoe" instead of 1598 * "Segoe UI". 1599 * 2) The request is of the form "MyFont Bold", style=Font.ITALIC, 1600 * and so we want to see if there's a Bold Italic font, or 1601 * "MyFamily", style=Font.BOLD, and we may have matched the plain, 1602 * but now need to revise that to the BOLD font. 1603 */ 1604 FontFamily fontFamily = FontFamily.getFamily(fd.familyName); 1605 if (fontFamily != null) { 1606 if (font == null) { 1607 font = fontFamily.getFont(style); 1608 if (font == null) { 1609 font = fontFamily.getClosestStyle(style); 1610 } 1611 } else if (style > 0 && style != font.style) { 1612 style |= font.style; 1613 font = fontFamily.getFont(style); 1614 if (font == null) { 1615 font = fontFamily.getClosestStyle(style); 1616 } 1617 } 1618 } 1619 1620 return font; 1621 } 1622 private synchronized HashMap<String,String> getFullNameToFileMap() { 1623 if (fontToFileMap == null) { 1624 1625 pathDirs = getPlatformFontDirs(noType1Font); 1626 1627 fontToFileMap = new HashMap<>(100); 1628 fontToFamilyNameMap = new HashMap<>(100); 1629 familyToFontListMap = new HashMap<>(50); 1630 populateFontFileNameMap(fontToFileMap, 1631 fontToFamilyNameMap, 1632 familyToFontListMap, 1633 Locale.ENGLISH); 1634 if (FontUtilities.isWindows) { 1635 resolveWindowsFonts(); 1636 } 1637 if (FontUtilities.isLogging()) { 1638 logPlatformFontInfo(); 1639 } 1640 } 1641 return fontToFileMap; 1642 } 1643 1644 private void logPlatformFontInfo() { 1645 PlatformLogger logger = FontUtilities.getLogger(); 1646 for (int i=0; i< pathDirs.length;i++) { 1647 logger.info("fontdir="+pathDirs[i]); 1648 } 1649 for (String keyName : fontToFileMap.keySet()) { 1650 logger.info("font="+keyName+" file="+ fontToFileMap.get(keyName)); 1651 } 1652 for (String keyName : fontToFamilyNameMap.keySet()) { 1653 logger.info("font="+keyName+" family="+ 1654 fontToFamilyNameMap.get(keyName)); 1655 } 1656 for (String keyName : familyToFontListMap.keySet()) { 1657 logger.info("family="+keyName+ " fonts="+ 1658 familyToFontListMap.get(keyName)); 1659 } 1660 } 1661 1662 /* Note this return list excludes logical fonts and JRE fonts */ 1663 protected String[] getFontNamesFromPlatform() { 1664 if (getFullNameToFileMap().size() == 0) { 1665 return null; 1666 } 1667 checkForUnreferencedFontFiles(); 1668 /* This odd code with TreeMap is used to preserve a historical 1669 * behaviour wrt the sorting order .. */ 1670 ArrayList<String> fontNames = new ArrayList<>(); 1671 for (ArrayList<String> a : familyToFontListMap.values()) { 1672 for (String s : a) { 1673 fontNames.add(s); 1674 } 1675 } 1676 return fontNames.toArray(STR_ARRAY); 1677 } 1678 1679 public boolean gotFontsFromPlatform() { 1680 return getFullNameToFileMap().size() != 0; 1681 } 1682 1683 public String getFileNameForFontName(String fontName) { 1684 String fontNameLC = fontName.toLowerCase(Locale.ENGLISH); 1685 return fontToFileMap.get(fontNameLC); 1686 } 1687 1688 private PhysicalFont registerFontFile(String file) { 1689 if (new File(file).isAbsolute() && 1690 !registeredFonts.containsKey(file)) { 1691 int fontFormat = FONTFORMAT_NONE; 1692 int fontRank = Font2D.UNKNOWN_RANK; 1693 if (ttFilter.accept(null, file)) { 1694 fontFormat = FONTFORMAT_TRUETYPE; 1695 fontRank = Font2D.TTF_RANK; 1696 } else if 1697 (t1Filter.accept(null, file)) { 1698 fontFormat = FONTFORMAT_TYPE1; 1699 fontRank = Font2D.TYPE1_RANK; 1700 } 1701 if (fontFormat == FONTFORMAT_NONE) { 1702 return null; 1703 } 1704 return registerFontFile(file, null, fontFormat, false, fontRank); 1705 } 1706 return null; 1707 } 1708 1709 /* Used to register any font files that are found by platform APIs 1710 * that weren't previously found in the standard font locations. 1711 * the isAbsolute() check is needed since that's whats stored in the 1712 * set, and on windows, the fonts in the system font directory that 1713 * are in the fontToFileMap are just basenames. We don't want to try 1714 * to register those again, but we do want to register other registry 1715 * installed fonts. 1716 */ 1717 protected void registerOtherFontFiles(HashSet<String> registeredFontFiles) { 1718 if (getFullNameToFileMap().size() == 0) { 1719 return; 1720 } 1721 for (String file : fontToFileMap.values()) { 1722 registerFontFile(file); 1723 } 1724 } 1725 1726 public boolean 1727 getFamilyNamesFromPlatform(TreeMap<String,String> familyNames, 1728 Locale requestedLocale) { 1729 if (getFullNameToFileMap().size() == 0) { 1730 return false; 1731 } 1732 checkForUnreferencedFontFiles(); 1733 for (String name : fontToFamilyNameMap.values()) { 1734 familyNames.put(name.toLowerCase(requestedLocale), name); 1735 } 1736 return true; 1737 } 1738 1739 /* Path may be absolute or a base file name relative to one of 1740 * the platform font directories 1741 */ 1742 private String getPathName(final String s) { 1743 File f = new File(s); 1744 if (f.isAbsolute()) { 1745 return s; 1746 } else if (pathDirs.length==1) { 1747 return pathDirs[0] + File.separator + s; 1748 } else { 1749 @SuppressWarnings("removal") 1750 String path = AccessController.doPrivileged( 1751 new PrivilegedAction<String>() { 1752 public String run() { 1753 for (int p = 0; p < pathDirs.length; p++) { 1754 File f = new File(pathDirs[p] +File.separator+ s); 1755 if (f.exists()) { 1756 return f.getAbsolutePath(); 1757 } 1758 } 1759 return null; 1760 } 1761 }); 1762 if (path != null) { 1763 return path; 1764 } 1765 } 1766 return s; // shouldn't happen, but harmless 1767 } 1768 1769 /* lcName is required to be lower case for use as a key. 1770 * lcName may be a full name, or a family name, and style may 1771 * be specified in addition to either of these. So be sure to 1772 * get the right one. Since an app *could* ask for "Foo Regular" 1773 * and later ask for "Foo Italic", if we don't register all the 1774 * styles, then logic in findFont2D may try to style the original 1775 * so we register the entire family if we get a match here. 1776 * This is still a big win because this code is invoked where 1777 * otherwise we would register all fonts. 1778 * It's also useful for the case where "Foo Bold" was specified with 1779 * style Font.ITALIC, as we would want in that case to try to return 1780 * "Foo Bold Italic" if it exists, and it is only by locating "Foo Bold" 1781 * and opening it that we really "know" it's Bold, and can look for 1782 * a font that supports that and the italic style. 1783 * The code in here is not overtly windows-specific but in fact it 1784 * is unlikely to be useful as is on other platforms. It is maintained 1785 * in this shared source file to be close to its sole client and 1786 * because so much of the logic is intertwined with the logic in 1787 * findFont2D. 1788 */ 1789 private Font2D findFontFromPlatform(String lcName, int style) { 1790 if (getFullNameToFileMap().size() == 0) { 1791 return null; 1792 } 1793 1794 ArrayList<String> family = null; 1795 String fontFile = null; 1796 String familyName = fontToFamilyNameMap.get(lcName); 1797 if (familyName != null) { 1798 fontFile = fontToFileMap.get(lcName); 1799 family = familyToFontListMap.get 1800 (familyName.toLowerCase(Locale.ENGLISH)); 1801 } else { 1802 family = familyToFontListMap.get(lcName); // is lcName is a family? 1803 if (family != null && family.size() > 0) { 1804 String lcFontName = family.get(0).toLowerCase(Locale.ENGLISH); 1805 if (lcFontName != null) { 1806 familyName = fontToFamilyNameMap.get(lcFontName); 1807 } 1808 } 1809 } 1810 if (family == null || familyName == null) { 1811 return null; 1812 } 1813 String [] fontList = family.toArray(STR_ARRAY); 1814 if (fontList.length == 0) { 1815 return null; 1816 } 1817 1818 /* first check that for every font in this family we can find 1819 * a font file. The specific reason for doing this is that 1820 * in at least one case on Windows a font has the face name "David" 1821 * but the registry entry is "David Regular". That is the "unique" 1822 * name of the font but in other cases the registry contains the 1823 * "full" name. See the specifications of name ids 3 and 4 in the 1824 * TrueType 'name' table. 1825 * In general this could cause a problem that we fail to register 1826 * if we all members of a family that we may end up mapping to 1827 * the wrong font member: eg return Bold when Plain is needed. 1828 */ 1829 for (int f=0;f<fontList.length;f++) { 1830 String fontNameLC = fontList[f].toLowerCase(Locale.ENGLISH); 1831 String fileName = fontToFileMap.get(fontNameLC); 1832 if (fileName == null) { 1833 if (FontUtilities.isLogging()) { 1834 FontUtilities.logInfo("Platform lookup : No file for font " + 1835 fontList[f] + " in family " +familyName); 1836 } 1837 return null; 1838 } 1839 } 1840 1841 /* Currently this code only looks for TrueType fonts, so format 1842 * and rank can be specified without looking at the filename. 1843 */ 1844 PhysicalFont physicalFont = null; 1845 if (fontFile != null) { 1846 physicalFont = registerFontFile(getPathName(fontFile), null, 1847 FONTFORMAT_TRUETYPE, false, 1848 Font2D.TTF_RANK); 1849 } 1850 /* Register all fonts in this family. */ 1851 for (int f=0;f<fontList.length;f++) { 1852 String fontNameLC = fontList[f].toLowerCase(Locale.ENGLISH); 1853 String fileName = fontToFileMap.get(fontNameLC); 1854 if (fontFile != null && fontFile.equals(fileName)) { 1855 continue; 1856 } 1857 /* Currently this code only looks for TrueType fonts, so format 1858 * and rank can be specified without looking at the filename. 1859 */ 1860 registerFontFile(getPathName(fileName), null, 1861 FONTFORMAT_TRUETYPE, false, Font2D.TTF_RANK); 1862 } 1863 1864 Font2D font = null; 1865 FontFamily fontFamily = FontFamily.getFamily(familyName); 1866 /* Handle case where request "MyFont Bold", style=Font.ITALIC */ 1867 if (physicalFont != null) { 1868 style |= physicalFont.style; 1869 } 1870 if (fontFamily != null) { 1871 font = fontFamily.getFont(style); 1872 if (font == null) { 1873 font = fontFamily.getClosestStyle(style); 1874 } 1875 } 1876 return font; 1877 } 1878 1879 private ConcurrentHashMap<String, Font2D> fontNameCache = 1880 new ConcurrentHashMap<>(); 1881 1882 /* 1883 * The client supplies a name and a style. 1884 * The name could be a family name, or a full name. 1885 * A font may exist with the specified style, or it may 1886 * exist only in some other style. For non-native fonts the scaler 1887 * may be able to emulate the required style. 1888 */ 1889 public Font2D findFont2D(String name, int style, int fallback) { 1890 if (name == null) return null; 1891 String lowerCaseName = name.toLowerCase(Locale.ENGLISH); 1892 String mapName = lowerCaseName + dotStyleStr(style); 1893 1894 /* If preferLocaleFonts() or preferProportionalFonts() has been 1895 * called we may be using an alternate set of composite fonts in this 1896 * app context. The presence of a pre-built name map indicates whether 1897 * this is so, and gives access to the alternate composite for the 1898 * name. 1899 */ 1900 Font2D font = fontNameCache.get(mapName); 1901 if (font != null) { 1902 return font; 1903 } 1904 if (FontUtilities.isLogging()) { 1905 FontUtilities.logInfo("Search for font: " + name); 1906 } 1907 1908 // The check below is just so that the bitmap fonts being set by 1909 // AWT and Swing thru the desktop properties do not trigger the 1910 // the load fonts case. The two bitmap fonts are now mapped to 1911 // appropriate equivalents for serif and sansserif. 1912 // Note that the cost of this comparison is only for the first 1913 // call until the map is filled. 1914 if (FontUtilities.isWindows) { 1915 if (lowerCaseName.equals("ms sans serif")) { 1916 name = "sansserif"; 1917 } else if (lowerCaseName.equals("ms serif")) { 1918 name = "serif"; 1919 } 1920 } 1921 1922 /* This isn't intended to support a client passing in the 1923 * string default, but if a client passes in null for the name 1924 * the java.awt.Font class internally substitutes this name. 1925 * So we need to recognise it here to prevent a loadFonts 1926 * on the unrecognised name. The only potential problem with 1927 * this is it would hide any real font called "default"! 1928 * But that seems like a potential problem we can ignore for now. 1929 */ 1930 if (lowerCaseName.equals("default")) { 1931 name = "dialog"; 1932 } 1933 1934 /* First see if its a family name. */ 1935 FontFamily family = FontFamily.getFamily(name); 1936 if (family != null) { 1937 font = family.getFontWithExactStyleMatch(style); 1938 if (font == null) { 1939 font = findDeferredFont(name, style); 1940 } 1941 if (font == null) { 1942 font = findFontFromPlatform(lowerCaseName, style); 1943 } 1944 if (font == null) { 1945 font = family.getFont(style); 1946 } 1947 if (font == null) { 1948 font = family.getClosestStyle(style); 1949 } 1950 if (font != null) { 1951 fontNameCache.put(mapName, font); 1952 return font; 1953 } 1954 } 1955 1956 /* If it wasn't a family name, it should be a full name of 1957 * either a composite, or a physical font 1958 */ 1959 font = fullNameToFont.get(lowerCaseName); 1960 if (font != null) { 1961 /* Check that the requested style matches the matched font's style. 1962 * But also match style automatically if the requested style is 1963 * "plain". This because the existing behaviour is that the fonts 1964 * listed via getAllFonts etc always list their style as PLAIN. 1965 * This does lead to non-commutative behaviours where you might 1966 * start with "Lucida Sans Regular" and ask for a BOLD version 1967 * and get "Lucida Sans DemiBold" but if you ask for the PLAIN 1968 * style of "Lucida Sans DemiBold" you get "Lucida Sans DemiBold". 1969 * This consistent however with what happens if you have a bold 1970 * version of a font and no plain version exists - alg. styling 1971 * doesn't "unbolden" the font. 1972 */ 1973 if (font.style == style || style == Font.PLAIN) { 1974 fontNameCache.put(mapName, font); 1975 return font; 1976 } else { 1977 /* If it was a full name like "Lucida Sans Regular", but 1978 * the style requested is "bold", then we want to see if 1979 * there's the appropriate match against another font in 1980 * that family before trying to load all fonts, or applying a 1981 * algorithmic styling 1982 */ 1983 family = FontFamily.getFamily(font.getFamilyName(null)); 1984 if (family != null) { 1985 Font2D familyFont = family.getFont(style|font.style); 1986 /* We exactly matched the requested style, use it! */ 1987 if (familyFont != null) { 1988 fontNameCache.put(mapName, familyFont); 1989 return familyFont; 1990 } else { 1991 /* This next call is designed to support the case 1992 * where bold italic is requested, and if we must 1993 * style, then base it on either bold or italic - 1994 * not on plain! 1995 */ 1996 familyFont = family.getClosestStyle(style|font.style); 1997 if (familyFont != null) { 1998 /* The next check is perhaps one 1999 * that shouldn't be done. ie if we get this 2000 * far we have probably as close a match as we 2001 * are going to get. We could load all fonts to 2002 * see if somehow some parts of the family are 2003 * loaded but not all of it. 2004 */ 2005 if (familyFont.canDoStyle(style|font.style)) { 2006 fontNameCache.put(mapName, familyFont); 2007 return familyFont; 2008 } 2009 } 2010 } 2011 } 2012 } 2013 } 2014 2015 if (FontUtilities.isWindows) { 2016 2017 font = findFontFromPlatformMap(lowerCaseName, style); 2018 if (FontUtilities.isLogging()) { 2019 FontUtilities.logInfo("findFontFromPlatformMap returned " + font); 2020 } 2021 2022 if (font != null) { 2023 fontNameCache.put(mapName, font); 2024 return font; 2025 } 2026 /* Don't want Windows to return a font from C:\Windows\Fonts 2027 * if someone has installed a font with the same name 2028 * in the JRE. 2029 */ 2030 if (deferredFontFiles.size() > 0) { 2031 font = findJREDeferredFont(lowerCaseName, style); 2032 if (font != null) { 2033 fontNameCache.put(mapName, font); 2034 return font; 2035 } 2036 } 2037 font = findFontFromPlatform(lowerCaseName, style); 2038 if (font != null) { 2039 if (FontUtilities.isLogging()) { 2040 FontUtilities.logInfo("Found font via platform API for request:\"" + 2041 name + "\":, style="+style+ 2042 " found font: " + font); 2043 } 2044 fontNameCache.put(mapName, font); 2045 return font; 2046 } 2047 } 2048 2049 /* If reach here and no match has been located, then if there are 2050 * uninitialised deferred fonts, load as many of those as needed 2051 * to find the deferred font. If none is found through that 2052 * search continue on. 2053 * There is possibly a minor issue when more than one 2054 * deferred font implements the same font face. Since deferred 2055 * fonts are only those in font configuration files, this is a 2056 * controlled situation, the known case being Solaris euro_fonts 2057 * versions of Arial, Times New Roman, Courier New. However 2058 * the larger font will transparently replace the smaller one 2059 * - see addToFontList() - when it is needed by the composite font. 2060 */ 2061 if (deferredFontFiles.size() > 0) { 2062 font = findDeferredFont(name, style); 2063 if (font != null) { 2064 fontNameCache.put(mapName, font); 2065 return font; 2066 } 2067 } 2068 2069 /* We check for application registered fonts before 2070 * explicitly loading all fonts as if necessary the registration 2071 * code will have done so anyway. And we don't want to needlessly 2072 * load the actual files for all fonts. 2073 * Just as for installed fonts we check for family before fullname. 2074 * We do not add these fonts to fontNameCache for the 2075 * app context case which eliminates the overhead of a per context 2076 * cache for these. 2077 */ 2078 2079 if (fontsAreRegistered) { 2080 Hashtable<String, FontFamily> familyTable = createdByFamilyName; 2081 Hashtable<String, Font2D> nameTable = createdByFullName; 2082 2083 family = familyTable.get(lowerCaseName); 2084 if (family != null) { 2085 font = family.getFontWithExactStyleMatch(style); 2086 if (font == null) { 2087 font = family.getFont(style); 2088 } 2089 if (font == null) { 2090 font = family.getClosestStyle(style); 2091 } 2092 if (font != null) { 2093 if (fontsAreRegistered) { 2094 fontNameCache.put(mapName, font); 2095 } 2096 return font; 2097 } 2098 } 2099 font = nameTable.get(lowerCaseName); 2100 if (font != null) { 2101 if (fontsAreRegistered) { 2102 fontNameCache.put(mapName, font); 2103 } 2104 return font; 2105 } 2106 } 2107 2108 /* If reach here and no match has been located, then if all fonts 2109 * are not yet loaded, do so, and then recurse. 2110 */ 2111 if (!loadedAllFonts) { 2112 if (FontUtilities.isLogging()) { 2113 FontUtilities.logInfo("Load fonts looking for:" + name); 2114 } 2115 loadFonts(); 2116 loadedAllFonts = true; 2117 return findFont2D(name, style, fallback); 2118 } 2119 2120 if (!loadedAllFontFiles) { 2121 if (FontUtilities.isLogging()) { 2122 FontUtilities.logInfo("Load font files looking for:" + name); 2123 } 2124 loadFontFiles(); 2125 loadedAllFontFiles = true; 2126 return findFont2D(name, style, fallback); 2127 } 2128 2129 /* The primary name is the locale default - ie not US/English but 2130 * whatever is the default in this locale. This is the way it always 2131 * has been but may be surprising to some developers if "Arial Regular" 2132 * were hard-coded in their app and yet "Arial Regular" was not the 2133 * default name. Fortunately for them, as a consequence of the JDK 2134 * supporting returning names and family names for arbitrary locales, 2135 * we also need to support searching all localised names for a match. 2136 * But because this case of the name used to reference a font is not 2137 * the same as the default for this locale is rare, it makes sense to 2138 * search a much shorter list of default locale names and only go to 2139 * a longer list of names in the event that no match was found. 2140 * So add here code which searches localised names too. 2141 * As in 1.4.x this happens only after loading all fonts, which 2142 * is probably the right order. 2143 */ 2144 if ((font = findFont2DAllLocales(name, style)) != null) { 2145 fontNameCache.put(mapName, font); 2146 return font; 2147 } 2148 2149 /* Perhaps its a "compatibility" name - timesroman, helvetica, 2150 * or courier, which 1.0 apps used for logical fonts. 2151 * We look for these "late" after a loadFonts as we must not 2152 * hide real fonts of these names. 2153 * Map these appropriately: 2154 * On windows this means according to the rules specified by the 2155 * FontConfiguration : do it only for encoding==Cp1252 2156 * 2157 * REMIND: this is something we plan to remove. 2158 */ 2159 if (FontUtilities.isWindows) { 2160 String compatName = 2161 getFontConfiguration().getFallbackFamilyName(name, null); 2162 if (compatName != null) { 2163 font = findFont2D(compatName, style, fallback); 2164 fontNameCache.put(mapName, font); 2165 return font; 2166 } 2167 } else if (lowerCaseName.equals("timesroman")) { 2168 font = findFont2D("serif", style, fallback); 2169 fontNameCache.put(mapName, font); 2170 return font; 2171 } else if (lowerCaseName.equals("helvetica")) { 2172 font = findFont2D("sansserif", style, fallback); 2173 fontNameCache.put(mapName, font); 2174 return font; 2175 } else if (lowerCaseName.equals("courier")) { 2176 font = findFont2D("monospaced", style, fallback); 2177 fontNameCache.put(mapName, font); 2178 return font; 2179 } 2180 if (FontUtilities.isLogging()) { 2181 FontUtilities.logInfo("No font found for:" + name); 2182 } 2183 2184 switch (fallback) { 2185 case PHYSICAL_FALLBACK: return getDefaultPhysicalFont(); 2186 case LOGICAL_FALLBACK: return getDefaultLogicalFont(style); 2187 default: return null; 2188 } 2189 } 2190 2191 /* 2192 * Workaround for apps which are dependent on a font metrics bug 2193 * in JDK 1.1. This is an unsupported win32 private setting. 2194 * Left in for a customer - do not remove. 2195 */ 2196 public boolean usePlatformFontMetrics() { 2197 return usePlatformFontMetrics; 2198 } 2199 2200 public int getNumFonts() { 2201 return physicalFonts.size()+maxCompFont; 2202 } 2203 2204 private static boolean fontSupportsEncoding(Font font, String encoding) { 2205 return FontUtilities.getFont2D(font).supportsEncoding(encoding); 2206 } 2207 2208 protected abstract String getFontPath(boolean noType1Fonts); 2209 2210 Thread fileCloser = null; 2211 Vector<File> tmpFontFiles = null; 2212 2213 private int createdFontCount = 0; 2214 2215 @SuppressWarnings("removal") 2216 public Font2D[] createFont2D(File fontFile, int fontFormat, boolean all, 2217 boolean isCopy, CreatedFontTracker tracker) 2218 throws FontFormatException { 2219 2220 List<Font2D> fList = new ArrayList<>(); 2221 int cnt = 1; 2222 String fontFilePath = fontFile.getPath(); 2223 FileFont font2D = null; 2224 final File fFile = fontFile; 2225 final CreatedFontTracker _tracker = tracker; 2226 boolean weakRefs = false; 2227 int maxStrikes = 0; 2228 synchronized (this) { 2229 if (createdFontCount < maxSoftRefCnt) { 2230 createdFontCount++; 2231 } else { 2232 weakRefs = true; 2233 maxStrikes = 10; 2234 } 2235 } 2236 try { 2237 switch (fontFormat) { 2238 case Font.TRUETYPE_FONT: 2239 font2D = new TrueTypeFont(fontFilePath, null, 0, true); 2240 font2D.setUseWeakRefs(weakRefs, maxStrikes); 2241 fList.add(font2D); 2242 if (!all) { 2243 break; 2244 } 2245 cnt = ((TrueTypeFont)font2D).getFontCount(); 2246 int index = 1; 2247 while (index < cnt) { 2248 font2D = new TrueTypeFont(fontFilePath, null, index++, true); 2249 font2D.setUseWeakRefs(weakRefs, maxStrikes); 2250 fList.add(font2D); 2251 } 2252 break; 2253 case Font.TYPE1_FONT: 2254 font2D = new Type1Font(fontFilePath, null, isCopy); 2255 font2D.setUseWeakRefs(weakRefs, maxStrikes); 2256 fList.add(font2D); 2257 break; 2258 default: 2259 throw new FontFormatException("Unrecognised Font Format"); 2260 } 2261 } catch (FontFormatException e) { 2262 if (isCopy) { 2263 AccessController.doPrivileged(new PrivilegedAction<Void>() { 2264 public Void run() { 2265 if (_tracker != null) { 2266 _tracker.subBytes((int)fFile.length()); 2267 } 2268 fFile.delete(); 2269 return null; 2270 } 2271 }); 2272 } 2273 throw(e); 2274 } 2275 if (isCopy) { 2276 FileFont.setFileToRemove(fList, fontFile, cnt, tracker); 2277 synchronized (FontManager.class) { 2278 2279 if (tmpFontFiles == null) { 2280 tmpFontFiles = new Vector<File>(); 2281 } 2282 tmpFontFiles.add(fontFile); 2283 2284 if (fileCloser == null) { 2285 final Runnable fileCloserRunnable = new Runnable() { 2286 public void run() { 2287 AccessController.doPrivileged(new PrivilegedAction<Void>() { 2288 public Void run() { 2289 for (int i = 0;i < CHANNELPOOLSIZE; i++) { 2290 if (fontFileCache[i] != null) { 2291 try { 2292 fontFileCache[i].close(); 2293 } catch (Exception e) { 2294 } 2295 } 2296 } 2297 if (tmpFontFiles != null) { 2298 File[] files = new File[tmpFontFiles.size()]; 2299 files = tmpFontFiles.toArray(files); 2300 for (int f=0; f<files.length;f++) { 2301 try { 2302 files[f].delete(); 2303 } catch (Exception e) { 2304 } 2305 } 2306 } 2307 return null; 2308 } 2309 }); 2310 } 2311 }; 2312 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 2313 ThreadGroup rootTG = ThreadGroupUtils.getRootThreadGroup(); 2314 fileCloser = new Thread(rootTG, fileCloserRunnable, 2315 "FileCloser", 0, false); 2316 fileCloser.setContextClassLoader(null); 2317 Runtime.getRuntime().addShutdownHook(fileCloser); 2318 return null; 2319 }); 2320 } 2321 } 2322 } 2323 return fList.toArray(new Font2D[0]); 2324 } 2325 2326 /* remind: used in X11GraphicsEnvironment and called often enough 2327 * that we ought to obsolete this code 2328 */ 2329 public synchronized String getFullNameByFileName(String fileName) { 2330 PhysicalFont[] physFonts = getPhysicalFonts(); 2331 for (int i=0;i<physFonts.length;i++) { 2332 if (physFonts[i].platName.equals(fileName)) { 2333 return (physFonts[i].getFontName(null)); 2334 } 2335 } 2336 return null; 2337 } 2338 2339 /* 2340 * This is called when font is determined to be invalid/bad. 2341 * It designed to be called (for example) by the font scaler 2342 * when in processing a font file it is discovered to be incorrect. 2343 * This is different than the case where fonts are discovered to 2344 * be incorrect during initial verification, as such fonts are 2345 * never registered. 2346 * Handles to this font held are re-directed to a default font. 2347 * This default may not be an ideal substitute buts it better than 2348 * crashing This code assumes a PhysicalFont parameter as it doesn't 2349 * make sense for a Composite to be "bad". 2350 */ 2351 public synchronized void deRegisterBadFont(Font2D font2D) { 2352 if (!(font2D instanceof PhysicalFont)) { 2353 /* We should never reach here, but just in case */ 2354 return; 2355 } else { 2356 if (FontUtilities.isLogging()) { 2357 FontUtilities.logSevere("Deregister bad font: " + font2D); 2358 } 2359 replaceFont((PhysicalFont)font2D, getDefaultPhysicalFont()); 2360 } 2361 } 2362 2363 /* 2364 * This encapsulates all the work that needs to be done when a 2365 * Font2D is replaced by a different Font2D. 2366 */ 2367 public synchronized void replaceFont(PhysicalFont oldFont, 2368 PhysicalFont newFont) { 2369 2370 if (oldFont.handle.font2D != oldFont) { 2371 /* already done */ 2372 return; 2373 } 2374 2375 /* If we try to replace the font with itself, that won't work, 2376 * so pick any alternative physical font 2377 */ 2378 if (oldFont == newFont) { 2379 if (FontUtilities.isLogging()) { 2380 FontUtilities.logSevere("Can't replace bad font with itself " + oldFont); 2381 } 2382 PhysicalFont[] physFonts = getPhysicalFonts(); 2383 for (int i=0; i<physFonts.length;i++) { 2384 if (physFonts[i] != newFont) { 2385 newFont = physFonts[i]; 2386 break; 2387 } 2388 } 2389 if (oldFont == newFont) { 2390 if (FontUtilities.isLogging()) { 2391 FontUtilities.logSevere("This is bad. No good physicalFonts found."); 2392 } 2393 return; 2394 } 2395 } 2396 2397 /* eliminate references to this font, so it won't be located 2398 * by future callers, and will be eligible for GC when all 2399 * references are removed 2400 */ 2401 oldFont.handle.font2D = newFont; 2402 physicalFonts.remove(oldFont.fullName); 2403 fullNameToFont.remove(oldFont.fullName.toLowerCase(Locale.ENGLISH)); 2404 FontFamily.remove(oldFont); 2405 if (localeFullNamesToFont != null) { 2406 Map.Entry<?, ?>[] mapEntries = localeFullNamesToFont.entrySet(). 2407 toArray(new Map.Entry<?, ?>[0]); 2408 /* Should I be replacing these, or just I just remove 2409 * the names from the map? 2410 */ 2411 for (int i=0; i<mapEntries.length;i++) { 2412 if (mapEntries[i].getValue() == oldFont) { 2413 try { 2414 @SuppressWarnings("unchecked") 2415 Map.Entry<String, PhysicalFont> tmp = (Map.Entry<String, PhysicalFont>)mapEntries[i]; 2416 tmp.setValue(newFont); 2417 } catch (Exception e) { 2418 /* some maps don't support this operation. 2419 * In this case just give up and remove the entry. 2420 */ 2421 localeFullNamesToFont.remove(mapEntries[i].getKey()); 2422 } 2423 } 2424 } 2425 } 2426 2427 for (int i=0; i<maxCompFont; i++) { 2428 /* Deferred initialization of composites shouldn't be 2429 * a problem for this case, since a font must have been 2430 * initialised to be discovered to be bad. 2431 * Some JRE composites on Solaris use two versions of the same 2432 * font. The replaced font isn't bad, just "smaller" so there's 2433 * no need to make the slot point to the new font. 2434 * Since composites have a direct reference to the Font2D (not 2435 * via a handle) making this substitution is not safe and could 2436 * cause an additional problem and so this substitution is 2437 * warranted only when a font is truly "bad" and could cause 2438 * a crash. So we now replace it only if its being substituted 2439 * with some font other than a fontconfig rank font 2440 * Since in practice a substitution will have the same rank 2441 * this may never happen, but the code is safer even if its 2442 * also now a no-op. 2443 * The only obvious "glitch" from this stems from the current 2444 * implementation that when asked for the number of glyphs in a 2445 * composite it lies and returns the number in slot 0 because 2446 * composite glyphs aren't contiguous. Since we live with that 2447 * we can live with the glitch that depending on how it was 2448 * initialised a composite may return different values for this. 2449 * Fixing the issues with composite glyph ids is tricky as 2450 * there are exclusion ranges and unlike other fonts even the 2451 * true "numGlyphs" isn't a contiguous range. Likely the only 2452 * solution is an API that returns an array of glyph ranges 2453 * which takes precedence over the existing API. That might 2454 * also need to address excluding ranges which represent a 2455 * code point supported by an earlier component. 2456 */ 2457 if (newFont.getRank() > Font2D.FONT_CONFIG_RANK) { 2458 compFonts[i].replaceComponentFont(oldFont, newFont); 2459 } 2460 } 2461 } 2462 2463 private synchronized void loadLocaleNames() { 2464 if (localeFullNamesToFont != null) { 2465 return; 2466 } 2467 localeFullNamesToFont = new HashMap<>(); 2468 Font2D[] fonts = getRegisteredFonts(); 2469 for (int i=0; i<fonts.length; i++) { 2470 if (fonts[i] instanceof TrueTypeFont) { 2471 TrueTypeFont ttf = (TrueTypeFont)fonts[i]; 2472 String[] fullNames = ttf.getAllFullNames(); 2473 for (int n=0; n<fullNames.length; n++) { 2474 localeFullNamesToFont.put(fullNames[n], ttf); 2475 } 2476 FontFamily family = FontFamily.getFamily(ttf.familyName); 2477 if (family != null) { 2478 FontFamily.addLocaleNames(family, ttf.getAllFamilyNames()); 2479 } 2480 } 2481 } 2482 } 2483 2484 /* This replicate the core logic of findFont2D but operates on 2485 * all the locale names. This hasn't been merged into findFont2D to 2486 * keep the logic simpler and reduce overhead, since this case is 2487 * almost never used. The main case in which it is called is when 2488 * a bogus font name is used and we need to check all possible names 2489 * before returning the default case. 2490 */ 2491 private Font2D findFont2DAllLocales(String name, int style) { 2492 if (FontUtilities.isLogging()) { 2493 FontUtilities.logInfo("Searching localised font names for:" + name); 2494 } 2495 2496 /* If reach here and no match has been located, then if we have 2497 * not yet built the map of localeFullNamesToFont for TT fonts, do so 2498 * now. This method must be called after all fonts have been loaded. 2499 */ 2500 if (localeFullNamesToFont == null) { 2501 loadLocaleNames(); 2502 } 2503 String lowerCaseName = name.toLowerCase(); 2504 Font2D font = null; 2505 2506 /* First see if its a family name. */ 2507 FontFamily family = FontFamily.getLocaleFamily(lowerCaseName); 2508 if (family != null) { 2509 font = family.getFont(style); 2510 if (font == null) { 2511 font = family.getClosestStyle(style); 2512 } 2513 if (font != null) { 2514 return font; 2515 } 2516 } 2517 2518 /* If it wasn't a family name, it should be a full name. */ 2519 synchronized (this) { 2520 font = localeFullNamesToFont.get(name); 2521 } 2522 if (font != null) { 2523 if (font.style == style || style == Font.PLAIN) { 2524 return font; 2525 } else { 2526 family = FontFamily.getFamily(font.getFamilyName(null)); 2527 if (family != null) { 2528 Font2D familyFont = family.getFont(style); 2529 /* We exactly matched the requested style, use it! */ 2530 if (familyFont != null) { 2531 return familyFont; 2532 } else { 2533 familyFont = family.getClosestStyle(style); 2534 if (familyFont != null) { 2535 /* The next check is perhaps one 2536 * that shouldn't be done. ie if we get this 2537 * far we have probably as close a match as we 2538 * are going to get. We could load all fonts to 2539 * see if somehow some parts of the family are 2540 * loaded but not all of it. 2541 * This check is commented out for now. 2542 */ 2543 if (!familyFont.canDoStyle(style)) { 2544 familyFont = null; 2545 } 2546 return familyFont; 2547 } 2548 } 2549 } 2550 } 2551 } 2552 return font; 2553 } 2554 2555 /* Supporting "alternate" composite fonts on 2D graphics objects 2556 * is accessed by the application by calling methods on the local 2557 * GraphicsEnvironment. The overall implementation is described 2558 * in one place, here, since otherwise the implementation is spread 2559 * around it may be difficult to track. 2560 * The methods below call into SunGraphicsEnvironment which creates a 2561 * new FontConfiguration instance. The FontConfiguration class, 2562 * and its platform sub-classes are updated to take parameters requesting 2563 * these behaviours. This is then used to create new composite font 2564 * instances. Since this calls the initCompositeFont method in 2565 * SunGraphicsEnvironment it performs the same initialization as is 2566 * performed normally. There may be some duplication of effort, but 2567 * that code is already written to be able to perform properly if called 2568 * to duplicate work. The main difference is that if we detect we are 2569 * running in an applet/browser/Java plugin environment these new fonts 2570 * are not placed in the "default" maps but into an AppContext instance. 2571 * The font lookup mechanism in java.awt.Font.getFont2D() is also updated 2572 * so that look-up for composite fonts will in that case always 2573 * do a lookup rather than returning a cached result. 2574 * This is inefficient but necessary else singleton java.awt.Font 2575 * instances would not retrieve the correct Font2D for the appcontext. 2576 * sun.font.FontManager.findFont2D is also updated to that it uses 2577 * a name map cache specific to that appcontext. 2578 * 2579 * Getting an AppContext is expensive, so there is a global variable 2580 * that records whether these methods have ever been called and can 2581 * avoid the expense for almost all applications. Once the correct 2582 * CompositeFont is associated with the Font, everything should work 2583 * through existing mechanisms. 2584 * A special case is that GraphicsEnvironment.getAllFonts() must 2585 * return an AppContext specific list. 2586 * 2587 * Calling the methods below is "heavyweight" but it is expected that 2588 * these methods will be called very rarely. 2589 * 2590 * If _usingAlternateComposites is true, we are not in an "applet" 2591 * environment and the (single) application has selected 2592 * an alternate composite font behaviour. 2593 * 2594 * - Printing: The implementation delegates logical fonts to an AWT 2595 * mechanism which cannot use these alternate configurations. 2596 * We can detect that alternate fonts are in use and back-off to 2D, but 2597 * that uses outlines. Much of this can be fixed with additional work 2598 * but that may have to wait. The results should be correct, just not 2599 * optimal. 2600 */ 2601 private boolean _usingAlternateComposites = false; 2602 2603 private static boolean gAltJAFont = false; 2604 private boolean gLocalePref = false; 2605 private boolean gPropPref = false; 2606 2607 /* Its used by the FontMetrics caching code which in such 2608 * a case cannot retrieve a cached metrics solely on the basis of 2609 * the Font.equals() method since it needs to also check if the Font2D 2610 * is the same. 2611 * We also use non-standard composites for Swing native L&F fonts on 2612 * Windows. In that case the policy is that the metrics reported are 2613 * based solely on the physical font in the first slot which is the 2614 * visible java.awt.Font. So in that case the metrics cache which tests 2615 * the Font does what we want. In the near future when we expand the GTK 2616 * logical font definitions we may need to revisit this if GTK reports 2617 * combined metrics instead. For now though this test can be simple. 2618 */ 2619 public boolean usingAlternateCompositeFonts() { 2620 return _usingAlternateComposites; 2621 } 2622 2623 /* Modifies the behaviour of a subsequent call to preferLocaleFonts() 2624 * to use Mincho instead of Gothic for dialoginput in JA locales 2625 * on windows. Not needed on other platforms. 2626 */ 2627 public synchronized void useAlternateFontforJALocales() { 2628 if (FontUtilities.isLogging()) { 2629 FontUtilities.logInfo("Entered useAlternateFontforJALocales()."); 2630 } 2631 2632 if (!FontUtilities.isWindows) { 2633 return; 2634 } 2635 gAltJAFont = true; 2636 } 2637 2638 public boolean usingAlternateFontforJALocales() { 2639 return gAltJAFont; 2640 } 2641 2642 public synchronized void preferLocaleFonts() { 2643 if (FontUtilities.isLogging()) { 2644 FontUtilities.logInfo("Entered preferLocaleFonts()."); 2645 } 2646 2647 /* Test if re-ordering will have any effect */ 2648 if (!FontConfiguration.willReorderForStartupLocale()) { 2649 return; 2650 } 2651 if (gLocalePref == true) { 2652 return; 2653 } 2654 gLocalePref = true; 2655 createCompositeFonts(fontNameCache, gLocalePref, gPropPref); 2656 _usingAlternateComposites = true; 2657 } 2658 2659 public synchronized void preferProportionalFonts() { 2660 if (FontUtilities.isLogging()) { 2661 FontUtilities.logInfo("Entered preferProportionalFonts()."); 2662 } 2663 2664 /* If no proportional fonts are configured, there's no need 2665 * to take any action. 2666 */ 2667 if (!FontConfiguration.hasMonoToPropMap()) { 2668 return; 2669 } 2670 if (gPropPref == true) { 2671 return; 2672 } 2673 gPropPref = true; 2674 createCompositeFonts(fontNameCache, gLocalePref, gPropPref); 2675 _usingAlternateComposites = true; 2676 } 2677 2678 private static HashSet<String> installedNames = null; 2679 private static HashSet<String> getInstalledNames() { 2680 if (installedNames == null) { 2681 Locale l = getSystemStartupLocale(); 2682 SunFontManager fontManager = SunFontManager.getInstance(); 2683 String[] installedFamilies = 2684 fontManager.getInstalledFontFamilyNames(l); 2685 Font[] installedFonts = fontManager.getAllInstalledFonts(); 2686 HashSet<String> names = new HashSet<>(); 2687 for (int i=0; i<installedFamilies.length; i++) { 2688 names.add(installedFamilies[i].toLowerCase(l)); 2689 } 2690 for (int i=0; i<installedFonts.length; i++) { 2691 names.add(installedFonts[i].getFontName(l).toLowerCase(l)); 2692 } 2693 installedNames = names; 2694 } 2695 return installedNames; 2696 } 2697 2698 private static final Object regFamilyLock = new Object(); 2699 private Hashtable<String,FontFamily> createdByFamilyName; 2700 private Hashtable<String,Font2D> createdByFullName; 2701 private boolean fontsAreRegistered = false; 2702 2703 public boolean registerFont(Font font) { 2704 /* This method should not be called with "null". 2705 * It is the caller's responsibility to ensure that. 2706 */ 2707 if (font == null) { 2708 return false; 2709 } 2710 2711 /* Initialise these objects only once we start to use this API */ 2712 synchronized (regFamilyLock) { 2713 if (createdByFamilyName == null) { 2714 createdByFamilyName = new Hashtable<String,FontFamily>(); 2715 createdByFullName = new Hashtable<String,Font2D>(); 2716 } 2717 } 2718 2719 if (! FontAccess.getFontAccess().isCreatedFont(font)) { 2720 return false; 2721 } 2722 /* We want to ensure that this font cannot override existing 2723 * installed fonts. Check these conditions : 2724 * - family name is not that of an installed font 2725 * - full name is not that of an installed font 2726 * - family name is not the same as the full name of an installed font 2727 * - full name is not the same as the family name of an installed font 2728 * The last two of these may initially look odd but the reason is 2729 * that (unfortunately) Font constructors do not distinuguish these. 2730 * An extreme example of such a problem would be a font which has 2731 * family name "Dialog.Plain" and full name of "Dialog". 2732 * The one arguably overly stringent restriction here is that if an 2733 * application wants to supply a new member of an existing family 2734 * It will get rejected. But since the JRE can perform synthetic 2735 * styling in many cases its not necessary. 2736 * We don't apply the same logic to registered fonts. If apps want 2737 * to do this lets assume they have a reason. It won't cause problems 2738 * except for themselves. 2739 */ 2740 HashSet<String> names = getInstalledNames(); 2741 Locale l = getSystemStartupLocale(); 2742 String familyName = font.getFamily(l).toLowerCase(); 2743 String fullName = font.getFontName(l).toLowerCase(); 2744 if (names.contains(familyName) || names.contains(fullName)) { 2745 return false; 2746 } 2747 2748 /* Checks passed, now register the font */ 2749 Hashtable<String, FontFamily> familyTable = createdByFamilyName; 2750 Hashtable<String, Font2D> fullNameTable = createdByFullName; 2751 fontsAreRegistered = true; 2752 2753 /* Create the FontFamily and add font to the tables */ 2754 Font2D font2D = FontUtilities.getFont2D(font); 2755 int style = font2D.getStyle(); 2756 FontFamily family = familyTable.get(familyName); 2757 if (family == null) { 2758 family = new FontFamily(font.getFamily(l)); 2759 familyTable.put(familyName, family); 2760 } 2761 /* Remove name cache entries if not using app contexts. 2762 * To accommodate a case where code may have registered first a plain 2763 * family member and then used it and is now registering a bold family 2764 * member, we need to remove all members of the family, so that the 2765 * new style can get picked up rather than continuing to synthesise. 2766 */ 2767 if (fontsAreRegistered) { 2768 removeFromCache(family.getFont(Font.PLAIN)); 2769 removeFromCache(family.getFont(Font.BOLD)); 2770 removeFromCache(family.getFont(Font.ITALIC)); 2771 removeFromCache(family.getFont(Font.BOLD|Font.ITALIC)); 2772 removeFromCache(fullNameTable.get(fullName)); 2773 } 2774 family.setFont(font2D, style); 2775 fullNameTable.put(fullName, font2D); 2776 return true; 2777 } 2778 2779 /* Remove from the name cache all references to the Font2D */ 2780 private void removeFromCache(Font2D font) { 2781 if (font == null) { 2782 return; 2783 } 2784 String[] keys = fontNameCache.keySet().toArray(STR_ARRAY); 2785 for (int k=0; k<keys.length;k++) { 2786 if (fontNameCache.get(keys[k]) == font) { 2787 fontNameCache.remove(keys[k]); 2788 } 2789 } 2790 } 2791 2792 // It may look odd to use TreeMap but its more convenient to the caller. 2793 public TreeMap<String, String> getCreatedFontFamilyNames() { 2794 2795 Hashtable<String,FontFamily> familyTable; 2796 if (fontsAreRegistered) { 2797 familyTable = createdByFamilyName; 2798 } else { 2799 return null; 2800 } 2801 2802 Locale l = getSystemStartupLocale(); 2803 synchronized (familyTable) { 2804 TreeMap<String, String> map = new TreeMap<String, String>(); 2805 for (FontFamily f : familyTable.values()) { 2806 Font2D font2D = f.getFont(Font.PLAIN); 2807 if (font2D == null) { 2808 font2D = f.getClosestStyle(Font.PLAIN); 2809 } 2810 String name = font2D.getFamilyName(l); 2811 map.put(name.toLowerCase(l), name); 2812 } 2813 return map; 2814 } 2815 } 2816 2817 public Font[] getCreatedFonts() { 2818 2819 Hashtable<String,Font2D> nameTable; 2820 if (fontsAreRegistered) { 2821 nameTable = createdByFullName; 2822 } else { 2823 return null; 2824 } 2825 2826 Locale l = getSystemStartupLocale(); 2827 synchronized (nameTable) { 2828 Font[] fonts = new Font[nameTable.size()]; 2829 int i=0; 2830 for (Font2D font2D : nameTable.values()) { 2831 fonts[i++] = new Font(font2D.getFontName(l), Font.PLAIN, 1); 2832 } 2833 return fonts; 2834 } 2835 } 2836 2837 2838 protected String[] getPlatformFontDirs(boolean noType1Fonts) { 2839 2840 /* First check if we already initialised path dirs */ 2841 if (pathDirs != null) { 2842 return pathDirs; 2843 } 2844 2845 String path = getPlatformFontPath(noType1Fonts); 2846 StringTokenizer parser = 2847 new StringTokenizer(path, File.pathSeparator); 2848 ArrayList<String> pathList = new ArrayList<>(); 2849 try { 2850 while (parser.hasMoreTokens()) { 2851 pathList.add(parser.nextToken()); 2852 } 2853 } catch (NoSuchElementException e) { 2854 } 2855 pathDirs = pathList.toArray(new String[0]); 2856 return pathDirs; 2857 } 2858 2859 /** 2860 * Returns an array of two strings. The first element is the 2861 * name of the font. The second element is the file name. 2862 */ 2863 protected abstract String[] getDefaultPlatformFont(); 2864 2865 // Begin: Refactored from SunGraphicsEnviroment. 2866 2867 /* 2868 * helper function for registerFonts 2869 */ 2870 private void addDirFonts(String dirName, File dirFile, 2871 FilenameFilter filter, 2872 int fontFormat, boolean useJavaRasterizer, 2873 int fontRank, 2874 boolean defer, boolean resolveSymLinks) { 2875 String[] ls = dirFile.list(filter); 2876 if (ls == null || ls.length == 0) { 2877 return; 2878 } 2879 String[] fontNames = new String[ls.length]; 2880 String[][] nativeNames = new String[ls.length][]; 2881 int fontCount = 0; 2882 2883 for (int i=0; i < ls.length; i++ ) { 2884 File theFile = new File(dirFile, ls[i]); 2885 String fullName = null; 2886 if (resolveSymLinks) { 2887 try { 2888 fullName = theFile.getCanonicalPath(); 2889 } catch (IOException e) { 2890 } 2891 } 2892 if (fullName == null) { 2893 fullName = dirName + File.separator + ls[i]; 2894 } 2895 2896 // REMIND: case compare depends on platform 2897 if (registeredFontFiles.contains(fullName)) { 2898 continue; 2899 } 2900 2901 if (badFonts != null && badFonts.contains(fullName)) { 2902 if (FontUtilities.debugFonts()) { 2903 FontUtilities.logWarning("skip bad font " + fullName); 2904 } 2905 continue; // skip this font file. 2906 } 2907 2908 registeredFontFiles.add(fullName); 2909 2910 if (FontUtilities.debugFonts() 2911 && FontUtilities.getLogger().isLoggable(PlatformLogger.Level.INFO)) { 2912 String message = "Registering font " + fullName; 2913 String[] natNames = getNativeNames(fullName, null); 2914 if (natNames == null) { 2915 message += " with no native name"; 2916 } else { 2917 message += " with native name(s) " + natNames[0]; 2918 for (int nn = 1; nn < natNames.length; nn++) { 2919 message += ", " + natNames[nn]; 2920 } 2921 } 2922 FontUtilities.logInfo(message); 2923 } 2924 fontNames[fontCount] = fullName; 2925 nativeNames[fontCount++] = getNativeNames(fullName, null); 2926 } 2927 registerFonts(fontNames, nativeNames, fontCount, fontFormat, 2928 useJavaRasterizer, fontRank, defer); 2929 return; 2930 } 2931 2932 protected String[] getNativeNames(String fontFileName, 2933 String platformName) { 2934 return null; 2935 } 2936 2937 /** 2938 * Returns a file name for the physical font represented by this platform 2939 * font name. The default implementation tries to obtain the file name 2940 * from the font configuration. 2941 * Subclasses may override to provide information from other sources. 2942 */ 2943 protected String getFileNameFromPlatformName(String platformFontName) { 2944 return fontConfig.getFileNameFromPlatformName(platformFontName); 2945 } 2946 2947 /** 2948 * Return the default font configuration. 2949 */ 2950 public FontConfiguration getFontConfiguration() { 2951 return fontConfig; 2952 } 2953 2954 /* A call to this method should be followed by a call to 2955 * registerFontDirs(..) 2956 */ 2957 public String getPlatformFontPath(boolean noType1Font) { 2958 if (fontPath == null) { 2959 fontPath = getFontPath(noType1Font); 2960 } 2961 return fontPath; 2962 } 2963 2964 @SuppressWarnings("removal") 2965 protected void loadFonts() { 2966 if (discoveredAllFonts) { 2967 return; 2968 } 2969 /* Use lock specific to the font system */ 2970 synchronized (this) { 2971 if (FontUtilities.debugFonts()) { 2972 Thread.dumpStack(); 2973 FontUtilities.logInfo("SunGraphicsEnvironment.loadFonts() called"); 2974 } 2975 initialiseDeferredFonts(); 2976 2977 AccessController.doPrivileged(new PrivilegedAction<Void>() { 2978 public Void run() { 2979 if (fontPath == null) { 2980 fontPath = getPlatformFontPath(noType1Font); 2981 registerFontDirs(fontPath); 2982 } 2983 if (fontPath != null) { 2984 // this will find all fonts including those already 2985 // registered. But we have checks in place to prevent 2986 // double registration. 2987 if (! gotFontsFromPlatform()) { 2988 registerFontsOnPath(fontPath, false, 2989 Font2D.UNKNOWN_RANK, 2990 false, true); 2991 loadedAllFontFiles = true; 2992 } 2993 } 2994 registerOtherFontFiles(registeredFontFiles); 2995 discoveredAllFonts = true; 2996 return null; 2997 } 2998 }); 2999 } 3000 } 3001 3002 protected void registerFontDirs(String pathName) { 3003 return; 3004 } 3005 3006 private void registerFontsOnPath(String pathName, 3007 boolean useJavaRasterizer, int fontRank, 3008 boolean defer, boolean resolveSymLinks) { 3009 3010 StringTokenizer parser = new StringTokenizer(pathName, 3011 File.pathSeparator); 3012 try { 3013 while (parser.hasMoreTokens()) { 3014 registerFontsInDir(parser.nextToken(), 3015 useJavaRasterizer, fontRank, 3016 defer, resolveSymLinks); 3017 } 3018 } catch (NoSuchElementException e) { 3019 } 3020 } 3021 3022 /* Called to register fall back fonts */ 3023 public void registerFontsInDir(String dirName) { 3024 registerFontsInDir(dirName, true, Font2D.JRE_RANK, true, false); 3025 } 3026 3027 // MACOSX begin -- need to access this in subclass 3028 protected void registerFontsInDir(String dirName, boolean useJavaRasterizer, 3029 // MACOSX end 3030 int fontRank, 3031 boolean defer, boolean resolveSymLinks) { 3032 File pathFile = new File(dirName); 3033 addDirFonts(dirName, pathFile, ttFilter, 3034 FONTFORMAT_TRUETYPE, useJavaRasterizer, 3035 fontRank==Font2D.UNKNOWN_RANK ? 3036 Font2D.TTF_RANK : fontRank, 3037 defer, resolveSymLinks); 3038 addDirFonts(dirName, pathFile, t1Filter, 3039 FONTFORMAT_TYPE1, useJavaRasterizer, 3040 fontRank==Font2D.UNKNOWN_RANK ? 3041 Font2D.TYPE1_RANK : fontRank, 3042 defer, resolveSymLinks); 3043 } 3044 3045 protected void registerFontDir(String path) { 3046 } 3047 3048 /** 3049 * Returns file name for default font, either absolute 3050 * or relative as needed by registerFontFile. 3051 */ 3052 public synchronized String getDefaultFontFile() { 3053 return defaultFontFileName; 3054 } 3055 3056 /** 3057 * Whether registerFontFile expects absolute or relative 3058 * font file names. 3059 */ 3060 protected boolean useAbsoluteFontFileNames() { 3061 return true; 3062 } 3063 3064 /** 3065 * Creates this environment's FontConfiguration. 3066 */ 3067 protected abstract FontConfiguration createFontConfiguration(); 3068 3069 public abstract FontConfiguration 3070 createFontConfiguration(boolean preferLocaleFonts, 3071 boolean preferPropFonts); 3072 3073 /** 3074 * Returns face name for default font, or null if 3075 * no face names are used for CompositeFontDescriptors 3076 * for this platform. 3077 */ 3078 public synchronized String getDefaultFontFaceName() { 3079 return defaultFontName; 3080 } 3081 3082 @SuppressWarnings("removal") 3083 public void loadFontFiles() { 3084 loadFonts(); 3085 if (loadedAllFontFiles) { 3086 return; 3087 } 3088 /* Use lock specific to the font system */ 3089 synchronized (this) { 3090 if (FontUtilities.debugFonts()) { 3091 Thread.dumpStack(); 3092 FontUtilities.logInfo("loadAllFontFiles() called"); 3093 } 3094 AccessController.doPrivileged(new PrivilegedAction<Void>() { 3095 public Void run() { 3096 if (fontPath == null) { 3097 fontPath = getPlatformFontPath(noType1Font); 3098 } 3099 if (fontPath != null) { 3100 // this will find all fonts including those already 3101 // registered. But we have checks in place to prevent 3102 // double registration. 3103 registerFontsOnPath(fontPath, false, 3104 Font2D.UNKNOWN_RANK, 3105 false, true); 3106 } 3107 loadedAllFontFiles = true; 3108 return null; 3109 } 3110 }); 3111 } 3112 } 3113 3114 /* 3115 * This method asks the font configuration API for all platform names 3116 * used as components of composite/logical fonts and iterates over these 3117 * looking up their corresponding file name and registers these fonts. 3118 * It also ensures that the fonts are accessible via platform APIs. 3119 * The composites themselves are then registered. 3120 */ 3121 private void 3122 initCompositeFonts(FontConfiguration fontConfig, 3123 ConcurrentHashMap<String, Font2D> altNameCache) { 3124 if (FontUtilities.isLogging()) { 3125 FontUtilities.logInfo("Initialising composite fonts"); 3126 } 3127 3128 int numCoreFonts = fontConfig.getNumberCoreFonts(); 3129 String[] fcFonts = fontConfig.getPlatformFontNames(); 3130 for (int f=0; f<fcFonts.length; f++) { 3131 String platformFontName = fcFonts[f]; 3132 String fontFileName = 3133 getFileNameFromPlatformName(platformFontName); 3134 String[] nativeNames = null; 3135 if (fontFileName == null 3136 || fontFileName.equals(platformFontName)) { 3137 /* No file located, so register using the platform name, 3138 * i.e. as a native font. 3139 */ 3140 fontFileName = platformFontName; 3141 } else { 3142 if (f < numCoreFonts) { 3143 /* If platform APIs also need to access the font, add it 3144 * to a set to be registered with the platform too. 3145 * This may be used to add the parent directory to the X11 3146 * font path if its not already there. See the docs for the 3147 * subclass implementation. 3148 * This is now mainly for the benefit of X11-based AWT 3149 * But for historical reasons, 2D initialisation code 3150 * makes these calls. 3151 * If the fontconfiguration file is properly set up 3152 * so that all fonts are mapped to files and all their 3153 * appropriate directories are specified, then this 3154 * method will be low cost as it will return after 3155 * a test that finds a null lookup map. 3156 */ 3157 addFontToPlatformFontPath(platformFontName); 3158 } 3159 nativeNames = getNativeNames(fontFileName, platformFontName); 3160 } 3161 /* Uncomment these two lines to "generate" the XLFD->filename 3162 * mappings needed to speed start-up on Solaris. 3163 * Augment this with the appendedpathname and the mappings 3164 * for native (F3) fonts 3165 */ 3166 //String platName = platformFontName.replaceAll(" ", "_"); 3167 //System.out.println("filename."+platName+"="+fontFileName); 3168 registerFontFile(fontFileName, nativeNames, 3169 Font2D.FONT_CONFIG_RANK, true); 3170 3171 3172 } 3173 /* This registers accumulated paths from the calls to 3174 * addFontToPlatformFontPath(..) and any specified by 3175 * the font configuration. Rather than registering 3176 * the fonts it puts them in a place and form suitable for 3177 * the Toolkit to pick up and use if a toolkit is initialised, 3178 * and if it uses X11 fonts. 3179 */ 3180 registerPlatformFontsUsedByFontConfiguration(); 3181 3182 CompositeFontDescriptor[] compositeFontInfo 3183 = fontConfig.get2DCompositeFontInfo(); 3184 for (int i = 0; i < compositeFontInfo.length; i++) { 3185 CompositeFontDescriptor descriptor = compositeFontInfo[i]; 3186 String[] componentFileNames = descriptor.getComponentFileNames(); 3187 String[] componentFaceNames = descriptor.getComponentFaceNames(); 3188 3189 /* It would be better eventually to handle this in the 3190 * FontConfiguration code which should also remove duplicate slots 3191 */ 3192 if (missingFontFiles != null) { 3193 for (int ii=0; ii<componentFileNames.length; ii++) { 3194 if (missingFontFiles.contains(componentFileNames[ii])) { 3195 componentFileNames[ii] = getDefaultFontFile(); 3196 componentFaceNames[ii] = getDefaultFontFaceName(); 3197 } 3198 } 3199 } 3200 3201 /* FontConfiguration needs to convey how many fonts it has added 3202 * as fallback component fonts which should not affect metrics. 3203 * The core component count will be the number of metrics slots. 3204 * This does not preclude other mechanisms for adding 3205 * fall back component fonts to the composite. 3206 */ 3207 if (altNameCache != null) { 3208 SunFontManager.registerCompositeFont( 3209 descriptor.getFaceName(), 3210 componentFileNames, componentFaceNames, 3211 descriptor.getCoreComponentCount(), 3212 descriptor.getExclusionRanges(), 3213 descriptor.getExclusionRangeLimits(), 3214 true, 3215 altNameCache); 3216 } else { 3217 registerCompositeFont(descriptor.getFaceName(), 3218 componentFileNames, componentFaceNames, 3219 descriptor.getCoreComponentCount(), 3220 descriptor.getExclusionRanges(), 3221 descriptor.getExclusionRangeLimits(), 3222 true); 3223 } 3224 if (FontUtilities.debugFonts()) { 3225 FontUtilities.logInfo("registered " + descriptor.getFaceName()); 3226 } 3227 } 3228 } 3229 3230 /** 3231 * Notifies graphics environment that the logical font configuration 3232 * uses the given platform font name. The graphics environment may 3233 * use this for platform specific initialization. 3234 */ 3235 protected void addFontToPlatformFontPath(String platformFontName) { 3236 } 3237 3238 protected void registerFontFile(String fontFileName, String[] nativeNames, 3239 int fontRank, boolean defer) { 3240 // REMIND: case compare depends on platform 3241 if (registeredFontFiles.contains(fontFileName)) { 3242 return; 3243 } 3244 int fontFormat; 3245 if (ttFilter.accept(null, fontFileName)) { 3246 fontFormat = FONTFORMAT_TRUETYPE; 3247 } else if (t1Filter.accept(null, fontFileName)) { 3248 fontFormat = FONTFORMAT_TYPE1; 3249 } else { 3250 fontFormat = FONTFORMAT_NATIVE; 3251 } 3252 registeredFontFiles.add(fontFileName); 3253 if (defer) { 3254 registerDeferredFont(fontFileName, fontFileName, nativeNames, 3255 fontFormat, false, fontRank); 3256 } else { 3257 registerFontFile(fontFileName, nativeNames, fontFormat, false, 3258 fontRank); 3259 } 3260 } 3261 3262 protected void registerPlatformFontsUsedByFontConfiguration() { 3263 } 3264 3265 /* 3266 * A GE may verify whether a font file used in a fontconfiguration 3267 * exists. If it doesn't then either we may substitute the default 3268 * font, or perhaps elide it altogether from the composite font. 3269 * This makes some sense on windows where the font file is only 3270 * likely to be in one place. But on other OSes, eg Linux, the file 3271 * can move around depending. So there we probably don't want to assume 3272 * its missing and so won't add it to this list. 3273 * If this list - missingFontFiles - is non-null then the composite 3274 * font initialisation logic tests to see if a font file is in that 3275 * set. 3276 * Only one thread should be able to add to this set so we don't 3277 * synchronize. 3278 */ 3279 protected void addToMissingFontFileList(String fileName) { 3280 if (missingFontFiles == null) { 3281 missingFontFiles = new HashSet<>(); 3282 } 3283 missingFontFiles.add(fileName); 3284 } 3285 3286 /* 3287 * This is for use only within getAllFonts(). 3288 * Fonts listed in the fontconfig files for windows were all 3289 * on the "deferred" initialisation list. They were registered 3290 * either in the course of the application, or in the call to 3291 * loadFonts() within getAllFonts(). The fontconfig file specifies 3292 * the names of the fonts using the English names. If there's a 3293 * different name in the execution locale, then the platform will 3294 * report that, and we will construct the font with both names, and 3295 * thereby enumerate it twice. This happens for Japanese fonts listed 3296 * in the windows fontconfig, when run in the JA locale. The solution 3297 * is to rely (in this case) on the platform's font->file mapping to 3298 * determine that this name corresponds to a file we already registered. 3299 * This works because 3300 * - we know when we get here all deferred fonts are already initialised 3301 * - when we register a font file, we register all fonts in it. 3302 * - we know the fontconfig fonts are all in the windows registry 3303 */ 3304 private boolean isNameForRegisteredFile(String fontName) { 3305 String fileName = getFileNameForFontName(fontName); 3306 if (fileName == null) { 3307 return false; 3308 } 3309 return registeredFontFiles.contains(fileName); 3310 } 3311 3312 /* 3313 * This invocation is not in a privileged block because 3314 * all privileged operations (reading files and properties) 3315 * was conducted on the creation of the GE 3316 */ 3317 public void 3318 createCompositeFonts(ConcurrentHashMap<String, Font2D> altNameCache, 3319 boolean preferLocale, 3320 boolean preferProportional) { 3321 3322 FontConfiguration fontConfig = 3323 createFontConfiguration(preferLocale, preferProportional); 3324 initCompositeFonts(fontConfig, altNameCache); 3325 } 3326 3327 /** 3328 * Returns all fonts installed in this environment. 3329 */ 3330 public Font[] getAllInstalledFonts() { 3331 if (allFonts == null) { 3332 loadFonts(); 3333 TreeMap<String, Font2D> fontMapNames = new TreeMap<>(); 3334 /* warning: the number of composite fonts could change dynamically 3335 * if applications are allowed to create them. "allfonts" could 3336 * then be stale. 3337 */ 3338 Font2D[] allfonts = getRegisteredFonts(); 3339 for (int i=0; i < allfonts.length; i++) { 3340 if (!(allfonts[i] instanceof NativeFont)) { 3341 fontMapNames.put(allfonts[i].getFontName(null), 3342 allfonts[i]); 3343 } 3344 } 3345 3346 String[] platformNames = getFontNamesFromPlatform(); 3347 if (platformNames != null) { 3348 for (int i=0; i<platformNames.length; i++) { 3349 if (!isNameForRegisteredFile(platformNames[i])) { 3350 fontMapNames.put(platformNames[i], null); 3351 } 3352 } 3353 } 3354 3355 String[] fontNames = null; 3356 if (fontMapNames.size() > 0) { 3357 fontNames = new String[fontMapNames.size()]; 3358 Object [] keyNames = fontMapNames.keySet().toArray(); 3359 for (int i=0; i < keyNames.length; i++) { 3360 fontNames[i] = (String)keyNames[i]; 3361 } 3362 } 3363 Font[] fonts = new Font[fontNames.length]; 3364 for (int i=0; i < fontNames.length; i++) { 3365 fonts[i] = new Font(fontNames[i], Font.PLAIN, 1); 3366 Font2D f2d = fontMapNames.get(fontNames[i]); 3367 if (f2d != null) { 3368 FontAccess.getFontAccess().setFont2D(fonts[i], f2d.handle); 3369 } 3370 } 3371 allFonts = fonts; 3372 } 3373 3374 Font []copyFonts = new Font[allFonts.length]; 3375 System.arraycopy(allFonts, 0, copyFonts, 0, allFonts.length); 3376 return copyFonts; 3377 } 3378 3379 /** 3380 * Get a list of installed fonts in the requested {@link Locale}. 3381 * The list contains the fonts Family Names. 3382 * If Locale is null, the default locale is used. 3383 * 3384 * @param requestedLocale, if null the default locale is used. 3385 * @return list of installed fonts in the system. 3386 */ 3387 public String[] getInstalledFontFamilyNames(Locale requestedLocale) { 3388 if (requestedLocale == null) { 3389 requestedLocale = Locale.getDefault(); 3390 } 3391 if (allFamilies != null && lastDefaultLocale != null && 3392 requestedLocale.equals(lastDefaultLocale)) { 3393 String[] copyFamilies = new String[allFamilies.length]; 3394 System.arraycopy(allFamilies, 0, copyFamilies, 3395 0, allFamilies.length); 3396 return copyFamilies; 3397 } 3398 3399 TreeMap<String,String> familyNames = new TreeMap<String,String>(); 3400 // these names are always there and aren't localised 3401 String str; 3402 str = Font.SERIF; familyNames.put(str.toLowerCase(), str); 3403 str = Font.SANS_SERIF; familyNames.put(str.toLowerCase(), str); 3404 str = Font.MONOSPACED; familyNames.put(str.toLowerCase(), str); 3405 str = Font.DIALOG; familyNames.put(str.toLowerCase(), str); 3406 str = Font.DIALOG_INPUT; familyNames.put(str.toLowerCase(), str); 3407 3408 /* Platform APIs may be used to get the set of available family 3409 * names for the current default locale so long as it is the same 3410 * as the start-up system locale, rather than loading all fonts. 3411 */ 3412 if (requestedLocale.equals(getSystemStartupLocale()) && 3413 getFamilyNamesFromPlatform(familyNames, requestedLocale)) { 3414 /* Augment platform names with JRE font family names */ 3415 getJREFontFamilyNames(familyNames, requestedLocale); 3416 } else { 3417 loadFontFiles(); 3418 Font2D[] physicalfonts = getPhysicalFonts(); 3419 for (int i=0; i < physicalfonts.length; i++) { 3420 if (!(physicalfonts[i] instanceof NativeFont)) { 3421 String name = 3422 physicalfonts[i].getFamilyName(requestedLocale); 3423 familyNames.put(name.toLowerCase(requestedLocale), name); 3424 } 3425 } 3426 } 3427 3428 // Add any native font family names here 3429 addNativeFontFamilyNames(familyNames, requestedLocale); 3430 3431 String[] retval = new String[familyNames.size()]; 3432 Object [] keyNames = familyNames.keySet().toArray(); 3433 for (int i=0; i < keyNames.length; i++) { 3434 retval[i] = familyNames.get(keyNames[i]); 3435 } 3436 if (requestedLocale.equals(Locale.getDefault())) { 3437 lastDefaultLocale = requestedLocale; 3438 allFamilies = new String[retval.length]; 3439 System.arraycopy(retval, 0, allFamilies, 0, allFamilies.length); 3440 } 3441 return retval; 3442 } 3443 3444 // Provides an aperture to add native font family names to the map 3445 protected void addNativeFontFamilyNames(TreeMap<String, String> familyNames, Locale requestedLocale) { } 3446 3447 @SuppressWarnings("removal") 3448 public void register1dot0Fonts() { 3449 AccessController.doPrivileged(new PrivilegedAction<Void>() { 3450 public Void run() { 3451 String type1Dir = "/usr/openwin/lib/X11/fonts/Type1"; 3452 registerFontsInDir(type1Dir, true, Font2D.TYPE1_RANK, 3453 false, false); 3454 return null; 3455 } 3456 }); 3457 } 3458 3459 /* Really we need only the JRE fonts family names, but there's little 3460 * overhead in doing this the easy way by adding all the currently 3461 * known fonts. 3462 */ 3463 protected void getJREFontFamilyNames(TreeMap<String,String> familyNames, 3464 Locale requestedLocale) { 3465 registerDeferredJREFonts(jreFontDirName); 3466 Font2D[] physicalfonts = getPhysicalFonts(); 3467 for (int i=0; i < physicalfonts.length; i++) { 3468 if (!(physicalfonts[i] instanceof NativeFont)) { 3469 String name = 3470 physicalfonts[i].getFamilyName(requestedLocale); 3471 familyNames.put(name.toLowerCase(requestedLocale), name); 3472 } 3473 } 3474 } 3475 3476 /** 3477 * Default locale can be changed but we need to know the initial locale 3478 * as that is what is used by native code. Changing Java default locale 3479 * doesn't affect that. 3480 * Returns the locale in use when using native code to communicate 3481 * with platform APIs. On windows this is known as the "system" locale, 3482 * and it is usually the same as the platform locale, but not always, 3483 * so this method also checks an implementation property used only 3484 * on windows and uses that if set. 3485 */ 3486 private static Locale systemLocale = null; 3487 @SuppressWarnings("removal") 3488 private static Locale getSystemStartupLocale() { 3489 if (systemLocale == null) { 3490 systemLocale = AccessController.doPrivileged(new PrivilegedAction<Locale>() { 3491 public Locale run() { 3492 /* On windows the system locale may be different than the 3493 * user locale. This is an unsupported configuration, but 3494 * in that case we want to return a dummy locale that will 3495 * never cause a match in the usage of this API. This is 3496 * important because Windows documents that the family 3497 * names of fonts are enumerated using the language of 3498 * the system locale. BY returning a dummy locale in that 3499 * case we do not use the platform API which would not 3500 * return us the names we want. 3501 */ 3502 String fileEncoding = System.getProperty("file.encoding", ""); 3503 String sysEncoding = System.getProperty("sun.jnu.encoding"); 3504 if (sysEncoding != null && !sysEncoding.equals(fileEncoding)) { 3505 return Locale.ROOT; 3506 } 3507 3508 String language = System.getProperty("user.language", "en"); 3509 String country = System.getProperty("user.country",""); 3510 String variant = System.getProperty("user.variant",""); 3511 return new Locale(language, country, variant); 3512 } 3513 }); 3514 } 3515 return systemLocale; 3516 } 3517 3518 void addToPool(FileFont font) { 3519 3520 FileFont fontFileToClose = null; 3521 int freeSlot = -1; 3522 3523 synchronized (fontFileCache) { 3524 /* Avoid duplicate entries in the pool, and don't close() it, 3525 * since this method is called only from within open(). 3526 * Seeing a duplicate is most likely to happen if the thread 3527 * was interrupted during a read, forcing perhaps repeated 3528 * close and open calls and it eventually it ends up pointing 3529 * at the same slot. 3530 */ 3531 for (int i=0;i<CHANNELPOOLSIZE;i++) { 3532 if (fontFileCache[i] == font) { 3533 return; 3534 } 3535 if (fontFileCache[i] == null && freeSlot < 0) { 3536 freeSlot = i; 3537 } 3538 } 3539 if (freeSlot >= 0) { 3540 fontFileCache[freeSlot] = font; 3541 return; 3542 } else { 3543 /* replace with new font. */ 3544 fontFileToClose = fontFileCache[lastPoolIndex]; 3545 fontFileCache[lastPoolIndex] = font; 3546 /* lastPoolIndex is updated so that the least recently opened 3547 * file will be closed next. 3548 */ 3549 lastPoolIndex = (lastPoolIndex+1) % CHANNELPOOLSIZE; 3550 } 3551 } 3552 /* Need to close the font file outside of the synchronized block, 3553 * since its possible some other thread is in an open() call on 3554 * this font file, and could be holding its lock and the pool lock. 3555 * Releasing the pool lock allows that thread to continue, so it can 3556 * then release the lock on this font, allowing the close() call 3557 * below to proceed. 3558 * Also, calling close() is safe because any other thread using 3559 * the font we are closing() synchronizes all reading, so we 3560 * will not close the file while its in use. 3561 */ 3562 if (fontFileToClose != null) { 3563 fontFileToClose.close(); 3564 } 3565 } 3566 3567 protected FontUIResource getFontConfigFUIR(String family, int style, 3568 int size) 3569 { 3570 return new FontUIResource(family, style, size); 3571 } 3572 } 3573