1 package sun.awt; 2 3 import java.awt.GraphicsEnvironment; 4 import java.io.BufferedReader; 5 import java.io.File; 6 import java.io.FileReader; 7 import java.io.IOException; 8 import java.io.StreamTokenizer; 9 import java.util.HashMap; 10 import java.util.HashSet; 11 import java.util.Locale; 12 import java.util.Map; 13 import java.util.NoSuchElementException; 14 import java.util.StringTokenizer; 15 import java.util.Vector; 16 17 import javax.swing.plaf.FontUIResource; 18 import sun.awt.motif.MFontConfiguration; 19 import sun.font.CompositeFont; 20 import sun.font.FontManager; 21 import sun.font.SunFontManager; 22 import sun.font.FontConfigManager; 23 import sun.font.FcFontConfiguration; 24 import sun.font.FontAccess; 25 import sun.font.FontUtilities; 26 import sun.font.NativeFont; 27 import sun.util.logging.PlatformLogger; 28 29 /** 30 * The X11 implementation of {@link FontManager}. 31 */ 32 public class X11FontManager extends SunFontManager { 33 34 // constants identifying XLFD and font ID fields 35 private static final int FOUNDRY_FIELD = 1; 36 private static final int FAMILY_NAME_FIELD = 2; 37 private static final int WEIGHT_NAME_FIELD = 3; 38 private static final int SLANT_FIELD = 4; 39 private static final int SETWIDTH_NAME_FIELD = 5; 40 private static final int ADD_STYLE_NAME_FIELD = 6; 41 private static final int PIXEL_SIZE_FIELD = 7; 42 private static final int POINT_SIZE_FIELD = 8; 43 private static final int RESOLUTION_X_FIELD = 9; 44 private static final int RESOLUTION_Y_FIELD = 10; 45 private static final int SPACING_FIELD = 11; 46 private static final int AVERAGE_WIDTH_FIELD = 12; 47 private static final int CHARSET_REGISTRY_FIELD = 13; 48 private static final int CHARSET_ENCODING_FIELD = 14; 49 50 /* 51 * fontNameMap is a map from a fontID (which is a substring of an XLFD like 52 * "-monotype-arial-bold-r-normal-iso8859-7") 53 * to font file path like 54 * /usr/openwin/lib/locale/iso_8859_7/X11/fonts/TrueType/ArialBoldItalic.ttf 55 * It's used in a couple of methods like 56 * getFileNameFomPlatformName(..) to help locate the font file. 57 * We use this substring of a full XLFD because the font configuration files 58 * define the XLFDs in a way that's easier to make into a request. 59 * E.g., the -0-0-0-0-p-0- reported by X is -*-%d-*-*-p-*- in the font 60 * configuration files. We need to remove that part for comparisons. 61 */ 62 private static Map fontNameMap = new HashMap(); 63 64 /* 65 * xlfdMap is a map from a platform path like 66 * /usr/openwin/lib/locale/ja/X11/fonts/TT/HG-GothicB.ttf to an XLFD like 67 * "-ricoh-hg gothic b-medium-r-normal--0-0-0-0-m-0-jisx0201.1976-0" 68 * Because there may be multiple native names, because the font is used 69 * to support multiple X encodings for example, the value of an entry in 70 * this map is always a vector where we store all the native names. 71 * For fonts which we don't understand the key isn't a pathname, its 72 * the full XLFD string like :- 73 * "-ricoh-hg gothic b-medium-r-normal--0-0-0-0-m-0-jisx0201.1976-0" 74 */ 75 private static Map xlfdMap = new HashMap(); 76 77 /* xFontDirsMap is also a map from a font ID to a font filepath. 78 * The difference from fontNameMap is just that it does not have 79 * resolved symbolic links. Normally this is not interesting except 80 * that we need to know the directory in which a font was found to 81 * add it to the X font server path, since although the files may 82 * be linked, the fonts.dir is different and specific to the encoding 83 * handled by that directory. This map is nulled out after use to free 84 * heap space. If the optimal path is taken, such that all fonts in 85 * font configuration files are referenced by filename, then the font 86 * dir can be directly derived as its parent directory. 87 * If a font is used by two XLFDs, each corresponding to a different 88 * X11 font directory, then precautions must be taken to include both 89 * directories. 90 */ 91 private static Map xFontDirsMap; 92 93 /* 94 * This is the set of font directories needed to be on the X font path 95 * to enable AWT heavyweights to find all of the font configuration fonts. 96 * It is populated by : 97 * - awtfontpath entries in the fontconfig.properties 98 * - parent directories of "core" fonts used in the fontconfig.properties 99 * - looking up font dirs in the xFontDirsMap where the key is a fontID 100 * (cut down version of the XLFD read from the font configuration file). 101 * This set is nulled out after use to free heap space. 102 */ 103 private static HashSet<String> fontConfigDirs = null; 104 105 /* These maps are used on Linux where we reference the Lucida oblique 106 * fonts in fontconfig files even though they aren't in the standard 107 * font directory. This explicitly remaps the XLFDs for these to the 108 * correct base font. This is needed to prevent composite fonts from 109 * defaulting to the Lucida Sans which is a bad substitute for the 110 * monospaced Lucida Sans Typewriter. Also these maps prevent the 111 * JRE from doing wasted work at start up. 112 */ 113 HashMap<String, String> oblmap = null; 114 115 116 /* 117 * Used to eliminate redundant work. When a font directory is 118 * registered it added to this list. Subsequent registrations for the 119 * same directory can then be skipped by checking this Map. 120 * Access to this map is not synchronised here since creation 121 * of the singleton GE instance is already synchronised and that is 122 * the only code path that accesses this map. 123 */ 124 private static HashMap registeredDirs = new HashMap(); 125 126 /* Array of directories to be added to the X11 font path. 127 * Used by static method called from Toolkits which use X11 fonts. 128 * Specifically this means MToolkit 129 */ 130 private static String[] fontdirs = null; 131 132 private static String[] defaultPlatformFont = null; 133 134 private FontConfigManager fcManager = null; 135 136 public static X11FontManager getInstance() { 137 return (X11FontManager) SunFontManager.getInstance(); 138 } 139 140 /** 141 * Takes family name property in the following format: 142 * "-linotype-helvetica-medium-r-normal-sans-*-%d-*-*-p-*-iso8859-1" 143 * and returns the name of the corresponding physical font. 144 * This code is used to resolve font configuration fonts, and expects 145 * only to get called for these fonts. 146 */ 147 @Override 148 public String getFileNameFromPlatformName(String platName) { 149 150 /* If the FontConfig file doesn't use xlfds, or its 151 * FcFontConfiguration, this may be already a file name. 152 */ 153 if (platName.startsWith("/")) { 154 return platName; 155 } 156 157 String fileName = null; 158 String fontID = specificFontIDForName(platName); 159 160 /* If the font filename has been explicitly assigned in the 161 * font configuration file, use it. This avoids accessing 162 * the wrong fonts on Linux, where different fonts (some 163 * of which may not be usable by 2D) may share the same 164 * specific font ID. It may also speed up the lookup. 165 */ 166 fileName = super.getFileNameFromPlatformName(platName); 167 if (fileName != null) { 168 if (isHeadless() && fileName.startsWith("-")) { 169 /* if it's headless, no xlfd should be used */ 170 return null; 171 } 172 if (fileName.startsWith("/")) { 173 /* If a path is assigned in the font configuration file, 174 * it is required that the config file also specify using the 175 * new awtfontpath key the X11 font directories 176 * which must be added to the X11 font path to support 177 * AWT access to that font. For that reason we no longer 178 * have code here to add the parent directory to the list 179 * of font config dirs, since the parent directory may not 180 * be sufficient if fonts are symbolically linked to a 181 * different directory. 182 * 183 * Add this XLFD (platform name) to the list of known 184 * ones for this file. 185 */ 186 Vector xVal = (Vector) xlfdMap.get(fileName); 187 if (xVal == null) { 188 /* Try to be robust on Linux distros which move fonts 189 * around by verifying that the fileName represents a 190 * file that exists. If it doesn't, set it to null 191 * to trigger a search. 192 */ 193 if (getFontConfiguration().needToSearchForFile(fileName)) { 194 fileName = null; 195 } 196 if (fileName != null) { 197 xVal = new Vector(); 198 xVal.add(platName); 199 xlfdMap.put(fileName, xVal); 200 } 201 } else { 202 if (!xVal.contains(platName)) { 203 xVal.add(platName); 204 } 205 } 206 } 207 if (fileName != null) { 208 fontNameMap.put(fontID, fileName); 209 return fileName; 210 } 211 } 212 213 if (fontID != null) { 214 fileName = (String)fontNameMap.get(fontID); 215 /* On Linux check for the Lucida Oblique fonts */ 216 if (fileName == null && FontUtilities.isLinux && !isOpenJDK()) { 217 if (oblmap == null) { 218 initObliqueLucidaFontMap(); 219 } 220 String oblkey = getObliqueLucidaFontID(fontID); 221 if (oblkey != null) { 222 fileName = oblmap.get(oblkey); 223 } 224 } 225 if (fontPath == null && 226 (fileName == null || !fileName.startsWith("/"))) { 227 if (FontUtilities.debugFonts()) { 228 FontUtilities.getLogger() 229 .warning("** Registering all font paths because " + 230 "can't find file for " + platName); 231 } 232 fontPath = getPlatformFontPath(noType1Font); 233 registerFontDirs(fontPath); 234 if (FontUtilities.debugFonts()) { 235 FontUtilities.getLogger() 236 .warning("** Finished registering all font paths"); 237 } 238 fileName = (String)fontNameMap.get(fontID); 239 } 240 if (fileName == null && !isHeadless()) { 241 /* Query X11 directly to see if this font is available 242 * as a native font. 243 */ 244 fileName = getX11FontName(platName); 245 } 246 if (fileName == null) { 247 fontID = switchFontIDForName(platName); 248 fileName = (String)fontNameMap.get(fontID); 249 } 250 if (fileName != null) { 251 fontNameMap.put(fontID, fileName); 252 } 253 } 254 return fileName; 255 } 256 257 @Override 258 protected String[] getNativeNames(String fontFileName, 259 String platformName) { 260 Vector nativeNames; 261 if ((nativeNames=(Vector)xlfdMap.get(fontFileName))==null) { 262 if (platformName == null) { 263 return null; 264 } else { 265 /* back-stop so that at least the name used in the 266 * font configuration file is known as a native name 267 */ 268 String []natNames = new String[1]; 269 natNames[0] = platformName; 270 return natNames; 271 } 272 } else { 273 int len = nativeNames.size(); 274 return (String[])nativeNames.toArray(new String[len]); 275 } 276 } 277 278 /* NOTE: this method needs to be executed in a privileged context. 279 * The superclass constructor which is the primary caller of 280 * this method executes entirely in such a context. Additionally 281 * the loadFonts() method does too. So all should be well. 282 283 */ 284 @Override 285 protected void registerFontDir(String path) { 286 /* fonts.dir file format looks like :- 287 * 47 288 * Arial.ttf -monotype-arial-regular-r-normal--0-0-0-0-p-0-iso8859-1 289 * Arial-Bold.ttf -monotype-arial-bold-r-normal--0-0-0-0-p-0-iso8859-1 290 * ... 291 */ 292 if (FontUtilities.debugFonts()) { 293 FontUtilities.getLogger().info("ParseFontDir " + path); 294 } 295 File fontsDotDir = new File(path + File.separator + "fonts.dir"); 296 FileReader fr = null; 297 try { 298 if (fontsDotDir.canRead()) { 299 fr = new FileReader(fontsDotDir); 300 BufferedReader br = new BufferedReader(fr, 8192); 301 StreamTokenizer st = new StreamTokenizer(br); 302 st.eolIsSignificant(true); 303 int ttype = st.nextToken(); 304 if (ttype == StreamTokenizer.TT_NUMBER) { 305 int numEntries = (int)st.nval; 306 ttype = st.nextToken(); 307 if (ttype == StreamTokenizer.TT_EOL) { 308 st.resetSyntax(); 309 st.wordChars(32, 127); 310 st.wordChars(128 + 32, 255); 311 st.whitespaceChars(0, 31); 312 313 for (int i=0; i < numEntries; i++) { 314 ttype = st.nextToken(); 315 if (ttype == StreamTokenizer.TT_EOF) { 316 break; 317 } 318 if (ttype != StreamTokenizer.TT_WORD) { 319 break; 320 } 321 int breakPos = st.sval.indexOf(' '); 322 if (breakPos <= 0) { 323 /* On TurboLinux 8.0 a fonts.dir file had 324 * a line with integer value "24" which 325 * appeared to be the number of remaining 326 * entries in the file. This didn't add to 327 * the value on the first line of the file. 328 * Seemed like XFree86 didn't like this line 329 * much either. It failed to parse the file. 330 * Ignore lines like this completely, and 331 * don't let them count as an entry. 332 */ 333 numEntries++; 334 ttype = st.nextToken(); 335 if (ttype != StreamTokenizer.TT_EOL) { 336 break; 337 } 338 339 continue; 340 } 341 if (st.sval.charAt(0) == '!') { 342 /* TurboLinux 8.0 comment line: ignore. 343 * can't use st.commentChar('!') to just 344 * skip because this line mustn't count 345 * against numEntries. 346 */ 347 numEntries++; 348 ttype = st.nextToken(); 349 if (ttype != StreamTokenizer.TT_EOL) { 350 break; 351 } 352 continue; 353 } 354 String fileName = st.sval.substring(0, breakPos); 355 /* TurboLinux 8.0 uses some additional syntax to 356 * indicate algorithmic styling values. 357 * Ignore ':' separated files at the beginning 358 * of the fileName 359 */ 360 int lastColon = fileName.lastIndexOf(':'); 361 if (lastColon > 0) { 362 if (lastColon+1 >= fileName.length()) { 363 continue; 364 } 365 fileName = fileName.substring(lastColon+1); 366 } 367 String fontPart = st.sval.substring(breakPos+1); 368 String fontID = specificFontIDForName(fontPart); 369 String sVal = (String) fontNameMap.get(fontID); 370 371 if (FontUtilities.debugFonts()) { 372 PlatformLogger logger = FontUtilities.getLogger(); 373 logger.info("file=" + fileName + 374 " xlfd=" + fontPart); 375 logger.info("fontID=" + fontID + 376 " sVal=" + sVal); 377 } 378 String fullPath = null; 379 try { 380 File file = new File(path,fileName); 381 /* we may have a resolved symbolic link 382 * this becomes important for an xlfd we 383 * still need to know the location it was 384 * found to update the X server font path 385 * for use by AWT heavyweights - and when 2D 386 * wants to use the native rasteriser. 387 */ 388 if (xFontDirsMap == null) { 389 xFontDirsMap = new HashMap(); 390 } 391 xFontDirsMap.put(fontID, path); 392 fullPath = file.getCanonicalPath(); 393 } catch (IOException e) { 394 fullPath = path + File.separator + fileName; 395 } 396 Vector xVal = (Vector) xlfdMap.get(fullPath); 397 if (FontUtilities.debugFonts()) { 398 FontUtilities.getLogger() 399 .info("fullPath=" + fullPath + 400 " xVal=" + xVal); 401 } 402 if ((xVal == null || !xVal.contains(fontPart)) && 403 (sVal == null) || !sVal.startsWith("/")) { 404 if (FontUtilities.debugFonts()) { 405 FontUtilities.getLogger() 406 .info("Map fontID:"+fontID + 407 "to file:" + fullPath); 408 } 409 fontNameMap.put(fontID, fullPath); 410 if (xVal == null) { 411 xVal = new Vector(); 412 xlfdMap.put (fullPath, xVal); 413 } 414 xVal.add(fontPart); 415 } 416 417 ttype = st.nextToken(); 418 if (ttype != StreamTokenizer.TT_EOL) { 419 break; 420 } 421 } 422 } 423 } 424 fr.close(); 425 } 426 } catch (IOException ioe1) { 427 } finally { 428 if (fr != null) { 429 try { 430 fr.close(); 431 } catch (IOException ioe2) { 432 } 433 } 434 } 435 } 436 437 @Override 438 public void loadFonts() { 439 super.loadFonts(); 440 /* These maps are greatly expanded during a loadFonts but 441 * can be reset to their initial state afterwards. 442 * Since preferLocaleFonts() and preferProportionalFonts() will 443 * trigger a partial repopulating from the FontConfiguration 444 * it has to be the inital (empty) state for the latter two, not 445 * simply nulling out. 446 * xFontDirsMap is a special case in that the implementation 447 * will typically not ever need to initialise it so it can be null. 448 */ 449 xFontDirsMap = null; 450 xlfdMap = new HashMap(1); 451 fontNameMap = new HashMap(1); 452 } 453 454 private String getObliqueLucidaFontID(String fontID) { 455 if (fontID.startsWith("-lucidasans-medium-i-normal") || 456 fontID.startsWith("-lucidasans-bold-i-normal") || 457 fontID.startsWith("-lucidatypewriter-medium-i-normal") || 458 fontID.startsWith("-lucidatypewriter-bold-i-normal")) { 459 return fontID.substring(0, fontID.indexOf("-i-")); 460 } else { 461 return null; 462 } 463 } 464 465 private static String getX11FontName(String platName) { 466 String xlfd = platName.replaceAll("%d", "*"); 467 if (NativeFont.fontExists(xlfd)) { 468 return xlfd; 469 } else { 470 return null; 471 } 472 } 473 474 private void initObliqueLucidaFontMap() { 475 oblmap = new HashMap<String, String>(); 476 oblmap.put("-lucidasans-medium", 477 jreLibDirName+"/fonts/LucidaSansRegular.ttf"); 478 oblmap.put("-lucidasans-bold", 479 jreLibDirName+"/fonts/LucidaSansDemiBold.ttf"); 480 oblmap.put("-lucidatypewriter-medium", 481 jreLibDirName+"/fonts/LucidaTypewriterRegular.ttf"); 482 oblmap.put("-lucidatypewriter-bold", 483 jreLibDirName+"/fonts/LucidaTypewriterBold.ttf"); 484 } 485 486 private boolean isHeadless() { 487 GraphicsEnvironment ge = 488 GraphicsEnvironment.getLocalGraphicsEnvironment(); 489 return GraphicsEnvironment.isHeadless(); 490 } 491 492 private String specificFontIDForName(String name) { 493 494 int[] hPos = new int[14]; 495 int hyphenCnt = 1; 496 int pos = 1; 497 498 while (pos != -1 && hyphenCnt < 14) { 499 pos = name.indexOf('-', pos); 500 if (pos != -1) { 501 hPos[hyphenCnt++] = pos; 502 pos++; 503 } 504 } 505 506 if (hyphenCnt != 14) { 507 if (FontUtilities.debugFonts()) { 508 FontUtilities.getLogger() 509 .severe("Font Configuration Font ID is malformed:" + name); 510 } 511 return name; // what else can we do? 512 } 513 514 StringBuffer sb = 515 new StringBuffer(name.substring(hPos[FAMILY_NAME_FIELD-1], 516 hPos[SETWIDTH_NAME_FIELD])); 517 sb.append(name.substring(hPos[CHARSET_REGISTRY_FIELD-1])); 518 String retval = sb.toString().toLowerCase (Locale.ENGLISH); 519 return retval; 520 } 521 522 private String switchFontIDForName(String name) { 523 524 int[] hPos = new int[14]; 525 int hyphenCnt = 1; 526 int pos = 1; 527 528 while (pos != -1 && hyphenCnt < 14) { 529 pos = name.indexOf('-', pos); 530 if (pos != -1) { 531 hPos[hyphenCnt++] = pos; 532 pos++; 533 } 534 } 535 536 if (hyphenCnt != 14) { 537 if (FontUtilities.debugFonts()) { 538 FontUtilities.getLogger() 539 .severe("Font Configuration Font ID is malformed:" + name); 540 } 541 return name; // what else can we do? 542 } 543 544 String slant = name.substring(hPos[SLANT_FIELD-1]+1, 545 hPos[SLANT_FIELD]); 546 String family = name.substring(hPos[FAMILY_NAME_FIELD-1]+1, 547 hPos[FAMILY_NAME_FIELD]); 548 String registry = name.substring(hPos[CHARSET_REGISTRY_FIELD-1]+1, 549 hPos[CHARSET_REGISTRY_FIELD]); 550 String encoding = name.substring(hPos[CHARSET_ENCODING_FIELD-1]+1); 551 552 if (slant.equals("i")) { 553 slant = "o"; 554 } else if (slant.equals("o")) { 555 slant = "i"; 556 } 557 // workaround for #4471000 558 if (family.equals("itc zapfdingbats") 559 && registry.equals("sun") 560 && encoding.equals("fontspecific")){ 561 registry = "adobe"; 562 } 563 StringBuffer sb = 564 new StringBuffer(name.substring(hPos[FAMILY_NAME_FIELD-1], 565 hPos[SLANT_FIELD-1]+1)); 566 sb.append(slant); 567 sb.append(name.substring(hPos[SLANT_FIELD], 568 hPos[SETWIDTH_NAME_FIELD]+1)); 569 sb.append(registry); 570 sb.append(name.substring(hPos[CHARSET_ENCODING_FIELD-1])); 571 String retval = sb.toString().toLowerCase (Locale.ENGLISH); 572 return retval; 573 } 574 575 /** 576 * Returns the face name for the given XLFD. 577 */ 578 public String getFileNameFromXLFD(String name) { 579 String fileName = null; 580 String fontID = specificFontIDForName(name); 581 if (fontID != null) { 582 fileName = (String)fontNameMap.get(fontID); 583 if (fileName == null) { 584 fontID = switchFontIDForName(name); 585 fileName = (String)fontNameMap.get(fontID); 586 } 587 if (fileName == null) { 588 fileName = getDefaultFontFile(); 589 } 590 } 591 return fileName; 592 } 593 594 /* Register just the paths, (it doesn't register the fonts). 595 * If a font configuration file has specified a baseFontPath 596 * fontPath is just those directories, unless on usage we 597 * find it doesn't contain what we need for the logical fonts. 598 * Otherwise, we register all the paths on Solaris, because 599 * the fontPath we have here is the complete one from 600 * parsing /var/sadm/install/contents, not just 601 * what's on the X font path (may be this should be 602 * changed). 603 * But for now what it means is that if we didn't do 604 * this then if the font weren't listed anywhere on the 605 * less complete font path we'd trigger loadFonts which 606 * actually registers the fonts. This may actually be 607 * the right thing tho' since that would also set up 608 * the X font path without which we wouldn't be able to 609 * display some "native" fonts. 610 * So something to revisit is that probably fontPath 611 * here ought to be only the X font path + jre font dir. 612 * loadFonts should have a separate native call to 613 * get the rest of the platform font path. 614 * 615 * Registering the directories can now be avoided in the 616 * font configuration initialisation when filename entries 617 * exist in the font configuration file for all fonts. 618 * (Perhaps a little confusingly a filename entry is 619 * actually keyed using the XLFD used in the font entries, 620 * and it maps *to* a real filename). 621 * In the event any are missing, registration of all 622 * directories will be invoked to find the real files. 623 * 624 * But registering the directory performed other 625 * functions such as filling in the map of all native names 626 * for the font. So when this method isn't invoked, they still 627 * must be found. This is mitigated by getNativeNames now 628 * being able to return at least the platform name, but mostly 629 * by ensuring that when a filename key is found, that 630 * xlfd key is stored as one of the set of platform names 631 * for the font. Its a set because typical font configuration 632 * files reference the same CJK font files using multiple 633 * X11 encodings. For the code that adds this to the map 634 * see X11GE.getFileNameFromPlatformName(..) 635 * If you don't get all of these then some code points may 636 * not use the Xserver, and will not get the PCF bitmaps 637 * that are available for some point sizes. 638 * So, in the event that there is such a problem, 639 * unconditionally making this call may be necessary, at 640 * some cost to JRE start-up 641 */ 642 @Override 643 protected void registerFontDirs(String pathName) { 644 645 StringTokenizer parser = new StringTokenizer(pathName, 646 File.pathSeparator); 647 try { 648 while (parser.hasMoreTokens()) { 649 String dirPath = parser.nextToken(); 650 if (dirPath != null && !registeredDirs.containsKey(dirPath)) { 651 registeredDirs.put(dirPath, null); 652 registerFontDir(dirPath); 653 } 654 } 655 } catch (NoSuchElementException e) { 656 } 657 } 658 659 // An X font spec (xlfd) includes an encoding. The same TrueType font file 660 // may be referenced from different X font directories in font.dir files 661 // to support use in multiple encodings by X apps. 662 // So for the purposes of font configuration logical fonts where AWT 663 // heavyweights need to access the font via X APIs we need to ensure that 664 // the directory for precisely the encodings needed by this are added to 665 // the x font path. This requires that we note the platform names 666 // specified in font configuration files and use that to identify the 667 // X font directory that contains a font.dir file for that platform name 668 // and add it to the X font path (if display is local) 669 // Here we make use of an already built map of xlfds to font locations 670 // to add the font location to the set of those required to build the 671 // x font path needed by AWT. 672 // These are added to the x font path later. 673 // All this is necessary because on Solaris the font.dir directories 674 // may contain not real font files, but symbolic links to the actual 675 // location but that location is not suitable for the x font path, since 676 // it probably doesn't have a font.dir at all and certainly not one 677 // with the required encodings 678 // If the fontconfiguration file is properly set up so that all fonts 679 // are mapped to files then we will never trigger initialising 680 // xFontDirsMap (it will be null). In this case the awtfontpath entries 681 // must specify all the X11 directories needed by AWT. 682 @Override 683 protected void addFontToPlatformFontPath(String platformName) { 684 // Lazily initialize fontConfigDirs. 685 getPlatformFontPathFromFontConfig(); 686 if (xFontDirsMap != null) { 687 String fontID = specificFontIDForName(platformName); 688 String dirName = (String)xFontDirsMap.get(fontID); 689 if (dirName != null) { 690 fontConfigDirs.add(dirName); 691 } 692 } 693 return; 694 } 695 696 private void getPlatformFontPathFromFontConfig() { 697 if (fontConfigDirs == null) { 698 fontConfigDirs = getFontConfiguration().getAWTFontPathSet(); 699 if (FontUtilities.debugFonts() && fontConfigDirs != null) { 700 String[] names = fontConfigDirs.toArray(new String[0]); 701 for (int i=0;i<names.length;i++) { 702 FontUtilities.getLogger().info("awtfontpath : " + names[i]); 703 } 704 } 705 } 706 } 707 708 @Override 709 protected void registerPlatformFontsUsedByFontConfiguration() { 710 // Lazily initialize fontConfigDirs. 711 getPlatformFontPathFromFontConfig(); 712 if (fontConfigDirs == null) { 713 return; 714 } 715 if (FontUtilities.isLinux) { 716 fontConfigDirs.add(jreLibDirName+File.separator+"oblique-fonts"); 717 } 718 fontdirs = (String[])fontConfigDirs.toArray(new String[0]); 719 } 720 721 // Implements SunGraphicsEnvironment.createFontConfiguration. 722 protected FontConfiguration createFontConfiguration() { 723 /* The logic here decides whether to use a preconfigured 724 * fontconfig.properties file, or synthesise one using platform APIs. 725 * On Solaris (as opposed to OpenSolaris) we try to use the 726 * pre-configured ones, but if the files it specifies are missing 727 * we fail-safe to synthesising one. This might happen if Solaris 728 * changes its fonts. 729 * For OpenSolaris I don't expect us to ever create fontconfig files, 730 * so it will always synthesise. Note that if we misidentify 731 * OpenSolaris as Solaris, then the test for the presence of 732 * Solaris-only font files will correct this. 733 * For Linux we require an exact match of distro and version to 734 * use the preconfigured file, and also that it points to 735 * existent fonts. 736 * If synthesising fails, we fall back to any preconfigured file 737 * and do the best we can. For the commercial JDK this will be 738 * fine as it includes the Lucida fonts. OpenJDK should not hit 739 * this as the synthesis should always work on its platforms. 740 */ 741 FontConfiguration mFontConfig = new MFontConfiguration(this); 742 if (FontUtilities.isOpenSolaris || 743 (FontUtilities.isLinux && 744 (!mFontConfig.foundOsSpecificFile() || 745 !mFontConfig.fontFilesArePresent()) || 746 (FontUtilities.isSolaris && !mFontConfig.fontFilesArePresent()))) { 747 FcFontConfiguration fcFontConfig = 748 new FcFontConfiguration(this); 749 if (fcFontConfig.init()) { 750 return fcFontConfig; 751 } 752 } 753 mFontConfig.init(); 754 return mFontConfig; 755 } 756 public FontConfiguration 757 createFontConfiguration(boolean preferLocaleFonts, 758 boolean preferPropFonts) { 759 760 return new MFontConfiguration(this, 761 preferLocaleFonts, preferPropFonts); 762 } 763 764 public synchronized native String getFontPathNative(boolean noType1Fonts); 765 766 protected synchronized String getFontPath(boolean noType1Fonts) { 767 isHeadless(); // make sure GE is inited, as its the X11 lock. 768 return getFontPathNative(noType1Fonts); 769 } 770 771 public String[] getDefaultPlatformFont() { 772 if (defaultPlatformFont != null) { 773 return defaultPlatformFont; 774 } 775 String[] info = new String[2]; 776 getFontConfigManager().initFontConfigFonts(false); 777 FontConfigManager.FcCompFont[] fontConfigFonts = 778 getFontConfigManager().getFontConfigFonts(); 779 for (int i=0; i<fontConfigFonts.length; i++) { 780 if ("sans".equals(fontConfigFonts[i].fcFamily) && 781 0 == fontConfigFonts[i].style) { 782 info[0] = fontConfigFonts[i].firstFont.familyName; 783 info[1] = fontConfigFonts[i].firstFont.fontFile; 784 break; 785 } 786 } 787 /* Absolute last ditch attempt in the face of fontconfig problems. 788 * If we didn't match, pick the first, or just make something 789 * up so we don't NPE. 790 */ 791 if (info[0] == null) { 792 if (fontConfigFonts.length > 0 && 793 fontConfigFonts[0].firstFont.fontFile != null) { 794 info[0] = fontConfigFonts[0].firstFont.familyName; 795 info[1] = fontConfigFonts[0].firstFont.fontFile; 796 } else { 797 info[0] = "Dialog"; 798 info[1] = "/dialog.ttf"; 799 } 800 } 801 defaultPlatformFont = info; 802 return defaultPlatformFont; 803 } 804 805 public synchronized FontConfigManager getFontConfigManager() { 806 807 if (fcManager == null) { 808 fcManager = new FontConfigManager(); 809 } 810 811 return fcManager; 812 } 813 814 @Override 815 protected FontUIResource getFontConfigFUIR(String family, int style, int size) { 816 817 CompositeFont font2D = getFontConfigManager().getFontConfigFont(family, style); 818 819 if (font2D == null) { // Not expected, just a precaution. 820 return new FontUIResource(family, style, size); 821 } 822 823 /* The name of the font will be that of the physical font in slot, 824 * but by setting the handle to that of the CompositeFont it 825 * renders as that CompositeFont. 826 * It also needs to be marked as a created font which is the 827 * current mechanism to signal that deriveFont etc must copy 828 * the handle from the original font. 829 */ 830 FontUIResource fuir = 831 new FontUIResource(font2D.getFamilyName(null), style, size); 832 FontAccess.getFontAccess().setFont2D(fuir, font2D.handle); 833 FontAccess.getFontAccess().setCreatedFont(fuir); 834 return fuir; 835 } 836 }