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