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