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