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