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