1 /* 2 * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Sun designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Sun in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 22 * CA 95054 USA or visit www.sun.com if you need additional information or 23 * have any questions. 24 */ 25 26 package sun.java2d; 27 28 import java.awt.Color; 29 import java.awt.Font; 30 import java.awt.Graphics2D; 31 import java.awt.GraphicsConfiguration; 32 import java.awt.GraphicsDevice; 33 import java.awt.GraphicsEnvironment; 34 import java.awt.Insets; 35 import java.awt.Rectangle; 36 import java.awt.Toolkit; 37 import java.awt.font.TextAttribute; 38 import java.awt.image.BufferedImage; 39 import java.io.BufferedReader; 40 import java.io.File; 41 import java.io.FileInputStream; 42 import java.io.FilenameFilter; 43 import java.io.InputStreamReader; 44 import java.io.IOException; 45 import java.text.AttributedCharacterIterator; 46 import java.util.ArrayList; 47 import java.util.HashSet; 48 import java.util.Iterator; 49 import java.util.Locale; 50 import java.util.Map; 51 import java.util.NoSuchElementException; 52 import java.util.Set; 53 import java.util.StringTokenizer; 54 import java.util.TreeMap; 55 import java.util.Vector; 56 import java.util.concurrent.ConcurrentHashMap; 57 import java.util.logging.Level; 58 import java.util.logging.Logger; 59 import sun.awt.AppContext; 60 import sun.awt.DisplayChangedListener; 61 import sun.awt.FontConfiguration; 62 import sun.awt.SunDisplayChanger; 63 import sun.font.CompositeFontDescriptor; 64 import sun.font.Font2D; 65 import sun.font.FontManager; 66 import sun.font.NativeFont; 67 68 /** 69 * This is an implementation of a GraphicsEnvironment object for the 70 * default local GraphicsEnvironment. 71 * 72 * @see GraphicsDevice 73 * @see GraphicsConfiguration 74 */ 75 76 public abstract class SunGraphicsEnvironment extends GraphicsEnvironment 77 implements FontSupport, DisplayChangedListener { 78 79 public static boolean isLinux; 80 public static boolean isSolaris; 81 public static boolean isWindows; 82 public static boolean noType1Font; 83 private static Font defaultFont; 84 private static String defaultFontFileName; 85 private static String defaultFontName; 86 public static final String lucidaFontName = "Lucida Sans Regular"; 87 public static final String lucidaFileName = "LucidaSansRegular.ttf"; 88 public static boolean debugFonts = false; 89 protected static Logger logger = null; 90 private static ArrayList badFonts; 91 public static String jreLibDirName; 92 public static String jreFontDirName; 93 private static HashSet<String> missingFontFiles = null; 94 95 private FontConfiguration fontConfig; 96 97 /* fontPath is the location of all fonts on the system, excluding the 98 * JRE's own font directory but including any path specified using the 99 * sun.java2d.fontpath property. Together with that property, it is 100 * initialised by the getPlatformFontPath() method 101 * This call must be followed by a call to registerFontDirs(fontPath) 102 * once any extra debugging path has been appended. 103 */ 104 protected String fontPath; 105 106 /* discoveredAllFonts is set to true when all fonts on the font path are 107 * discovered. This usually also implies opening, validating and 108 * registering, but an implementation may be optimized to avold this. 109 * So see also "loadedAllFontFiles" 110 */ 111 private boolean discoveredAllFonts = false; 112 113 /* loadedAllFontFiles is set to true when all fonts on the font path are 114 * actually opened, validated and registered. This always implies 115 * discoveredAllFonts is true. 116 */ 117 private boolean loadedAllFontFiles = false; 118 119 protected HashSet registeredFontFiles = new HashSet(); 120 public static String eudcFontFileName; /* Initialised only on windows */ 121 122 private static boolean isOpenJDK; 123 /** 124 * A few things in Java 2D code are different in OpenJDK, 125 * so need a way to tell which implementation this is. 126 * The absence of Lucida Sans Regular is the simplest way for now. 127 */ 128 public static boolean isOpenJDK() { 129 return isOpenJDK; 130 } 131 132 static { 133 java.security.AccessController.doPrivileged( 134 new java.security.PrivilegedAction() { 135 public Object run() { 136 137 jreLibDirName = System.getProperty("java.home","") + 138 File.separator + "lib"; 139 jreFontDirName = jreLibDirName + File.separator + "fonts"; 140 File lucidaFile = 141 new File(jreFontDirName + File.separator + lucidaFileName); 142 isOpenJDK = !lucidaFile.exists(); 143 144 String debugLevel = 145 System.getProperty("sun.java2d.debugfonts"); 146 147 if (debugLevel != null && !debugLevel.equals("false")) { 148 debugFonts = true; 149 logger = Logger.getLogger("sun.java2d"); 150 if (debugLevel.equals("warning")) { 151 logger.setLevel(Level.WARNING); 152 } else if (debugLevel.equals("severe")) { 153 logger.setLevel(Level.SEVERE); 154 } 155 } 156 return null; 157 } 158 }); 159 }; 160 161 public SunGraphicsEnvironment() { 162 java.security.AccessController.doPrivileged( 163 new java.security.PrivilegedAction() { 164 public Object run() { 165 String osName = System.getProperty("os.name"); 166 if ("Linux".equals(osName)) { 167 isLinux = true; 168 } else if ("SunOS".equals(osName)) { 169 isSolaris = true; 170 } else if ("Windows".equals(osName)) { 171 isWindows = true; 172 } 173 174 noType1Font = "true". 175 equals(System.getProperty("sun.java2d.noType1Font")); 176 177 if (isOpenJDK()) { 178 String[] fontInfo = FontManager.getDefaultPlatformFont(); 179 defaultFontName = fontInfo[0]; 180 defaultFontFileName = fontInfo[1]; 181 } else { 182 defaultFontName = lucidaFontName; 183 if (useAbsoluteFontFileNames()) { 184 defaultFontFileName = 185 jreFontDirName + File.separator + lucidaFileName; 186 } else { 187 defaultFontFileName = lucidaFileName; 188 } 189 } 190 191 File badFontFile = 192 new File(jreFontDirName + File.separator + "badfonts.txt"); 193 if (badFontFile.exists()) { 194 FileInputStream fis = null; 195 try { 196 badFonts = new ArrayList(); 197 fis = new FileInputStream(badFontFile); 198 InputStreamReader isr = new InputStreamReader(fis); 199 BufferedReader br = new BufferedReader(isr); 200 while (true) { 201 String name = br.readLine(); 202 if (name == null) { 203 break; 204 } else { 205 if (debugFonts) { 206 logger.warning("read bad font: " + name); 207 } 208 badFonts.add(name); 209 } 210 } 211 } catch (IOException e) { 212 try { 213 if (fis != null) { 214 fis.close(); 215 } 216 } catch (IOException ioe) { 217 } 218 } 219 } 220 221 /* Here we get the fonts in jre/lib/fonts and register them 222 * so they are always available and preferred over other fonts. 223 * This needs to be registered before the composite fonts as 224 * otherwise some native font that corresponds may be found 225 * as we don't have a way to handle two fonts of the same 226 * name, so the JRE one must be the first one registered. 227 * Pass "true" to registerFonts method as on-screen these 228 * JRE fonts always go through the T2K rasteriser. 229 */ 230 if (isLinux) { 231 /* Linux font configuration uses these fonts */ 232 registerFontDir(jreFontDirName); 233 } 234 registerFontsInDir(jreFontDirName, true, Font2D.JRE_RANK, 235 true, false); 236 237 /* Register the JRE fonts so that the native platform can 238 * access them. This is used only on Windows so that when 239 * printing the printer driver can access the fonts. 240 */ 241 registerJREFontsWithPlatform(jreFontDirName); 242 243 /* Create the font configuration and get any font path 244 * that might be specified. 245 */ 246 fontConfig = createFontConfiguration(); 247 getPlatformFontPathFromFontConfig(); 248 249 String extraFontPath = fontConfig.getExtraFontPath(); 250 251 /* In prior releases the debugging font path replaced 252 * all normally located font directories except for the 253 * JRE fonts dir. This directory is still always located and 254 * placed at the head of the path but as an augmentation 255 * to the previous behaviour the 256 * changes below allow you to additionally append to 257 * the font path by starting with append: or prepend by 258 * starting with a prepend: sign. Eg: to append 259 * -Dsun.java2d.fontpath=append:/usr/local/myfonts 260 * and to prepend 261 * -Dsun.java2d.fontpath=prepend:/usr/local/myfonts Disp 262 * 263 * If there is an appendedfontpath it in the font configuration 264 * it is used instead of searching the system for dirs. 265 * The behaviour of append and prepend is then similar 266 * to the normal case. ie it goes after what 267 * you prepend and * before what you append. If the 268 * sun.java2d.fontpath property is used, but it 269 * neither the append or prepend syntaxes is used then as 270 * except for the JRE dir the path is replaced and it is 271 * up to you to make sure that all the right directories 272 * are located. This is platform and locale-specific so 273 * its almost impossible to get right, so it should be 274 * used with caution. 275 */ 276 boolean prependToPath = false; 277 boolean appendToPath = false; 278 String dbgFontPath = System.getProperty("sun.java2d.fontpath"); 279 280 if (dbgFontPath != null) { 281 if (dbgFontPath.startsWith("prepend:")) { 282 prependToPath = true; 283 dbgFontPath = 284 dbgFontPath.substring("prepend:".length()); 285 } else if (dbgFontPath.startsWith("append:")) { 286 appendToPath = true; 287 dbgFontPath = 288 dbgFontPath.substring("append:".length()); 289 } 290 } 291 292 if (debugFonts) { 293 logger.info("JRE font directory: " + jreFontDirName); 294 logger.info("Extra font path: " + extraFontPath); 295 logger.info("Debug font path: " + dbgFontPath); 296 } 297 298 if (dbgFontPath != null) { 299 /* In debugging mode we register all the paths 300 * Caution: this is a very expensive call on Solaris:- 301 */ 302 fontPath = getPlatformFontPath(noType1Font); 303 304 if (extraFontPath != null) { 305 fontPath = 306 extraFontPath + File.pathSeparator + fontPath; 307 } 308 if (appendToPath) { 309 fontPath = fontPath + File.pathSeparator + dbgFontPath; 310 } else if (prependToPath) { 311 fontPath = dbgFontPath + File.pathSeparator + fontPath; 312 } else { 313 fontPath = dbgFontPath; 314 } 315 registerFontDirs(fontPath); 316 } else if (extraFontPath != null) { 317 /* If the font configuration contains an "appendedfontpath" 318 * entry, it is interpreted as a set of locations that 319 * should always be registered. 320 * It may be additional to locations normally found for 321 * that place, or it may be locations that need to have 322 * all their paths registered to locate all the needed 323 * platform names. 324 * This is typically when the same .TTF file is referenced 325 * from multiple font.dir files and all of these must be 326 * read to find all the native (XLFD) names for the font, 327 * so that X11 font APIs can be used for as many code 328 * points as possible. 329 */ 330 registerFontDirs(extraFontPath); 331 } 332 333 /* On Solaris, we need to register the Japanese TrueType 334 * directory so that we can find the corresponding bitmap 335 * fonts. This could be done by listing the directory in 336 * the font configuration file, but we don't want to 337 * confuse users with this quirk. There are no bitmap fonts 338 * for other writing systems that correspond to TrueType 339 * fonts and have matching XLFDs. We need to register the 340 * bitmap fonts only in environments where they're on the 341 * X font path, i.e., in the Japanese locale. 342 * Note that if the X Toolkit is in use the font path isn't 343 * set up by JDK, but users of a JA locale should have it 344 * set up already by their login environment. 345 */ 346 if (isSolaris && Locale.JAPAN.equals(Locale.getDefault())) { 347 registerFontDir("/usr/openwin/lib/locale/ja/X11/fonts/TT"); 348 } 349 350 initCompositeFonts(fontConfig, null); 351 352 /* Establish the default font to be used by SG2D etc */ 353 defaultFont = new Font(Font.DIALOG, Font.PLAIN, 12); 354 355 return null; 356 } 357 }); 358 } 359 360 protected GraphicsDevice[] screens; 361 362 /** 363 * Returns an array of all of the screen devices. 364 */ 365 public synchronized GraphicsDevice[] getScreenDevices() { 366 GraphicsDevice[] ret = screens; 367 if (ret == null) { 368 int num = getNumScreens(); 369 ret = new GraphicsDevice[num]; 370 for (int i = 0; i < num; i++) { 371 ret[i] = makeScreenDevice(i); 372 } 373 screens = ret; 374 } 375 return ret; 376 } 377 378 protected abstract int getNumScreens(); 379 protected abstract GraphicsDevice makeScreenDevice(int screennum); 380 381 /** 382 * Returns the default screen graphics device. 383 */ 384 public GraphicsDevice getDefaultScreenDevice() { 385 return getScreenDevices()[0]; 386 } 387 388 /** 389 * Returns a Graphics2D object for rendering into the 390 * given BufferedImage. 391 * @throws NullPointerException if BufferedImage argument is null 392 */ 393 public Graphics2D createGraphics(BufferedImage img) { 394 if (img == null) { 395 throw new NullPointerException("BufferedImage cannot be null"); 396 } 397 SurfaceData sd = SurfaceData.getPrimarySurfaceData(img); 398 return new SunGraphics2D(sd, Color.white, Color.black, defaultFont); 399 } 400 401 /* A call to this method should be followed by a call to 402 * registerFontDirs(..) 403 */ 404 protected String getPlatformFontPath(boolean noType1Font) { 405 if (fontPath == null) { 406 fontPath = FontManager.getFontPath(noType1Font); 407 } 408 return fontPath; 409 } 410 411 private String[] platformFontDirs; 412 /** 413 * Get all directories which contain installed fonts. 414 */ 415 public String[] getPlatformFontDirs() { 416 if (platformFontDirs == null) { 417 String path = getPlatformFontPath(noType1Font); 418 StringTokenizer parser = 419 new StringTokenizer(path, File.pathSeparator);; 420 ArrayList<String> pathList = new ArrayList<String>(); 421 try { 422 while (parser.hasMoreTokens()) { 423 pathList.add(parser.nextToken()); 424 } 425 } catch (NoSuchElementException e) { 426 } 427 platformFontDirs = pathList.toArray(new String[0]); 428 } 429 return platformFontDirs; 430 } 431 432 /** 433 * Whether registerFontFile expects absolute or relative 434 * font file names. 435 */ 436 protected boolean useAbsoluteFontFileNames() { 437 return true; 438 } 439 440 /** 441 * Returns file name for default font, either absolute 442 * or relative as needed by registerFontFile. 443 */ 444 public String getDefaultFontFile() { 445 return defaultFontFileName; 446 } 447 448 /** 449 * Returns face name for default font, or null if 450 * no face names are used for CompositeFontDescriptors 451 * for this platform. 452 */ 453 public String getDefaultFontFaceName() { 454 return defaultFontName; 455 } 456 457 public void loadFonts() { 458 if (discoveredAllFonts) { 459 return; 460 } 461 /* Use lock specific to the font system */ 462 synchronized (FontManager.class) { 463 if (debugFonts) { 464 Thread.dumpStack(); 465 logger.info("SunGraphicsEnvironment.loadFonts() called"); 466 } 467 FontManager.initialiseDeferredFonts(); 468 469 java.security.AccessController.doPrivileged( 470 new java.security.PrivilegedAction() { 471 public Object run() { 472 if (fontPath == null) { 473 fontPath = getPlatformFontPath(noType1Font); 474 registerFontDirs(fontPath); 475 } 476 if (fontPath != null) { 477 // this will find all fonts including those already 478 // registered. But we have checks in place to prevent 479 // double registration. 480 if (!FontManager.gotFontsFromPlatform()) { 481 registerFontsOnPath(fontPath, false, 482 Font2D.UNKNOWN_RANK, 483 false, true); 484 loadedAllFontFiles = true; 485 } 486 } 487 FontManager.registerOtherFontFiles(registeredFontFiles); 488 discoveredAllFonts = true; 489 return null; 490 } 491 }); 492 } 493 } 494 495 496 public void loadFontFiles() { 497 loadFonts(); 498 if (loadedAllFontFiles) { 499 return; 500 } 501 /* Use lock specific to the font system */ 502 synchronized (FontManager.class) { 503 if (debugFonts) { 504 Thread.dumpStack(); 505 logger.info("loadAllFontFiles() called"); 506 } 507 java.security.AccessController.doPrivileged( 508 new java.security.PrivilegedAction() { 509 public Object run() { 510 if (fontPath == null) { 511 fontPath = getPlatformFontPath(noType1Font); 512 } 513 if (fontPath != null) { 514 // this will find all fonts including those already 515 // registered. But we have checks in place to prevent 516 // double registration. 517 registerFontsOnPath(fontPath, false, 518 Font2D.UNKNOWN_RANK, 519 false, true); 520 } 521 loadedAllFontFiles = true; 522 return null; 523 } 524 }); 525 } 526 } 527 528 /* 529 * This is for use only within getAllFonts(). 530 * Fonts listed in the fontconfig files for windows were all 531 * on the "deferred" initialisation list. They were registered 532 * either in the course of the application, or in the call to 533 * loadFonts() within getAllFonts(). The fontconfig file specifies 534 * the names of the fonts using the English names. If there's a 535 * different name in the execution locale, then the platform will 536 * report that, and we will construct the font with both names, and 537 * thereby enumerate it twice. This happens for Japanese fonts listed 538 * in the windows fontconfig, when run in the JA locale. The solution 539 * is to rely (in this case) on the platform's font->file mapping to 540 * determine that this name corresponds to a file we already registered. 541 * This works because 542 * - we know when we get here all deferred fonts are already initialised 543 * - when we register a font file, we register all fonts in it. 544 * - we know the fontconfig fonts are all in the windows registry 545 */ 546 private boolean isNameForRegisteredFile(String fontName) { 547 String fileName = FontManager.getFileNameForFontName(fontName); 548 if (fileName == null) { 549 return false; 550 } 551 return registeredFontFiles.contains(fileName); 552 } 553 554 private Font[] allFonts; 555 556 /** 557 * Returns all fonts installed in this environment. 558 */ 559 public Font[] getAllInstalledFonts() { 560 if (allFonts == null) { 561 loadFonts(); 562 TreeMap fontMapNames = new TreeMap(); 563 /* warning: the number of composite fonts could change dynamically 564 * if applications are allowed to create them. "allfonts" could 565 * then be stale. 566 */ 567 568 Font2D[] allfonts = FontManager.getRegisteredFonts(); 569 for (int i=0; i < allfonts.length; i++) { 570 if (!(allfonts[i] instanceof NativeFont)) { 571 fontMapNames.put(allfonts[i].getFontName(null), 572 allfonts[i]); 573 } 574 } 575 576 String[] platformNames = FontManager.getFontNamesFromPlatform(); 577 if (platformNames != null) { 578 for (int i=0; i<platformNames.length; i++) { 579 if (!isNameForRegisteredFile(platformNames[i])) { 580 fontMapNames.put(platformNames[i], null); 581 } 582 } 583 } 584 585 String[] fontNames = null; 586 if (fontMapNames.size() > 0) { 587 fontNames = new String[fontMapNames.size()]; 588 Object [] keyNames = fontMapNames.keySet().toArray(); 589 for (int i=0; i < keyNames.length; i++) { 590 fontNames[i] = (String)keyNames[i]; 591 } 592 } 593 Font[] fonts = new Font[fontNames.length]; 594 for (int i=0; i < fontNames.length; i++) { 595 fonts[i] = new Font(fontNames[i], Font.PLAIN, 1); 596 Font2D f2d = (Font2D)fontMapNames.get(fontNames[i]); 597 if (f2d != null) { 598 FontManager.setFont2D(fonts[i], f2d.handle); 599 } 600 } 601 allFonts = fonts; 602 } 603 604 Font []copyFonts = new Font[allFonts.length]; 605 System.arraycopy(allFonts, 0, copyFonts, 0, allFonts.length); 606 return copyFonts; 607 } 608 609 /** 610 * Returns all fonts available in this environment. 611 */ 612 public Font[] getAllFonts() { 613 Font[] installedFonts = getAllInstalledFonts(); 614 Font[] created = FontManager.getCreatedFonts(); 615 if (created == null || created.length == 0) { 616 return installedFonts; 617 } else { 618 int newlen = installedFonts.length + created.length; 619 Font [] fonts = java.util.Arrays.copyOf(installedFonts, newlen); 620 System.arraycopy(created, 0, fonts, 621 installedFonts.length, created.length); 622 return fonts; 623 } 624 } 625 626 /** 627 * Default locale can be changed but we need to know the initial locale 628 * as that is what is used by native code. Changing Java default locale 629 * doesn't affect that. 630 * Returns the locale in use when using native code to communicate 631 * with platform APIs. On windows this is known as the "system" locale, 632 * and it is usually the same as the platform locale, but not always, 633 * so this method also checks an implementation property used only 634 * on windows and uses that if set. 635 */ 636 private static Locale systemLocale = null; 637 public static Locale getSystemStartupLocale() { 638 if (systemLocale == null) { 639 systemLocale = (Locale) 640 java.security.AccessController.doPrivileged( 641 new java.security.PrivilegedAction() { 642 public Object run() { 643 /* On windows the system locale may be different than the 644 * user locale. This is an unsupported configuration, but 645 * in that case we want to return a dummy locale that will 646 * never cause a match in the usage of this API. This is 647 * important because Windows documents that the family 648 * names of fonts are enumerated using the language of 649 * the system locale. BY returning a dummy locale in that 650 * case we do not use the platform API which would not 651 * return us the names we want. 652 */ 653 String fileEncoding = System.getProperty("file.encoding", ""); 654 String sysEncoding = System.getProperty("sun.jnu.encoding"); 655 if (sysEncoding != null && !sysEncoding.equals(fileEncoding)) { 656 return Locale.ROOT; 657 } 658 659 String language = System.getProperty("user.language", "en"); 660 String country = System.getProperty("user.country",""); 661 String variant = System.getProperty("user.variant",""); 662 return new Locale(language, country, variant); 663 } 664 }); 665 } 666 return systemLocale; 667 } 668 669 /* Really we need only the JRE fonts family names, but there's little 670 * overhead in doing this the easy way by adding all the currently 671 * known fonts. 672 */ 673 protected void getJREFontFamilyNames(TreeMap<String,String> familyNames, 674 Locale requestedLocale) { 675 FontManager.registerDeferredJREFonts(jreFontDirName); 676 Font2D[] physicalfonts = FontManager.getPhysicalFonts(); 677 for (int i=0; i < physicalfonts.length; i++) { 678 if (!(physicalfonts[i] instanceof NativeFont)) { 679 String name = 680 physicalfonts[i].getFamilyName(requestedLocale); 681 familyNames.put(name.toLowerCase(requestedLocale), name); 682 } 683 } 684 } 685 686 private String[] allFamilies; // cache for default locale only 687 private Locale lastDefaultLocale; 688 689 public String[] getInstalledFontFamilyNames(Locale requestedLocale) { 690 if (requestedLocale == null) { 691 requestedLocale = Locale.getDefault(); 692 } 693 if (allFamilies != null && lastDefaultLocale != null && 694 requestedLocale.equals(lastDefaultLocale)) { 695 String[] copyFamilies = new String[allFamilies.length]; 696 System.arraycopy(allFamilies, 0, copyFamilies, 697 0, allFamilies.length); 698 return copyFamilies; 699 } 700 701 TreeMap<String,String> familyNames = new TreeMap<String,String>(); 702 // these names are always there and aren't localised 703 String str; 704 str = Font.SERIF; familyNames.put(str.toLowerCase(), str); 705 str = Font.SANS_SERIF; familyNames.put(str.toLowerCase(), str); 706 str = Font.MONOSPACED; familyNames.put(str.toLowerCase(), str); 707 str = Font.DIALOG; familyNames.put(str.toLowerCase(), str); 708 str = Font.DIALOG_INPUT; familyNames.put(str.toLowerCase(), str); 709 710 /* Platform APIs may be used to get the set of available family 711 * names for the current default locale so long as it is the same 712 * as the start-up system locale, rather than loading all fonts. 713 */ 714 if (requestedLocale.equals(getSystemStartupLocale()) && 715 FontManager.getFamilyNamesFromPlatform(familyNames, 716 requestedLocale)) { 717 /* Augment platform names with JRE font family names */ 718 getJREFontFamilyNames(familyNames, requestedLocale); 719 } else { 720 loadFontFiles(); 721 Font2D[] physicalfonts = FontManager.getPhysicalFonts(); 722 for (int i=0; i < physicalfonts.length; i++) { 723 if (!(physicalfonts[i] instanceof NativeFont)) { 724 String name = 725 physicalfonts[i].getFamilyName(requestedLocale); 726 familyNames.put(name.toLowerCase(requestedLocale), name); 727 } 728 } 729 } 730 731 String[] retval = new String[familyNames.size()]; 732 Object [] keyNames = familyNames.keySet().toArray(); 733 for (int i=0; i < keyNames.length; i++) { 734 retval[i] = (String)familyNames.get(keyNames[i]); 735 } 736 if (requestedLocale.equals(Locale.getDefault())) { 737 lastDefaultLocale = requestedLocale; 738 allFamilies = new String[retval.length]; 739 System.arraycopy(retval, 0, allFamilies, 0, allFamilies.length); 740 } 741 return retval; 742 } 743 744 public String[] getAvailableFontFamilyNames(Locale requestedLocale) { 745 String[] installed = getInstalledFontFamilyNames(requestedLocale); 746 /* Use a new TreeMap as used in getInstalledFontFamilyNames 747 * and insert all the keys in lower case, so that the sort order 748 * is the same as the installed families. This preserves historical 749 * behaviour and inserts new families in the right place. 750 * It would have been marginally more efficient to directly obtain 751 * the tree map and just insert new entries, but not so much as 752 * to justify the extra internal interface. 753 */ 754 TreeMap<String, String> map = FontManager.getCreatedFontFamilyNames(); 755 if (map == null || map.size() == 0) { 756 return installed; 757 } else { 758 for (int i=0; i<installed.length; i++) { 759 map.put(installed[i].toLowerCase(requestedLocale), 760 installed[i]); 761 } 762 String[] retval = new String[map.size()]; 763 Object [] keyNames = map.keySet().toArray(); 764 for (int i=0; i < keyNames.length; i++) { 765 retval[i] = (String)map.get(keyNames[i]); 766 } 767 return retval; 768 } 769 } 770 771 public String[] getAvailableFontFamilyNames() { 772 return getAvailableFontFamilyNames(Locale.getDefault()); 773 } 774 775 /** 776 * Returns a file name for the physical font represented by this platform 777 * font name. The default implementation tries to obtain the file name 778 * from the font configuration. 779 * Subclasses may override to provide information from other sources. 780 */ 781 protected String getFileNameFromPlatformName(String platformFontName) { 782 return fontConfig.getFileNameFromPlatformName(platformFontName); 783 } 784 785 public static class TTFilter implements FilenameFilter { 786 public boolean accept(File dir,String name) { 787 /* all conveniently have the same suffix length */ 788 int offset = name.length()-4; 789 if (offset <= 0) { /* must be at least A.ttf */ 790 return false; 791 } else { 792 return(name.startsWith(".ttf", offset) || 793 name.startsWith(".TTF", offset) || 794 name.startsWith(".ttc", offset) || 795 name.startsWith(".TTC", offset)); 796 } 797 } 798 } 799 800 public static class T1Filter implements FilenameFilter { 801 public boolean accept(File dir,String name) { 802 if (noType1Font) { 803 return false; 804 } 805 /* all conveniently have the same suffix length */ 806 int offset = name.length()-4; 807 if (offset <= 0) { /* must be at least A.pfa */ 808 return false; 809 } else { 810 return(name.startsWith(".pfa", offset) || 811 name.startsWith(".pfb", offset) || 812 name.startsWith(".PFA", offset) || 813 name.startsWith(".PFB", offset)); 814 } 815 } 816 } 817 818 public static class TTorT1Filter implements FilenameFilter { 819 public boolean accept(File dir, String name) { 820 821 /* all conveniently have the same suffix length */ 822 int offset = name.length()-4; 823 if (offset <= 0) { /* must be at least A.ttf or A.pfa */ 824 return false; 825 } else { 826 boolean isTT = 827 name.startsWith(".ttf", offset) || 828 name.startsWith(".TTF", offset) || 829 name.startsWith(".ttc", offset) || 830 name.startsWith(".TTC", offset); 831 if (isTT) { 832 return true; 833 } else if (noType1Font) { 834 return false; 835 } else { 836 return(name.startsWith(".pfa", offset) || 837 name.startsWith(".pfb", offset) || 838 name.startsWith(".PFA", offset) || 839 name.startsWith(".PFB", offset)); 840 } 841 } 842 } 843 } 844 845 /* No need to keep consing up new instances - reuse a singleton. 846 * The trade-off is that these objects don't get GC'd. 847 */ 848 public static final TTFilter ttFilter = new TTFilter(); 849 public static final T1Filter t1Filter = new T1Filter(); 850 851 /* The majority of the register functions in this class are 852 * registering platform fonts in the JRE's font maps. 853 * The next one is opposite in function as it registers the JRE 854 * fonts as platform fonts. If subsequent to calling this 855 * your implementation enumerates platform fonts in a way that 856 * would return these fonts too you may get duplicates. 857 * This function is primarily used to install the JRE fonts 858 * so that the native platform can access them. 859 * It is intended to be overridden by platform subclasses 860 * Currently minimal use is made of this as generally 861 * Java 2D doesn't need the platform to be able to 862 * use its fonts and platforms which already have matching 863 * fonts registered (possibly even from other different JRE 864 * versions) generally can't be guaranteed to use the 865 * one registered by this JRE version in response to 866 * requests from this JRE. 867 */ 868 protected void registerJREFontsWithPlatform(String pathName) { 869 return; 870 } 871 872 /* Called from FontManager - has Solaris specific implementation */ 873 public void register1dot0Fonts() { 874 java.security.AccessController.doPrivileged( 875 new java.security.PrivilegedAction() { 876 public Object run() { 877 String type1Dir = "/usr/openwin/lib/X11/fonts/Type1"; 878 registerFontsInDir(type1Dir, true, Font2D.TYPE1_RANK, 879 false, false); 880 return null; 881 } 882 }); 883 } 884 885 protected void registerFontDirs(String pathName) { 886 return; 887 } 888 889 /* Called to register fall back fonts */ 890 public void registerFontsInDir(String dirName) { 891 registerFontsInDir(dirName, true, Font2D.JRE_RANK, true, false); 892 } 893 894 private void registerFontsInDir(String dirName, boolean useJavaRasterizer, 895 int fontRank, 896 boolean defer, boolean resolveSymLinks) { 897 File pathFile = new File(dirName); 898 addDirFonts(dirName, pathFile, ttFilter, 899 FontManager.FONTFORMAT_TRUETYPE, useJavaRasterizer, 900 fontRank==Font2D.UNKNOWN_RANK ? 901 Font2D.TTF_RANK : fontRank, 902 defer, resolveSymLinks); 903 addDirFonts(dirName, pathFile, t1Filter, 904 FontManager.FONTFORMAT_TYPE1, useJavaRasterizer, 905 fontRank==Font2D.UNKNOWN_RANK ? 906 Font2D.TYPE1_RANK : fontRank, 907 defer, resolveSymLinks); 908 } 909 910 private void registerFontsOnPath(String pathName, 911 boolean useJavaRasterizer, int fontRank, 912 boolean defer, boolean resolveSymLinks) { 913 914 StringTokenizer parser = new StringTokenizer(pathName, 915 File.pathSeparator); 916 try { 917 while (parser.hasMoreTokens()) { 918 registerFontsInDir(parser.nextToken(), 919 useJavaRasterizer, fontRank, 920 defer, resolveSymLinks); 921 } 922 } catch (NoSuchElementException e) { 923 } 924 } 925 926 protected void registerFontFile(String fontFileName, String[] nativeNames, 927 int fontRank, boolean defer) { 928 // REMIND: case compare depends on platform 929 if (registeredFontFiles.contains(fontFileName)) { 930 return; 931 } 932 int fontFormat; 933 if (ttFilter.accept(null, fontFileName)) { 934 fontFormat = FontManager.FONTFORMAT_TRUETYPE; 935 } else if (t1Filter.accept(null, fontFileName)) { 936 fontFormat = FontManager.FONTFORMAT_TYPE1; 937 } else { 938 fontFormat = FontManager.FONTFORMAT_NATIVE; 939 } 940 registeredFontFiles.add(fontFileName); 941 if (defer) { 942 FontManager.registerDeferredFont(fontFileName, 943 fontFileName, nativeNames, 944 fontFormat, false, fontRank); 945 } else { 946 FontManager.registerFontFile(fontFileName, nativeNames, 947 fontFormat, false, fontRank); 948 } 949 } 950 951 protected void registerFontDir(String path) { 952 } 953 954 protected String[] getNativeNames(String fontFileName, 955 String platformName) { 956 return null; 957 } 958 959 /* 960 * helper function for registerFonts 961 */ 962 private void addDirFonts(String dirName, File dirFile, 963 FilenameFilter filter, 964 int fontFormat, boolean useJavaRasterizer, 965 int fontRank, 966 boolean defer, boolean resolveSymLinks) { 967 String[] ls = dirFile.list(filter); 968 if (ls == null || ls.length == 0) { 969 return; 970 } 971 String[] fontNames = new String[ls.length]; 972 String[][] nativeNames = new String[ls.length][]; 973 int fontCount = 0; 974 975 for (int i=0; i < ls.length; i++ ) { 976 File theFile = new File(dirFile, ls[i]); 977 String fullName = null; 978 if (resolveSymLinks) { 979 try { 980 fullName = theFile.getCanonicalPath(); 981 } catch (IOException e) { 982 } 983 } 984 if (fullName == null) { 985 fullName = dirName + File.separator + ls[i]; 986 } 987 988 // REMIND: case compare depends on platform 989 if (registeredFontFiles.contains(fullName)) { 990 continue; 991 } 992 993 if (badFonts != null && badFonts.contains(fullName)) { 994 if (debugFonts) { 995 logger.warning("skip bad font " + fullName); 996 } 997 continue; // skip this font file. 998 } 999 1000 registeredFontFiles.add(fullName); 1001 1002 if (debugFonts && logger.isLoggable(Level.INFO)) { 1003 String message = "Registering font " + fullName; 1004 String[] natNames = getNativeNames(fullName, null); 1005 if (natNames == null) { 1006 message += " with no native name"; 1007 } else { 1008 message += " with native name(s) " + natNames[0]; 1009 for (int nn = 1; nn < natNames.length; nn++) { 1010 message += ", " + natNames[nn]; 1011 } 1012 } 1013 logger.info(message); 1014 } 1015 fontNames[fontCount] = fullName; 1016 nativeNames[fontCount++] = getNativeNames(fullName, null); 1017 } 1018 FontManager.registerFonts(fontNames, nativeNames, fontCount, 1019 fontFormat, useJavaRasterizer, fontRank, 1020 defer); 1021 return; 1022 } 1023 1024 /* 1025 * A GE may verify whether a font file used in a fontconfiguration 1026 * exists. If it doesn't then either we may substitute the default 1027 * font, or perhaps elide it altogether from the composite font. 1028 * This makes some sense on windows where the font file is only 1029 * likely to be in one place. But on other OSes, eg Linux, the file 1030 * can move around depending. So there we probably don't want to assume 1031 * its missing and so won't add it to this list. 1032 * If this list - missingFontFiles - is non-null then the composite 1033 * font initialisation logic tests to see if a font file is in that 1034 * set. 1035 * Only one thread should be able to add to this set so we don't 1036 * synchronize. 1037 */ 1038 protected void addToMissingFontFileList(String fileName) { 1039 if (missingFontFiles == null) { 1040 missingFontFiles = new HashSet<String>(); 1041 } 1042 missingFontFiles.add(fileName); 1043 } 1044 1045 /** 1046 * Creates this environment's FontConfiguration. 1047 */ 1048 protected abstract FontConfiguration createFontConfiguration(); 1049 1050 public abstract FontConfiguration 1051 createFontConfiguration(boolean preferLocaleFonts, 1052 boolean preferPropFonts); 1053 1054 /* 1055 * This method asks the font configuration API for all platform names 1056 * used as components of composite/logical fonts and iterates over these 1057 * looking up their corresponding file name and registers these fonts. 1058 * It also ensures that the fonts are accessible via platform APIs. 1059 * The composites themselves are then registered. 1060 */ 1061 private void 1062 initCompositeFonts(FontConfiguration fontConfig, 1063 ConcurrentHashMap<String, Font2D> altNameCache) { 1064 1065 int numCoreFonts = fontConfig.getNumberCoreFonts(); 1066 String[] fcFonts = fontConfig.getPlatformFontNames(); 1067 for (int f=0; f<fcFonts.length; f++) { 1068 String platformFontName = fcFonts[f]; 1069 String fontFileName = 1070 getFileNameFromPlatformName(platformFontName); 1071 String[] nativeNames = null; 1072 if (fontFileName == null) { 1073 /* No file located, so register using the platform name, 1074 * i.e. as a native font. 1075 */ 1076 fontFileName = platformFontName; 1077 } else { 1078 if (f < numCoreFonts) { 1079 /* If platform APIs also need to access the font, add it 1080 * to a set to be registered with the platform too. 1081 * This may be used to add the parent directory to the X11 1082 * font path if its not already there. See the docs for the 1083 * subclass implementation. 1084 * This is now mainly for the benefit of X11-based AWT 1085 * But for historical reasons, 2D initialisation code 1086 * makes these calls. 1087 * If the fontconfiguration file is properly set up 1088 * so that all fonts are mapped to files and all their 1089 * appropriate directories are specified, then this 1090 * method will be low cost as it will return after 1091 * a test that finds a null lookup map. 1092 */ 1093 addFontToPlatformFontPath(platformFontName); 1094 } 1095 nativeNames = getNativeNames(fontFileName, platformFontName); 1096 } 1097 /* Uncomment these two lines to "generate" the XLFD->filename 1098 * mappings needed to speed start-up on Solaris. 1099 * Augment this with the appendedpathname and the mappings 1100 * for native (F3) fonts 1101 */ 1102 //String platName = platformFontName.replaceAll(" ", "_"); 1103 //System.out.println("filename."+platName+"="+fontFileName); 1104 registerFontFile(fontFileName, nativeNames, 1105 Font2D.FONT_CONFIG_RANK, true); 1106 1107 1108 } 1109 /* This registers accumulated paths from the calls to 1110 * addFontToPlatformFontPath(..) and any specified by 1111 * the font configuration. Rather than registering 1112 * the fonts it puts them in a place and form suitable for 1113 * the Toolkit to pick up and use if a toolkit is initialised, 1114 * and if it uses X11 fonts. 1115 */ 1116 registerPlatformFontsUsedByFontConfiguration(); 1117 1118 CompositeFontDescriptor[] compositeFontInfo 1119 = fontConfig.get2DCompositeFontInfo(); 1120 for (int i = 0; i < compositeFontInfo.length; i++) { 1121 CompositeFontDescriptor descriptor = compositeFontInfo[i]; 1122 String[] componentFileNames = descriptor.getComponentFileNames(); 1123 String[] componentFaceNames = descriptor.getComponentFaceNames(); 1124 1125 /* It would be better eventually to handle this in the 1126 * FontConfiguration code which should also remove duplicate slots 1127 */ 1128 if (missingFontFiles != null) { 1129 for (int ii=0; ii<componentFileNames.length; ii++) { 1130 if (missingFontFiles.contains(componentFileNames[ii])) { 1131 componentFileNames[ii] = getDefaultFontFile(); 1132 componentFaceNames[ii] = getDefaultFontFaceName(); 1133 } 1134 } 1135 } 1136 1137 /* FontConfiguration needs to convey how many fonts it has added 1138 * as fallback component fonts which should not affect metrics. 1139 * The core component count will be the number of metrics slots. 1140 * This does not preclude other mechanisms for adding 1141 * fall back component fonts to the composite. 1142 */ 1143 if (altNameCache != null) { 1144 FontManager.registerCompositeFont( 1145 descriptor.getFaceName(), 1146 componentFileNames, componentFaceNames, 1147 descriptor.getCoreComponentCount(), 1148 descriptor.getExclusionRanges(), 1149 descriptor.getExclusionRangeLimits(), 1150 true, 1151 altNameCache); 1152 } else { 1153 FontManager.registerCompositeFont( 1154 descriptor.getFaceName(), 1155 componentFileNames, componentFaceNames, 1156 descriptor.getCoreComponentCount(), 1157 descriptor.getExclusionRanges(), 1158 descriptor.getExclusionRangeLimits(), 1159 true); 1160 } 1161 if (debugFonts) { 1162 logger.info("registered " + descriptor.getFaceName()); 1163 } 1164 } 1165 } 1166 1167 /** 1168 * Notifies graphics environment that the logical font configuration 1169 * uses the given platform font name. The graphics environment may 1170 * use this for platform specific initialization. 1171 */ 1172 protected void addFontToPlatformFontPath(String platformFontName) { 1173 } 1174 1175 protected void registerPlatformFontsUsedByFontConfiguration() { 1176 } 1177 1178 /** 1179 * Determines whether the given font is a logical font. 1180 */ 1181 public static boolean isLogicalFont(Font f) { 1182 return FontConfiguration.isLogicalFontFamilyName(f.getFamily()); 1183 } 1184 1185 /** 1186 * Return the default font configuration. 1187 */ 1188 public FontConfiguration getFontConfiguration() { 1189 return fontConfig; 1190 } 1191 1192 /** 1193 * Return the bounds of a GraphicsDevice, less its screen insets. 1194 * See also java.awt.GraphicsEnvironment.getUsableBounds(); 1195 */ 1196 public static Rectangle getUsableBounds(GraphicsDevice gd) { 1197 GraphicsConfiguration gc = gd.getDefaultConfiguration(); 1198 Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(gc); 1199 Rectangle usableBounds = gc.getBounds(); 1200 1201 usableBounds.x += insets.left; 1202 usableBounds.y += insets.top; 1203 usableBounds.width -= (insets.left + insets.right); 1204 usableBounds.height -= (insets.top + insets.bottom); 1205 1206 return usableBounds; 1207 } 1208 1209 /** 1210 * This method is provided for internal and exclusive use by Swing. 1211 * This method should no longer be called, instead directly call 1212 * FontManager.fontSupportsDefaultEncoding(Font). 1213 * This method will be removed once Swing is updated to no longer 1214 * call it. 1215 */ 1216 public static boolean fontSupportsDefaultEncoding(Font font) { 1217 return FontManager.fontSupportsDefaultEncoding(font); 1218 } 1219 1220 public static void useAlternateFontforJALocales() { 1221 FontManager.useAlternateFontforJALocales(); 1222 } 1223 1224 /* 1225 * This invocation is not in a privileged block because 1226 * all privileged operations (reading files and properties) 1227 * was conducted on the creation of the GE 1228 */ 1229 public void 1230 createCompositeFonts(ConcurrentHashMap<String, Font2D> altNameCache, 1231 boolean preferLocale, 1232 boolean preferProportional) { 1233 1234 FontConfiguration fontConfig = 1235 createFontConfiguration(preferLocale, preferProportional); 1236 initCompositeFonts(fontConfig, altNameCache); 1237 } 1238 1239 /* If (as we do on X11) need to set a platform font path, 1240 * then the needed path may be specified by the platform 1241 * specific FontConfiguration class & data file. Such platforms 1242 * (ie X11) need to override this method to retrieve this information 1243 * into suitable data structures. 1244 */ 1245 protected void getPlatformFontPathFromFontConfig() { 1246 } 1247 1248 /** 1249 * From the DisplayChangedListener interface; called 1250 * when the display mode has been changed. 1251 */ 1252 public void displayChanged() { 1253 // notify screens in device array to do display update stuff 1254 for (GraphicsDevice gd : getScreenDevices()) { 1255 if (gd instanceof DisplayChangedListener) { 1256 ((DisplayChangedListener) gd).displayChanged(); 1257 } 1258 } 1259 1260 // notify SunDisplayChanger list (e.g. VolatileSurfaceManagers and 1261 // SurfaceDataProxies) about the display change event 1262 displayChanger.notifyListeners(); 1263 } 1264 1265 /** 1266 * Part of the DisplayChangedListener interface: 1267 * propagate this event to listeners 1268 */ 1269 public void paletteChanged() { 1270 displayChanger.notifyPaletteChanged(); 1271 } 1272 1273 /* 1274 * ----DISPLAY CHANGE SUPPORT---- 1275 */ 1276 1277 protected SunDisplayChanger displayChanger = new SunDisplayChanger(); 1278 1279 /** 1280 * Add a DisplayChangeListener to be notified when the display settings 1281 * are changed. 1282 */ 1283 public void addDisplayChangedListener(DisplayChangedListener client) { 1284 displayChanger.add(client); 1285 } 1286 1287 /** 1288 * Remove a DisplayChangeListener from Win32GraphicsEnvironment 1289 */ 1290 public void removeDisplayChangedListener(DisplayChangedListener client) { 1291 displayChanger.remove(client); 1292 } 1293 1294 /* 1295 * ----END DISPLAY CHANGE SUPPORT---- 1296 */ 1297 }