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