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