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