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