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