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