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