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