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