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 }