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