1 /*
   2  * Copyright (c) 2008, 2015, 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.font;
  27 
  28 import java.awt.Font;
  29 import java.awt.FontFormatException;
  30 import java.io.BufferedReader;
  31 import java.io.File;
  32 import java.io.FileInputStream;
  33 import java.io.FilenameFilter;
  34 import java.io.IOException;
  35 import java.io.InputStreamReader;
  36 import java.security.AccessController;
  37 import java.security.PrivilegedAction;
  38 import java.util.ArrayList;
  39 import java.util.HashMap;
  40 import java.util.HashSet;
  41 import java.util.Hashtable;
  42 import java.util.Iterator;
  43 import java.util.List;
  44 import java.util.Locale;
  45 import java.util.Map;
  46 import java.util.NoSuchElementException;
  47 import java.util.StringTokenizer;
  48 import java.util.TreeMap;
  49 import java.util.Vector;
  50 import java.util.concurrent.ConcurrentHashMap;
  51 
  52 import javax.swing.plaf.FontUIResource;
  53 import sun.awt.AppContext;
  54 import sun.awt.FontConfiguration;
  55 import sun.awt.SunToolkit;
  56 import sun.awt.util.ThreadGroupUtils;
  57 import sun.java2d.FontSupport;
  58 import sun.util.logging.PlatformLogger;
  59 
  60 /**
  61  * The base implementation of the {@link FontManager} interface. It implements
  62  * the platform independent, shared parts of OpenJDK's FontManager
  63  * implementations. The platform specific parts are declared as abstract
  64  * methods that have to be implemented by specific implementations.
  65  */
  66 public abstract class SunFontManager implements FontSupport, FontManagerForSGE {
  67 
  68     private static class TTFilter implements FilenameFilter {
  69         public boolean accept(File dir,String name) {
  70             /* all conveniently have the same suffix length */
  71             int offset = name.length()-4;
  72             if (offset <= 0) { /* must be at least A.ttf */
  73                 return false;
  74             } else {
  75                 return(name.startsWith(".ttf", offset) ||
  76                        name.startsWith(".TTF", offset) ||
  77                        name.startsWith(".ttc", offset) ||
  78                        name.startsWith(".TTC", offset) ||
  79                        name.startsWith(".otf", offset) ||
  80                        name.startsWith(".OTF", offset));
  81             }
  82         }
  83     }
  84 
  85     private static class T1Filter implements FilenameFilter {
  86         public boolean accept(File dir,String name) {
  87             if (noType1Font) {
  88                 return false;
  89             }
  90             /* all conveniently have the same suffix length */
  91             int offset = name.length()-4;
  92             if (offset <= 0) { /* must be at least A.pfa */
  93                 return false;
  94             } else {
  95                 return(name.startsWith(".pfa", offset) ||
  96                        name.startsWith(".pfb", offset) ||
  97                        name.startsWith(".PFA", offset) ||
  98                        name.startsWith(".PFB", offset));
  99             }
 100         }
 101     }
 102 
 103      private static class TTorT1Filter implements FilenameFilter {
 104         public boolean accept(File dir, String name) {
 105 
 106             /* all conveniently have the same suffix length */
 107             int offset = name.length()-4;
 108             if (offset <= 0) { /* must be at least A.ttf or A.pfa */
 109                 return false;
 110             } else {
 111                 boolean isTT =
 112                     name.startsWith(".ttf", offset) ||
 113                     name.startsWith(".TTF", offset) ||
 114                     name.startsWith(".ttc", offset) ||
 115                     name.startsWith(".TTC", offset) ||
 116                     name.startsWith(".otf", offset) ||
 117                     name.startsWith(".OTF", offset);
 118                 if (isTT) {
 119                     return true;
 120                 } else if (noType1Font) {
 121                     return false;
 122                 } else {
 123                     return(name.startsWith(".pfa", offset) ||
 124                            name.startsWith(".pfb", offset) ||
 125                            name.startsWith(".PFA", offset) ||
 126                            name.startsWith(".PFB", offset));
 127                 }
 128             }
 129         }
 130     }
 131 
 132      public static final int FONTFORMAT_NONE = -1;
 133      public static final int FONTFORMAT_TRUETYPE = 0;
 134      public static final int FONTFORMAT_TYPE1 = 1;
 135      public static final int FONTFORMAT_T2K = 2;
 136      public static final int FONTFORMAT_TTC = 3;
 137      public static final int FONTFORMAT_COMPOSITE = 4;
 138      public static final int FONTFORMAT_NATIVE = 5;
 139 
 140      /* Pool of 20 font file channels chosen because some UTF-8 locale
 141       * composite fonts can use up to 16 platform fonts (including the
 142       * Lucida fall back). This should prevent channel thrashing when
 143       * dealing with one of these fonts.
 144       * The pool array stores the fonts, rather than directly referencing
 145       * the channels, as the font needs to do the open/close work.
 146       */
 147      // MACOSX begin -- need to access these in subclass
 148      protected static final int CHANNELPOOLSIZE = 20;
 149      protected FileFont fontFileCache[] = new FileFont[CHANNELPOOLSIZE];
 150      // MACOSX end
 151      private int lastPoolIndex = 0;
 152 
 153     /* Need to implement a simple linked list scheme for fast
 154      * traversal and lookup.
 155      * Also want to "fast path" dialog so there's minimal overhead.
 156      */
 157     /* There are at exactly 20 composite fonts: 5 faces (but some are not
 158      * usually different), in 4 styles. The array may be auto-expanded
 159      * later if more are needed, eg for user-defined composites or locale
 160      * variants.
 161      */
 162     private int maxCompFont = 0;
 163     private CompositeFont [] compFonts = new CompositeFont[20];
 164     private ConcurrentHashMap<String, CompositeFont>
 165         compositeFonts = new ConcurrentHashMap<String, CompositeFont>();
 166     private ConcurrentHashMap<String, PhysicalFont>
 167         physicalFonts = new ConcurrentHashMap<String, PhysicalFont>();
 168     private ConcurrentHashMap<String, PhysicalFont>
 169         registeredFonts = new ConcurrentHashMap<String, PhysicalFont>();
 170 
 171     /* given a full name find the Font. Remind: there's duplication
 172      * here in that this contains the content of compositeFonts +
 173      * physicalFonts.
 174      */
 175     // MACOSX begin -- need to access this in subclass
 176     protected ConcurrentHashMap<String, Font2D>
 177         fullNameToFont = new ConcurrentHashMap<String, Font2D>();
 178     // MACOSX end
 179 
 180     /* TrueType fonts have localised names. Support searching all
 181      * of these before giving up on a name.
 182      */
 183     private HashMap<String, TrueTypeFont> localeFullNamesToFont;
 184 
 185     private PhysicalFont defaultPhysicalFont;
 186 
 187     static boolean longAddresses;
 188     private boolean loaded1dot0Fonts = false;
 189     boolean loadedAllFonts = false;
 190     boolean loadedAllFontFiles = false;
 191     HashMap<String,String> jreFontMap;
 192     HashSet<String> jreLucidaFontFiles;
 193     String[] jreOtherFontFiles;
 194     boolean noOtherJREFontFiles = false; // initial assumption.
 195 
 196     public static final String lucidaFontName = "Lucida Sans Regular";
 197     public static String jreLibDirName;
 198     public static String jreFontDirName;
 199     private static HashSet<String> missingFontFiles = null;
 200     private String defaultFontName;
 201     private String defaultFontFileName;
 202     protected HashSet<String> registeredFontFiles = new HashSet<>();
 203 
 204     private ArrayList<String> badFonts;
 205     /* fontPath is the location of all fonts on the system, excluding the
 206      * JRE's own font directory but including any path specified using the
 207      * sun.java2d.fontpath property. Together with that property,  it is
 208      * initialised by the getPlatformFontPath() method
 209      * This call must be followed by a call to registerFontDirs(fontPath)
 210      * once any extra debugging path has been appended.
 211      */
 212     protected String fontPath;
 213     private FontConfiguration fontConfig;
 214     /* discoveredAllFonts is set to true when all fonts on the font path are
 215      * discovered. This usually also implies opening, validating and
 216      * registering, but an implementation may be optimized to avold this.
 217      * So see also "loadedAllFontFiles"
 218      */
 219     private boolean discoveredAllFonts = false;
 220 
 221     /* No need to keep consing up new instances - reuse a singleton.
 222      * The trade-off is that these objects don't get GC'd.
 223      */
 224     private static final FilenameFilter ttFilter = new TTFilter();
 225     private static final FilenameFilter t1Filter = new T1Filter();
 226 
 227     private Font[] allFonts;
 228     private String[] allFamilies; // cache for default locale only
 229     private Locale lastDefaultLocale;
 230 
 231     public static boolean noType1Font;
 232 
 233     /* Used to indicate required return type from toArray(..); */
 234     private static String[] STR_ARRAY = new String[0];
 235 
 236     /**
 237      * Deprecated, unsupported hack - actually invokes a bug!
 238      * Left in for a customer, don't remove.
 239      */
 240     private boolean usePlatformFontMetrics = false;
 241 
 242     /**
 243      * Returns the global SunFontManager instance. This is similar to
 244      * {@link FontManagerFactory#getInstance()} but it returns a
 245      * SunFontManager instance instead. This is only used in internal classes
 246      * where we can safely assume that a SunFontManager is to be used.
 247      *
 248      * @return the global SunFontManager instance
 249      */
 250     public static SunFontManager getInstance() {
 251         FontManager fm = FontManagerFactory.getInstance();
 252         return (SunFontManager) fm;
 253     }
 254 
 255     public FilenameFilter getTrueTypeFilter() {
 256         return ttFilter;
 257     }
 258 
 259     public FilenameFilter getType1Filter() {
 260         return t1Filter;
 261     }
 262 
 263     @Override
 264     public boolean usingPerAppContextComposites() {
 265         return _usingPerAppContextComposites;
 266     }
 267 
 268     private void initJREFontMap() {
 269 
 270         /* Key is familyname+style value as an int.
 271          * Value is filename containing the font.
 272          * If no mapping exists, it means there is no font file for the style
 273          * If the mapping exists but the file doesn't exist in the deferred
 274          * list then it means its not installed.
 275          * This looks like a lot of code and strings but if it saves even
 276          * a single file being opened at JRE start-up there's a big payoff.
 277          * Lucida Sans is probably the only important case as the others
 278          * are rarely used. Consider removing the other mappings if there's
 279          * no evidence they are useful in practice.
 280          */
 281         jreFontMap = new HashMap<String,String>();
 282         jreLucidaFontFiles = new HashSet<String>();
 283         if (isOpenJDK()) {
 284             return;
 285         }
 286         /* Lucida Sans Family */
 287         jreFontMap.put("lucida sans0",   "LucidaSansRegular.ttf");
 288         jreFontMap.put("lucida sans1",   "LucidaSansDemiBold.ttf");
 289         /* Lucida Sans full names (map Bold and DemiBold to same file) */
 290         jreFontMap.put("lucida sans regular0", "LucidaSansRegular.ttf");
 291         jreFontMap.put("lucida sans regular1", "LucidaSansDemiBold.ttf");
 292         jreFontMap.put("lucida sans bold1", "LucidaSansDemiBold.ttf");
 293         jreFontMap.put("lucida sans demibold1", "LucidaSansDemiBold.ttf");
 294 
 295         /* Lucida Sans Typewriter Family */
 296         jreFontMap.put("lucida sans typewriter0",
 297                        "LucidaTypewriterRegular.ttf");
 298         jreFontMap.put("lucida sans typewriter1", "LucidaTypewriterBold.ttf");
 299         /* Typewriter full names (map Bold and DemiBold to same file) */
 300         jreFontMap.put("lucida sans typewriter regular0",
 301                        "LucidaTypewriter.ttf");
 302         jreFontMap.put("lucida sans typewriter regular1",
 303                        "LucidaTypewriterBold.ttf");
 304         jreFontMap.put("lucida sans typewriter bold1",
 305                        "LucidaTypewriterBold.ttf");
 306         jreFontMap.put("lucida sans typewriter demibold1",
 307                        "LucidaTypewriterBold.ttf");
 308 
 309         /* Lucida Bright Family */
 310         jreFontMap.put("lucida bright0", "LucidaBrightRegular.ttf");
 311         jreFontMap.put("lucida bright1", "LucidaBrightDemiBold.ttf");
 312         jreFontMap.put("lucida bright2", "LucidaBrightItalic.ttf");
 313         jreFontMap.put("lucida bright3", "LucidaBrightDemiItalic.ttf");
 314         /* Lucida Bright full names (map Bold and DemiBold to same file) */
 315         jreFontMap.put("lucida bright regular0", "LucidaBrightRegular.ttf");
 316         jreFontMap.put("lucida bright regular1", "LucidaBrightDemiBold.ttf");
 317         jreFontMap.put("lucida bright regular2", "LucidaBrightItalic.ttf");
 318         jreFontMap.put("lucida bright regular3", "LucidaBrightDemiItalic.ttf");
 319         jreFontMap.put("lucida bright bold1", "LucidaBrightDemiBold.ttf");
 320         jreFontMap.put("lucida bright bold3", "LucidaBrightDemiItalic.ttf");
 321         jreFontMap.put("lucida bright demibold1", "LucidaBrightDemiBold.ttf");
 322         jreFontMap.put("lucida bright demibold3","LucidaBrightDemiItalic.ttf");
 323         jreFontMap.put("lucida bright italic2", "LucidaBrightItalic.ttf");
 324         jreFontMap.put("lucida bright italic3", "LucidaBrightDemiItalic.ttf");
 325         jreFontMap.put("lucida bright bold italic3",
 326                        "LucidaBrightDemiItalic.ttf");
 327         jreFontMap.put("lucida bright demibold italic3",
 328                        "LucidaBrightDemiItalic.ttf");
 329         for (String ffile : jreFontMap.values()) {
 330             jreLucidaFontFiles.add(ffile);
 331         }
 332     }
 333 
 334     static {
 335 
 336         java.security.AccessController.doPrivileged(
 337                                     new java.security.PrivilegedAction<Object>() {
 338 
 339            public Object run() {
 340                FontManagerNativeLibrary.load();
 341 
 342                // JNI throws an exception if a class/method/field is not found,
 343                // so there's no need to do anything explicit here.
 344                initIDs();
 345 
 346                switch (StrikeCache.nativeAddressSize) {
 347                case 8: longAddresses = true; break;
 348                case 4: longAddresses = false; break;
 349                default: throw new RuntimeException("Unexpected address size");
 350                }
 351 
 352                noType1Font =
 353                    "true".equals(System.getProperty("sun.java2d.noType1Font"));
 354                jreLibDirName =
 355                    System.getProperty("java.home","") + File.separator + "lib";
 356                jreFontDirName = jreLibDirName + File.separator + "fonts";
 357                File lucidaFile =
 358                    new File(jreFontDirName + File.separator + FontUtilities.LUCIDA_FILE_NAME);
 359 
 360                return null;
 361            }
 362         });
 363     }
 364 
 365     /**
 366      * If the module image layout changes the location of JDK fonts,
 367      * this will be updated to reflect that.
 368      */
 369     public static final String getJDKFontDir() {
 370         return jreFontDirName;
 371     }
 372 
 373     public TrueTypeFont getEUDCFont() {
 374         // Overridden in Windows.
 375         return null;
 376     }
 377 
 378     /* Initialise ptrs used by JNI methods */
 379     private static native void initIDs();
 380 
 381     @SuppressWarnings("unchecked")
 382     protected SunFontManager() {
 383 
 384         initJREFontMap();
 385         java.security.AccessController.doPrivileged(
 386                 new java.security.PrivilegedAction<Object>() {
 387                     public Object run() {
 388                         File badFontFile =
 389                             new File(jreFontDirName + File.separator +
 390                                      "badfonts.txt");
 391                         if (badFontFile.exists()) {
 392                             FileInputStream fis = null;
 393                             try {
 394                                 badFonts = new ArrayList<>();
 395                                 fis = new FileInputStream(badFontFile);
 396                                 InputStreamReader isr = new InputStreamReader(fis);
 397                                 BufferedReader br = new BufferedReader(isr);
 398                                 while (true) {
 399                                     String name = br.readLine();
 400                                     if (name == null) {
 401                                         break;
 402                                     } else {
 403                                         if (FontUtilities.debugFonts()) {
 404                                             FontUtilities.getLogger().warning("read bad font: " +
 405                                                            name);
 406                                         }
 407                                         badFonts.add(name);
 408                                     }
 409                                 }
 410                             } catch (IOException e) {
 411                                 try {
 412                                     if (fis != null) {
 413                                         fis.close();
 414                                     }
 415                                 } catch (IOException ioe) {
 416                                 }
 417                             }
 418                         }
 419 
 420                         /* Here we get the fonts in jre/lib/fonts and register
 421                          * them so they are always available and preferred over
 422                          * other fonts. This needs to be registered before the
 423                          * composite fonts as otherwise some native font that
 424                          * corresponds may be found as we don't have a way to
 425                          * handle two fonts of the same name, so the JRE one
 426                          * must be the first one registered. Pass "true" to
 427                          * registerFonts method as on-screen these JRE fonts
 428                          * always go through the T2K rasteriser.
 429                          */
 430                         if (FontUtilities.isLinux) {
 431                             /* Linux font configuration uses these fonts */
 432                             registerFontDir(jreFontDirName);
 433                         }
 434                         registerFontsInDir(jreFontDirName, true, Font2D.JRE_RANK,
 435                                            true, false);
 436 
 437                         /* Create the font configuration and get any font path
 438                          * that might be specified.
 439                          */
 440                         fontConfig = createFontConfiguration();
 441                         if (isOpenJDK()) {
 442                             String[] fontInfo = getDefaultPlatformFont();
 443                             defaultFontName = fontInfo[0];
 444                             defaultFontFileName = fontInfo[1];
 445                         }
 446 
 447                         String extraFontPath = fontConfig.getExtraFontPath();
 448 
 449                         /* In prior releases the debugging font path replaced
 450                          * all normally located font directories except for the
 451                          * JRE fonts dir. This directory is still always located
 452                          * and placed at the head of the path but as an
 453                          * augmentation to the previous behaviour the
 454                          * changes below allow you to additionally append to
 455                          * the font path by starting with append: or prepend by
 456                          * starting with a prepend: sign. Eg: to append
 457                          * -Dsun.java2d.fontpath=append:/usr/local/myfonts
 458                          * and to prepend
 459                          * -Dsun.java2d.fontpath=prepend:/usr/local/myfonts Disp
 460                          *
 461                          * If there is an appendedfontpath it in the font
 462                          * configuration it is used instead of searching the
 463                          * system for dirs.
 464                          * The behaviour of append and prepend is then similar
 465                          * to the normal case. ie it goes after what
 466                          * you prepend and * before what you append. If the
 467                          * sun.java2d.fontpath property is used, but it
 468                          * neither the append or prepend syntaxes is used then
 469                          * as except for the JRE dir the path is replaced and it
 470                          * is up to you to make sure that all the right
 471                          * directories are located. This is platform and
 472                          * locale-specific so its almost impossible to get
 473                          * right, so it should be used with caution.
 474                          */
 475                         boolean prependToPath = false;
 476                         boolean appendToPath = false;
 477                         String dbgFontPath =
 478                             System.getProperty("sun.java2d.fontpath");
 479 
 480                         if (dbgFontPath != null) {
 481                             if (dbgFontPath.startsWith("prepend:")) {
 482                                 prependToPath = true;
 483                                 dbgFontPath =
 484                                     dbgFontPath.substring("prepend:".length());
 485                             } else if (dbgFontPath.startsWith("append:")) {
 486                                 appendToPath = true;
 487                                 dbgFontPath =
 488                                     dbgFontPath.substring("append:".length());
 489                             }
 490                         }
 491 
 492                         if (FontUtilities.debugFonts()) {
 493                             PlatformLogger logger = FontUtilities.getLogger();
 494                             logger.info("JRE font directory: " + jreFontDirName);
 495                             logger.info("Extra font path: " + extraFontPath);
 496                             logger.info("Debug font path: " + dbgFontPath);
 497                         }
 498 
 499                         if (dbgFontPath != null) {
 500                             /* In debugging mode we register all the paths
 501                              * Caution: this is a very expensive call on Solaris:-
 502                              */
 503                             fontPath = getPlatformFontPath(noType1Font);
 504 
 505                             if (extraFontPath != null) {
 506                                 fontPath =
 507                                     extraFontPath + File.pathSeparator + fontPath;
 508                             }
 509                             if (appendToPath) {
 510                                 fontPath =
 511                                     fontPath + File.pathSeparator + dbgFontPath;
 512                             } else if (prependToPath) {
 513                                 fontPath =
 514                                     dbgFontPath + File.pathSeparator + fontPath;
 515                             } else {
 516                                 fontPath = dbgFontPath;
 517                             }
 518                             registerFontDirs(fontPath);
 519                         } else if (extraFontPath != null) {
 520                             /* If the font configuration contains an
 521                              * "appendedfontpath" entry, it is interpreted as a
 522                              * set of locations that should always be registered.
 523                              * It may be additional to locations normally found
 524                              * for that place, or it may be locations that need
 525                              * to have all their paths registered to locate all
 526                              * the needed platform names.
 527                              * This is typically when the same .TTF file is
 528                              * referenced from multiple font.dir files and all
 529                              * of these must be read to find all the native
 530                              * (XLFD) names for the font, so that X11 font APIs
 531                              * can be used for as many code points as possible.
 532                              */
 533                             registerFontDirs(extraFontPath);
 534                         }
 535 
 536                         /* On Solaris, we need to register the Japanese TrueType
 537                          * directory so that we can find the corresponding
 538                          * bitmap fonts. This could be done by listing the
 539                          * directory in the font configuration file, but we
 540                          * don't want to confuse users with this quirk. There
 541                          * are no bitmap fonts for other writing systems that
 542                          * correspond to TrueType fonts and have matching XLFDs.
 543                          * We need to register the bitmap fonts only in
 544                          * environments where they're on the X font path, i.e.,
 545                          * in the Japanese locale. Note that if the X Toolkit
 546                          * is in use the font path isn't set up by JDK, but
 547                          * users of a JA locale should have it
 548                          * set up already by their login environment.
 549                          */
 550                         if (FontUtilities.isSolaris && Locale.JAPAN.equals(Locale.getDefault())) {
 551                             registerFontDir("/usr/openwin/lib/locale/ja/X11/fonts/TT");
 552                         }
 553 
 554                         initCompositeFonts(fontConfig, null);
 555 
 556                         return null;
 557                     }
 558                 });
 559 
 560         boolean platformFont = AccessController.doPrivileged(
 561                         new PrivilegedAction<Boolean>() {
 562                                 public Boolean run() {
 563                                         String prop =
 564                                                 System.getProperty("java2d.font.usePlatformFont");
 565                                         String env = System.getenv("JAVA2D_USEPLATFORMFONT");
 566                                         return "true".equals(prop) || env != null;
 567                                 }
 568                         });
 569 
 570         if (platformFont) {
 571             usePlatformFontMetrics = true;
 572             System.out.println("Enabling platform font metrics for win32. This is an unsupported option.");
 573             System.out.println("This yields incorrect composite font metrics as reported by 1.1.x releases.");
 574             System.out.println("It is appropriate only for use by applications which do not use any Java 2");
 575             System.out.println("functionality. This property will be removed in a later release.");
 576         }
 577     }
 578 
 579     public Font2DHandle getNewComposite(String family, int style,
 580                                         Font2DHandle handle) {
 581 
 582         if (!(handle.font2D instanceof CompositeFont)) {
 583             return handle;
 584         }
 585 
 586         CompositeFont oldComp = (CompositeFont)handle.font2D;
 587         PhysicalFont oldFont = oldComp.getSlotFont(0);
 588 
 589         if (family == null) {
 590             family = oldFont.getFamilyName(null);
 591         }
 592         if (style == -1) {
 593             style = oldComp.getStyle();
 594         }
 595 
 596         Font2D newFont = findFont2D(family, style, NO_FALLBACK);
 597         if (!(newFont instanceof PhysicalFont)) {
 598             newFont = oldFont;
 599         }
 600         PhysicalFont physicalFont = (PhysicalFont)newFont;
 601         CompositeFont dialog2D =
 602             (CompositeFont)findFont2D("dialog", style, NO_FALLBACK);
 603         if (dialog2D == null) { /* shouldn't happen */
 604             return handle;
 605         }
 606         CompositeFont compFont = new CompositeFont(physicalFont, dialog2D);
 607         Font2DHandle newHandle = new Font2DHandle(compFont);
 608         return newHandle;
 609     }
 610 
 611     protected void registerCompositeFont(String compositeName,
 612                                       String[] componentFileNames,
 613                                       String[] componentNames,
 614                                       int numMetricsSlots,
 615                                       int[] exclusionRanges,
 616                                       int[] exclusionMaxIndex,
 617                                       boolean defer) {
 618 
 619         CompositeFont cf = new CompositeFont(compositeName,
 620                                              componentFileNames,
 621                                              componentNames,
 622                                              numMetricsSlots,
 623                                              exclusionRanges,
 624                                              exclusionMaxIndex, defer, this);
 625         addCompositeToFontList(cf, Font2D.FONT_CONFIG_RANK);
 626         synchronized (compFonts) {
 627             compFonts[maxCompFont++] = cf;
 628         }
 629     }
 630 
 631     /* This variant is used only when the application specifies
 632      * a variant of composite fonts which prefers locale specific or
 633      * proportional fonts.
 634      */
 635     protected static void registerCompositeFont(String compositeName,
 636                                                 String[] componentFileNames,
 637                                                 String[] componentNames,
 638                                                 int numMetricsSlots,
 639                                                 int[] exclusionRanges,
 640                                                 int[] exclusionMaxIndex,
 641                                                 boolean defer,
 642                                                 ConcurrentHashMap<String, Font2D>
 643                                                 altNameCache) {
 644 
 645         CompositeFont cf = new CompositeFont(compositeName,
 646                                              componentFileNames,
 647                                              componentNames,
 648                                              numMetricsSlots,
 649                                              exclusionRanges,
 650                                              exclusionMaxIndex, defer,
 651                                              SunFontManager.getInstance());
 652 
 653         /* if the cache has an existing composite for this case, make
 654          * its handle point to this new font.
 655          * This ensures that when the altNameCache that is passed in
 656          * is the global mapNameCache - ie we are running as an application -
 657          * that any statically created java.awt.Font instances which already
 658          * have a Font2D instance will have that re-directed to the new Font
 659          * on subsequent uses. This is particularly important for "the"
 660          * default font instance, or similar cases where a UI toolkit (eg
 661          * Swing) has cached a java.awt.Font. Note that if Swing is using
 662          * a custom composite APIs which update the standard composites have
 663          * no effect - this is typically the case only when using the Windows
 664          * L&F where these APIs would conflict with that L&F anyway.
 665          */
 666         Font2D oldFont =altNameCache.get(compositeName.toLowerCase(Locale.ENGLISH));
 667         if (oldFont instanceof CompositeFont) {
 668             oldFont.handle.font2D = cf;
 669         }
 670         altNameCache.put(compositeName.toLowerCase(Locale.ENGLISH), cf);
 671     }
 672 
 673     private void addCompositeToFontList(CompositeFont f, int rank) {
 674 
 675         if (FontUtilities.isLogging()) {
 676             FontUtilities.getLogger().info("Add to Family "+ f.familyName +
 677                         ", Font " + f.fullName + " rank="+rank);
 678         }
 679         f.setRank(rank);
 680         compositeFonts.put(f.fullName, f);
 681         fullNameToFont.put(f.fullName.toLowerCase(Locale.ENGLISH), f);
 682 
 683         FontFamily family = FontFamily.getFamily(f.familyName);
 684         if (family == null) {
 685             family = new FontFamily(f.familyName, true, rank);
 686         }
 687         family.setFont(f, f.style);
 688     }
 689 
 690     /*
 691      * Systems may have fonts with the same name.
 692      * We want to register only one of such fonts (at least until
 693      * such time as there might be APIs which can accommodate > 1).
 694      * Rank is 1) font configuration fonts, 2) JRE fonts, 3) OT/TT fonts,
 695      * 4) Type1 fonts, 5) native fonts.
 696      *
 697      * If the new font has the same name as the old font, the higher
 698      * ranked font gets added, replacing the lower ranked one.
 699      * If the fonts are of equal rank, then make a special case of
 700      * font configuration rank fonts, which are on closer inspection,
 701      * OT/TT fonts such that the larger font is registered. This is
 702      * a heuristic since a font may be "larger" in the sense of more
 703      * code points, or be a larger "file" because it has more bitmaps.
 704      * So it is possible that using filesize may lead to less glyphs, and
 705      * using glyphs may lead to lower quality display. Probably number
 706      * of glyphs is the ideal, but filesize is information we already
 707      * have and is good enough for the known cases.
 708      * Also don't want to register fonts that match JRE font families
 709      * but are coming from a source other than the JRE.
 710      * This will ensure that we will algorithmically style the JRE
 711      * plain font and get the same set of glyphs for all styles.
 712      *
 713      * Note that this method returns a value
 714      * if it returns the same object as its argument that means this
 715      * font was newly registered.
 716      * If it returns a different object it means this font already exists,
 717      * and you should use that one.
 718      * If it returns null means this font was not registered and none
 719      * in that name is registered. The caller must find a substitute
 720      */
 721     // MACOSX begin -- need to access this in subclass
 722     protected PhysicalFont addToFontList(PhysicalFont f, int rank) {
 723     // MACOSX end
 724 
 725         String fontName = f.fullName;
 726         String familyName = f.familyName;
 727         if (fontName == null || "".equals(fontName)) {
 728             return null;
 729         }
 730         if (compositeFonts.containsKey(fontName)) {
 731             /* Don't register any font that has the same name as a composite */
 732             return null;
 733         }
 734         f.setRank(rank);
 735         if (!physicalFonts.containsKey(fontName)) {
 736             if (FontUtilities.isLogging()) {
 737                 FontUtilities.getLogger().info("Add to Family "+familyName +
 738                             ", Font " + fontName + " rank="+rank);
 739             }
 740             physicalFonts.put(fontName, f);
 741             FontFamily family = FontFamily.getFamily(familyName);
 742             if (family == null) {
 743                 family = new FontFamily(familyName, false, rank);
 744                 family.setFont(f, f.style);
 745             } else {
 746                 family.setFont(f, f.style);
 747             }
 748             fullNameToFont.put(fontName.toLowerCase(Locale.ENGLISH), f);
 749             return f;
 750         } else {
 751             PhysicalFont newFont = f;
 752             PhysicalFont oldFont = physicalFonts.get(fontName);
 753             if (oldFont == null) {
 754                 return null;
 755             }
 756             /* If the new font is of an equal or higher rank, it is a
 757              * candidate to replace the current one, subject to further tests.
 758              */
 759             if (oldFont.getRank() >= rank) {
 760 
 761                 /* All fonts initialise their mapper when first
 762                  * used. If the mapper is non-null then this font
 763                  * has been accessed at least once. In that case
 764                  * do not replace it. This may be overly stringent,
 765                  * but its probably better not to replace a font that
 766                  * someone is already using without a compelling reason.
 767                  * Additionally the primary case where it is known
 768                  * this behaviour is important is in certain composite
 769                  * fonts, and since all the components of a given
 770                  * composite are usually initialised together this
 771                  * is unlikely. For this to be a problem, there would
 772                  * have to be a case where two different composites used
 773                  * different versions of the same-named font, and they
 774                  * were initialised and used at separate times.
 775                  * In that case we continue on and allow the new font to
 776                  * be installed, but replaceFont will continue to allow
 777                  * the original font to be used in Composite fonts.
 778                  */
 779                 if (oldFont.mapper != null && rank > Font2D.FONT_CONFIG_RANK) {
 780                     return oldFont;
 781                 }
 782 
 783                 /* Normally we require a higher rank to replace a font,
 784                  * but as a special case, if the two fonts are the same rank,
 785                  * and are instances of TrueTypeFont we want the
 786                  * more complete (larger) one.
 787                  */
 788                 if (oldFont.getRank() == rank) {
 789                     if (oldFont instanceof TrueTypeFont &&
 790                         newFont instanceof TrueTypeFont) {
 791                         TrueTypeFont oldTTFont = (TrueTypeFont)oldFont;
 792                         TrueTypeFont newTTFont = (TrueTypeFont)newFont;
 793                         if (oldTTFont.fileSize >= newTTFont.fileSize) {
 794                             return oldFont;
 795                         }
 796                     } else {
 797                         return oldFont;
 798                     }
 799                 }
 800                 /* Don't replace ever JRE fonts.
 801                  * This test is in case a font configuration references
 802                  * a Lucida font, which has been mapped to a Lucida
 803                  * from the host O/S. The assumption here is that any
 804                  * such font configuration file is probably incorrect, or
 805                  * the host O/S version is for the use of AWT.
 806                  * In other words if we reach here, there's a possible
 807                  * problem with our choice of font configuration fonts.
 808                  */
 809                 if (oldFont.platName.startsWith(jreFontDirName)) {
 810                     if (FontUtilities.isLogging()) {
 811                         FontUtilities.getLogger()
 812                               .warning("Unexpected attempt to replace a JRE " +
 813                                        " font " + fontName + " from " +
 814                                         oldFont.platName +
 815                                        " with " + newFont.platName);
 816                     }
 817                     return oldFont;
 818                 }
 819 
 820                 if (FontUtilities.isLogging()) {
 821                     FontUtilities.getLogger()
 822                           .info("Replace in Family " + familyName +
 823                                 ",Font " + fontName + " new rank="+rank +
 824                                 " from " + oldFont.platName +
 825                                 " with " + newFont.platName);
 826                 }
 827                 replaceFont(oldFont, newFont);
 828                 physicalFonts.put(fontName, newFont);
 829                 fullNameToFont.put(fontName.toLowerCase(Locale.ENGLISH),
 830                                    newFont);
 831 
 832                 FontFamily family = FontFamily.getFamily(familyName);
 833                 if (family == null) {
 834                     family = new FontFamily(familyName, false, rank);
 835                     family.setFont(newFont, newFont.style);
 836                 } else {
 837                     family.setFont(newFont, newFont.style);
 838                 }
 839                 return newFont;
 840             } else {
 841                 return oldFont;
 842             }
 843         }
 844     }
 845 
 846     public Font2D[] getRegisteredFonts() {
 847         PhysicalFont[] physFonts = getPhysicalFonts();
 848         int mcf = maxCompFont; /* for MT-safety */
 849         Font2D[] regFonts = new Font2D[physFonts.length+mcf];
 850         System.arraycopy(compFonts, 0, regFonts, 0, mcf);
 851         System.arraycopy(physFonts, 0, regFonts, mcf, physFonts.length);
 852         return regFonts;
 853     }
 854 
 855     protected PhysicalFont[] getPhysicalFonts() {
 856         return physicalFonts.values().toArray(new PhysicalFont[0]);
 857     }
 858 
 859 
 860     /* The class FontRegistrationInfo is used when a client says not
 861      * to register a font immediately. This mechanism is used to defer
 862      * initialisation of all the components of composite fonts at JRE
 863      * start-up. The CompositeFont class is "aware" of this and when it
 864      * is first used it asks for the registration of its components.
 865      * Also in the event that any physical font is requested the
 866      * deferred fonts are initialised before triggering a search of the
 867      * system.
 868      * Two maps are used. One to track the deferred fonts. The
 869      * other to track the fonts that have been initialised through this
 870      * mechanism.
 871      */
 872 
 873     private static final class FontRegistrationInfo {
 874 
 875         String fontFilePath;
 876         String[] nativeNames;
 877         int fontFormat;
 878         boolean javaRasterizer;
 879         int fontRank;
 880 
 881         FontRegistrationInfo(String fontPath, String[] names, int format,
 882                              boolean useJavaRasterizer, int rank) {
 883             this.fontFilePath = fontPath;
 884             this.nativeNames = names;
 885             this.fontFormat = format;
 886             this.javaRasterizer = useJavaRasterizer;
 887             this.fontRank = rank;
 888         }
 889     }
 890 
 891     private final ConcurrentHashMap<String, FontRegistrationInfo>
 892         deferredFontFiles =
 893         new ConcurrentHashMap<String, FontRegistrationInfo>();
 894     private final ConcurrentHashMap<String, Font2DHandle>
 895         initialisedFonts = new ConcurrentHashMap<String, Font2DHandle>();
 896 
 897     /* Remind: possibly enhance initialiseDeferredFonts() to be
 898      * optionally given a name and a style and it could stop when it
 899      * finds that font - but this would be a problem if two of the
 900      * fonts reference the same font face name (cf the Solaris
 901      * euro fonts).
 902      */
 903     protected synchronized void initialiseDeferredFonts() {
 904         for (String fileName : deferredFontFiles.keySet()) {
 905             initialiseDeferredFont(fileName);
 906         }
 907     }
 908 
 909     protected synchronized void registerDeferredJREFonts(String jreDir) {
 910         for (FontRegistrationInfo info : deferredFontFiles.values()) {
 911             if (info.fontFilePath != null &&
 912                 info.fontFilePath.startsWith(jreDir)) {
 913                 initialiseDeferredFont(info.fontFilePath);
 914             }
 915         }
 916     }
 917 
 918     public boolean isDeferredFont(String fileName) {
 919         return deferredFontFiles.containsKey(fileName);
 920     }
 921 
 922     /* We keep a map of the files which contain the Lucida fonts so we
 923      * don't need to search for them.
 924      * But since we know what fonts these files contain, we can also avoid
 925      * opening them to look for a font name we don't recognise - see
 926      * findDeferredFont().
 927      * For typical cases where the font isn't a JRE one the overhead is
 928      * this method call, HashMap.get() and null reference test, then
 929      * a boolean test of noOtherJREFontFiles.
 930      */
 931     public
 932     /*private*/ PhysicalFont findJREDeferredFont(String name, int style) {
 933 
 934         PhysicalFont physicalFont;
 935         String nameAndStyle = name.toLowerCase(Locale.ENGLISH) + style;
 936         String fileName = jreFontMap.get(nameAndStyle);
 937         if (fileName != null) {
 938             fileName = jreFontDirName + File.separator + fileName;
 939             if (deferredFontFiles.get(fileName) != null) {
 940                 physicalFont = initialiseDeferredFont(fileName);
 941                 if (physicalFont != null &&
 942                     (physicalFont.getFontName(null).equalsIgnoreCase(name) ||
 943                      physicalFont.getFamilyName(null).equalsIgnoreCase(name))
 944                     && physicalFont.style == style) {
 945                     return physicalFont;
 946                 }
 947             }
 948         }
 949 
 950         /* Iterate over the deferred font files looking for any in the
 951          * jre directory that we didn't recognise, open each of these.
 952          * In almost all installations this will quickly fall through
 953          * because only the Lucidas will be present and jreOtherFontFiles
 954          * will be empty.
 955          * noOtherJREFontFiles is used so we can skip this block as soon
 956          * as its determined that its not needed - almost always after the
 957          * very first time through.
 958          */
 959         if (noOtherJREFontFiles) {
 960             return null;
 961         }
 962         synchronized (jreLucidaFontFiles) {
 963             if (jreOtherFontFiles == null) {
 964                 HashSet<String> otherFontFiles = new HashSet<String>();
 965                 for (String deferredFile : deferredFontFiles.keySet()) {
 966                     File file = new File(deferredFile);
 967                     String dir = file.getParent();
 968                     String fname = file.getName();
 969                     /* skip names which aren't absolute, aren't in the JRE
 970                      * directory, or are known Lucida fonts.
 971                      */
 972                     if (dir == null ||
 973                         !dir.equals(jreFontDirName) ||
 974                         jreLucidaFontFiles.contains(fname)) {
 975                         continue;
 976                     }
 977                     otherFontFiles.add(deferredFile);
 978                 }
 979                 jreOtherFontFiles = otherFontFiles.toArray(STR_ARRAY);
 980                 if (jreOtherFontFiles.length == 0) {
 981                     noOtherJREFontFiles = true;
 982                 }
 983             }
 984 
 985             for (int i=0; i<jreOtherFontFiles.length;i++) {
 986                 fileName = jreOtherFontFiles[i];
 987                 if (fileName == null) {
 988                     continue;
 989                 }
 990                 jreOtherFontFiles[i] = null;
 991                 physicalFont = initialiseDeferredFont(fileName);
 992                 if (physicalFont != null &&
 993                     (physicalFont.getFontName(null).equalsIgnoreCase(name) ||
 994                      physicalFont.getFamilyName(null).equalsIgnoreCase(name))
 995                     && physicalFont.style == style) {
 996                     return physicalFont;
 997                 }
 998             }
 999         }
1000 
1001         return null;
1002     }
1003 
1004     /* This skips JRE installed fonts. */
1005     private PhysicalFont findOtherDeferredFont(String name, int style) {
1006         for (String fileName : deferredFontFiles.keySet()) {
1007             File file = new File(fileName);
1008             String dir = file.getParent();
1009             String fname = file.getName();
1010             if (dir != null &&
1011                 dir.equals(jreFontDirName) &&
1012                 jreLucidaFontFiles.contains(fname)) {
1013                 continue;
1014             }
1015             PhysicalFont physicalFont = initialiseDeferredFont(fileName);
1016             if (physicalFont != null &&
1017                 (physicalFont.getFontName(null).equalsIgnoreCase(name) ||
1018                 physicalFont.getFamilyName(null).equalsIgnoreCase(name)) &&
1019                 physicalFont.style == style) {
1020                 return physicalFont;
1021             }
1022         }
1023         return null;
1024     }
1025 
1026     private PhysicalFont findDeferredFont(String name, int style) {
1027 
1028         PhysicalFont physicalFont = findJREDeferredFont(name, style);
1029         if (physicalFont != null) {
1030             return physicalFont;
1031         } else {
1032             return findOtherDeferredFont(name, style);
1033         }
1034     }
1035 
1036     public void registerDeferredFont(String fileNameKey,
1037                                      String fullPathName,
1038                                      String[] nativeNames,
1039                                      int fontFormat,
1040                                      boolean useJavaRasterizer,
1041                                      int fontRank) {
1042         FontRegistrationInfo regInfo =
1043             new FontRegistrationInfo(fullPathName, nativeNames, fontFormat,
1044                                      useJavaRasterizer, fontRank);
1045         deferredFontFiles.put(fileNameKey, regInfo);
1046     }
1047 
1048 
1049     public synchronized
1050          PhysicalFont initialiseDeferredFont(String fileNameKey) {
1051 
1052         if (fileNameKey == null) {
1053             return null;
1054         }
1055         if (FontUtilities.isLogging()) {
1056             FontUtilities.getLogger()
1057                             .info("Opening deferred font file " + fileNameKey);
1058         }
1059 
1060         PhysicalFont physicalFont;
1061         FontRegistrationInfo regInfo = deferredFontFiles.get(fileNameKey);
1062         if (regInfo != null) {
1063             deferredFontFiles.remove(fileNameKey);
1064             physicalFont = registerFontFile(regInfo.fontFilePath,
1065                                             regInfo.nativeNames,
1066                                             regInfo.fontFormat,
1067                                             regInfo.javaRasterizer,
1068                                             regInfo.fontRank);
1069 
1070 
1071             if (physicalFont != null) {
1072                 /* Store the handle, so that if a font is bad, we
1073                  * retrieve the substituted font.
1074                  */
1075                 initialisedFonts.put(fileNameKey, physicalFont.handle);
1076             } else {
1077                 initialisedFonts.put(fileNameKey,
1078                                      getDefaultPhysicalFont().handle);
1079             }
1080         } else {
1081             Font2DHandle handle = initialisedFonts.get(fileNameKey);
1082             if (handle == null) {
1083                 /* Probably shouldn't happen, but just in case */
1084                 physicalFont = getDefaultPhysicalFont();
1085             } else {
1086                 physicalFont = (PhysicalFont)(handle.font2D);
1087             }
1088         }
1089         return physicalFont;
1090     }
1091 
1092     public boolean isRegisteredFontFile(String name) {
1093         return registeredFonts.containsKey(name);
1094     }
1095 
1096     public PhysicalFont getRegisteredFontFile(String name) {
1097         return registeredFonts.get(name);
1098     }
1099 
1100     /* Note that the return value from this method is not always
1101      * derived from this file, and may be null. See addToFontList for
1102      * some explanation of this.
1103      */
1104     public PhysicalFont registerFontFile(String fileName,
1105                                          String[] nativeNames,
1106                                          int fontFormat,
1107                                          boolean useJavaRasterizer,
1108                                          int fontRank) {
1109 
1110         PhysicalFont regFont = registeredFonts.get(fileName);
1111         if (regFont != null) {
1112             return regFont;
1113         }
1114 
1115         PhysicalFont physicalFont = null;
1116         try {
1117             String name;
1118 
1119             switch (fontFormat) {
1120 
1121             case FONTFORMAT_TRUETYPE:
1122                 int fn = 0;
1123                 TrueTypeFont ttf;
1124                 do {
1125                     ttf = new TrueTypeFont(fileName, nativeNames, fn++,
1126                                            useJavaRasterizer);
1127                     PhysicalFont pf = addToFontList(ttf, fontRank);
1128                     if (physicalFont == null) {
1129                         physicalFont = pf;
1130                     }
1131                 }
1132                 while (fn < ttf.getFontCount());
1133                 break;
1134 
1135             case FONTFORMAT_TYPE1:
1136                 Type1Font t1f = new Type1Font(fileName, nativeNames);
1137                 physicalFont = addToFontList(t1f, fontRank);
1138                 break;
1139 
1140             case FONTFORMAT_NATIVE:
1141                 NativeFont nf = new NativeFont(fileName, false);
1142                 physicalFont = addToFontList(nf, fontRank);
1143                 break;
1144             default:
1145 
1146             }
1147             if (FontUtilities.isLogging()) {
1148                 FontUtilities.getLogger()
1149                       .info("Registered file " + fileName + " as font " +
1150                             physicalFont + " rank="  + fontRank);
1151             }
1152         } catch (FontFormatException ffe) {
1153             if (FontUtilities.isLogging()) {
1154                 FontUtilities.getLogger().warning("Unusable font: " +
1155                                fileName + " " + ffe.toString());
1156             }
1157         }
1158         if (physicalFont != null &&
1159             fontFormat != FONTFORMAT_NATIVE) {
1160             registeredFonts.put(fileName, physicalFont);
1161         }
1162         return physicalFont;
1163     }
1164 
1165     public void registerFonts(String[] fileNames,
1166                               String[][] nativeNames,
1167                               int fontCount,
1168                               int fontFormat,
1169                               boolean useJavaRasterizer,
1170                               int fontRank, boolean defer) {
1171 
1172         for (int i=0; i < fontCount; i++) {
1173             if (defer) {
1174                 registerDeferredFont(fileNames[i],fileNames[i], nativeNames[i],
1175                                      fontFormat, useJavaRasterizer, fontRank);
1176             } else {
1177                 registerFontFile(fileNames[i], nativeNames[i],
1178                                  fontFormat, useJavaRasterizer, fontRank);
1179             }
1180         }
1181     }
1182 
1183     /*
1184      * This is the Physical font used when some other font on the system
1185      * can't be located. There has to be at least one font or the font
1186      * system is not useful and the graphics environment cannot sustain
1187      * the Java platform.
1188      */
1189     public PhysicalFont getDefaultPhysicalFont() {
1190         if (defaultPhysicalFont == null) {
1191             /* findFont2D will load all fonts before giving up the search.
1192              * If the JRE Lucida isn't found (eg because the JRE fonts
1193              * directory is missing), it could find another version of Lucida
1194              * from the host system. This is OK because at that point we are
1195              * trying to gracefully handle/recover from a system
1196              * misconfiguration and this is probably a reasonable substitution.
1197              */
1198             defaultPhysicalFont = (PhysicalFont)
1199                 findFont2D("Lucida Sans Regular", Font.PLAIN, NO_FALLBACK);
1200             if (defaultPhysicalFont == null) {
1201                 defaultPhysicalFont = (PhysicalFont)
1202                     findFont2D("Arial", Font.PLAIN, NO_FALLBACK);
1203             }
1204             if (defaultPhysicalFont == null) {
1205                 /* Because of the findFont2D call above, if we reach here, we
1206                  * know all fonts have already been loaded, just accept any
1207                  * match at this point. If this fails we are in real trouble
1208                  * and I don't know how to recover from there being absolutely
1209                  * no fonts anywhere on the system.
1210                  */
1211                 Iterator<PhysicalFont> i = physicalFonts.values().iterator();
1212                 if (i.hasNext()) {
1213                     defaultPhysicalFont = i.next();
1214                 } else {
1215                     throw new Error("Probable fatal error:No fonts found.");
1216                 }
1217             }
1218         }
1219         return defaultPhysicalFont;
1220     }
1221 
1222     public Font2D getDefaultLogicalFont(int style) {
1223         return findFont2D("dialog", style, NO_FALLBACK);
1224     }
1225 
1226     /*
1227      * return String representation of style prepended with "."
1228      * This is useful for performance to avoid unnecessary string operations.
1229      */
1230     private static String dotStyleStr(int num) {
1231         switch(num){
1232           case Font.BOLD:
1233             return ".bold";
1234           case Font.ITALIC:
1235             return ".italic";
1236           case Font.ITALIC | Font.BOLD:
1237             return ".bolditalic";
1238           default:
1239             return ".plain";
1240         }
1241     }
1242 
1243     /* This is implemented only on windows and is called from code that
1244      * executes only on windows. This isn't pretty but its not a precedent
1245      * in this file. This very probably should be cleaned up at some point.
1246      */
1247     protected void
1248         populateFontFileNameMap(HashMap<String,String> fontToFileMap,
1249                                 HashMap<String,String> fontToFamilyNameMap,
1250                                 HashMap<String,ArrayList<String>>
1251                                 familyToFontListMap,
1252                                 Locale locale) {
1253     }
1254 
1255     /* Obtained from Platform APIs (windows only)
1256      * Map from lower-case font full name to basename of font file.
1257      * Eg "arial bold" -> ARIALBD.TTF.
1258      * For TTC files, there is a mapping for each font in the file.
1259      */
1260     private HashMap<String,String> fontToFileMap = null;
1261 
1262     /* Obtained from Platform APIs (windows only)
1263      * Map from lower-case font full name to the name of its font family
1264      * Eg "arial bold" -> "Arial"
1265      */
1266     private HashMap<String,String> fontToFamilyNameMap = null;
1267 
1268     /* Obtained from Platform APIs (windows only)
1269      * Map from a lower-case family name to a list of full names of
1270      * the member fonts, eg:
1271      * "arial" -> ["Arial", "Arial Bold", "Arial Italic","Arial Bold Italic"]
1272      */
1273     private HashMap<String,ArrayList<String>> familyToFontListMap= null;
1274 
1275     /* The directories which contain platform fonts */
1276     private String[] pathDirs = null;
1277 
1278     private boolean haveCheckedUnreferencedFontFiles;
1279 
1280     private String[] getFontFilesFromPath(boolean noType1) {
1281         final FilenameFilter filter;
1282         if (noType1) {
1283             filter = ttFilter;
1284         } else {
1285             filter = new TTorT1Filter();
1286         }
1287         return (String[])AccessController.doPrivileged(new PrivilegedAction<Object>() {
1288             public Object run() {
1289                 if (pathDirs.length == 1) {
1290                     File dir = new File(pathDirs[0]);
1291                     String[] files = dir.list(filter);
1292                     if (files == null) {
1293                         return new String[0];
1294                     }
1295                     for (int f=0; f<files.length; f++) {
1296                         files[f] = files[f].toLowerCase();
1297                     }
1298                     return files;
1299                 } else {
1300                     ArrayList<String> fileList = new ArrayList<String>();
1301                     for (int i = 0; i< pathDirs.length; i++) {
1302                         File dir = new File(pathDirs[i]);
1303                         String[] files = dir.list(filter);
1304                         if (files == null) {
1305                             continue;
1306                         }
1307                         for (int f=0; f<files.length ; f++) {
1308                             fileList.add(files[f].toLowerCase());
1309                         }
1310                     }
1311                     return fileList.toArray(STR_ARRAY);
1312                 }
1313             }
1314         });
1315     }
1316 
1317     /* This is needed since some windows registry names don't match
1318      * the font names.
1319      * - UPC styled font names have a double space, but the
1320      * registry entry mapping to a file doesn't.
1321      * - Marlett is in a hidden file not listed in the registry
1322      * - The registry advertises that the file david.ttf contains a
1323      * font with the full name "David Regular" when in fact its
1324      * just "David".
1325      * Directly fix up these known cases as this is faster.
1326      * If a font which doesn't match these known cases has no file,
1327      * it may be a font that has been temporarily added to the known set
1328      * or it may be an installed font with a missing registry entry.
1329      * Installed fonts are those in the windows font directories.
1330      * Make a best effort attempt to locate these.
1331      * We obtain the list of TrueType fonts in these directories and
1332      * filter out all the font files we already know about from the registry.
1333      * What remains may be "bad" fonts, duplicate fonts, or perhaps the
1334      * missing font(s) we are looking for.
1335      * Open each of these files to find out.
1336      */
1337     private void resolveWindowsFonts() {
1338 
1339         ArrayList<String> unmappedFontNames = null;
1340         for (String font : fontToFamilyNameMap.keySet()) {
1341             String file = fontToFileMap.get(font);
1342             if (file == null) {
1343                 if (font.indexOf("  ") > 0) {
1344                     String newName = font.replaceFirst("  ", " ");
1345                     file = fontToFileMap.get(newName);
1346                     /* If this name exists and isn't for a valid name
1347                      * replace the mapping to the file with this font
1348                      */
1349                     if (file != null &&
1350                         !fontToFamilyNameMap.containsKey(newName)) {
1351                         fontToFileMap.remove(newName);
1352                         fontToFileMap.put(font, file);
1353                     }
1354                 } else if (font.equals("marlett")) {
1355                     fontToFileMap.put(font, "marlett.ttf");
1356                 } else if (font.equals("david")) {
1357                     file = fontToFileMap.get("david regular");
1358                     if (file != null) {
1359                         fontToFileMap.remove("david regular");
1360                         fontToFileMap.put("david", file);
1361                     }
1362                 } else {
1363                     if (unmappedFontNames == null) {
1364                         unmappedFontNames = new ArrayList<String>();
1365                     }
1366                     unmappedFontNames.add(font);
1367                 }
1368             }
1369         }
1370 
1371         if (unmappedFontNames != null) {
1372             HashSet<String> unmappedFontFiles = new HashSet<String>();
1373 
1374             /* Every font key in fontToFileMap ought to correspond to a
1375              * font key in fontToFamilyNameMap. Entries that don't seem
1376              * to correspond are likely fonts that were named differently
1377              * by GDI than in the registry. One known cause of this is when
1378              * Windows has had its regional settings changed so that from
1379              * GDI we get a localised (eg Chinese or Japanese) name for the
1380              * font, but the registry retains the English version of the name
1381              * that corresponded to the "install" locale for windows.
1382              * Since we are in this code block because there are unmapped
1383              * font names, we can look to find unused font->file mappings
1384              * and then open the files to read the names. We don't generally
1385              * want to open font files, as its a performance hit, but this
1386              * occurs only for a small number of fonts on specific system
1387              * configs - ie is believed that a "true" Japanese windows would
1388              * have JA names in the registry too.
1389              * Clone fontToFileMap and remove from the clone all keys which
1390              * match a fontToFamilyNameMap key. What remains maps to the
1391              * files we want to open to find the fonts GDI returned.
1392              * A font in such a file is added to the fontToFileMap after
1393              * checking its one of the unmappedFontNames we are looking for.
1394              * The original name that didn't map is removed from fontToFileMap
1395              * so essentially this "fixes up" fontToFileMap to use the same
1396              * name as GDI.
1397              * Also note that typically the fonts for which this occurs in
1398              * CJK locales are TTC fonts and not all fonts in a TTC may have
1399              * localised names. Eg MSGOTHIC.TTC contains 3 fonts and one of
1400              * them "MS UI Gothic" has no JA name whereas the other two do.
1401              * So not every font in these files is unmapped or new.
1402              */
1403             @SuppressWarnings("unchecked")
1404             HashMap<String,String> ffmapCopy =
1405                 (HashMap<String,String>)(fontToFileMap.clone());
1406             for (String key : fontToFamilyNameMap.keySet()) {
1407                 ffmapCopy.remove(key);
1408             }
1409             for (String key : ffmapCopy.keySet()) {
1410                 unmappedFontFiles.add(ffmapCopy.get(key));
1411                 fontToFileMap.remove(key);
1412             }
1413 
1414             resolveFontFiles(unmappedFontFiles, unmappedFontNames);
1415 
1416             /* If there are still unmapped font names, this means there's
1417              * something that wasn't in the registry. We need to get all
1418              * the font files directly and look at the ones that weren't
1419              * found in the registry.
1420              */
1421             if (unmappedFontNames.size() > 0) {
1422 
1423                 /* getFontFilesFromPath() returns all lower case names.
1424                  * To compare we also need lower case
1425                  * versions of the names from the registry.
1426                  */
1427                 ArrayList<String> registryFiles = new ArrayList<String>();
1428 
1429                 for (String regFile : fontToFileMap.values()) {
1430                     registryFiles.add(regFile.toLowerCase());
1431                 }
1432                 /* We don't look for Type1 files here as windows will
1433                  * not enumerate these, so aren't useful in reconciling
1434                  * GDI's unmapped files. We do find these later when
1435                  * we enumerate all fonts.
1436                  */
1437                 for (String pathFile : getFontFilesFromPath(true)) {
1438                     if (!registryFiles.contains(pathFile)) {
1439                         unmappedFontFiles.add(pathFile);
1440                     }
1441                 }
1442 
1443                 resolveFontFiles(unmappedFontFiles, unmappedFontNames);
1444             }
1445 
1446             /* remove from the set of names that will be returned to the
1447              * user any fonts that can't be mapped to files.
1448              */
1449             if (unmappedFontNames.size() > 0) {
1450                 int sz = unmappedFontNames.size();
1451                 for (int i=0; i<sz; i++) {
1452                     String name = unmappedFontNames.get(i);
1453                     String familyName = fontToFamilyNameMap.get(name);
1454                     if (familyName != null) {
1455                         ArrayList<String> family = familyToFontListMap.get(familyName);
1456                         if (family != null) {
1457                             if (family.size() <= 1) {
1458                                 familyToFontListMap.remove(familyName);
1459                             }
1460                         }
1461                     }
1462                     fontToFamilyNameMap.remove(name);
1463                     if (FontUtilities.isLogging()) {
1464                         FontUtilities.getLogger()
1465                                              .info("No file for font:" + name);
1466                     }
1467                 }
1468             }
1469         }
1470     }
1471 
1472     /**
1473      * In some cases windows may have fonts in the fonts folder that
1474      * don't show up in the registry or in the GDI calls to enumerate fonts.
1475      * The only way to find these is to list the directory. We invoke this
1476      * only in getAllFonts/Families, so most searches for a specific
1477      * font that is satisfied by the GDI/registry calls don't take the
1478      * additional hit of listing the directory. This hit is small enough
1479      * that its not significant in these 'enumerate all the fonts' cases.
1480      * The basic approach is to cross-reference the files windows found
1481      * with the ones in the directory listing approach, and for each
1482      * in the latter list that is missing from the former list, register it.
1483      */
1484     private synchronized void checkForUnreferencedFontFiles() {
1485         if (haveCheckedUnreferencedFontFiles) {
1486             return;
1487         }
1488         haveCheckedUnreferencedFontFiles = true;
1489         if (!FontUtilities.isWindows) {
1490             return;
1491         }
1492         /* getFontFilesFromPath() returns all lower case names.
1493          * To compare we also need lower case
1494          * versions of the names from the registry.
1495          */
1496         ArrayList<String> registryFiles = new ArrayList<String>();
1497         for (String regFile : fontToFileMap.values()) {
1498             registryFiles.add(regFile.toLowerCase());
1499         }
1500 
1501         /* To avoid any issues with concurrent modification, create
1502          * copies of the existing maps, add the new fonts into these
1503          * and then replace the references to the old ones with the
1504          * new maps. ConcurrentHashmap is another option but its a lot
1505          * more changes and with this exception, these maps are intended
1506          * to be static.
1507          */
1508         HashMap<String,String> fontToFileMap2 = null;
1509         HashMap<String,String> fontToFamilyNameMap2 = null;
1510         HashMap<String,ArrayList<String>> familyToFontListMap2 = null;;
1511 
1512         for (String pathFile : getFontFilesFromPath(false)) {
1513             if (!registryFiles.contains(pathFile)) {
1514                 if (FontUtilities.isLogging()) {
1515                     FontUtilities.getLogger()
1516                                  .info("Found non-registry file : " + pathFile);
1517                 }
1518                 PhysicalFont f = registerFontFile(getPathName(pathFile));
1519                 if (f == null) {
1520                     continue;
1521                 }
1522                 if (fontToFileMap2 == null) {
1523                     fontToFileMap2 = new HashMap<String,String>(fontToFileMap);
1524                     fontToFamilyNameMap2 =
1525                         new HashMap<String,String>(fontToFamilyNameMap);
1526                     familyToFontListMap2 = new
1527                         HashMap<String,ArrayList<String>>(familyToFontListMap);
1528                 }
1529                 String fontName = f.getFontName(null);
1530                 String family = f.getFamilyName(null);
1531                 String familyLC = family.toLowerCase();
1532                 fontToFamilyNameMap2.put(fontName, family);
1533                 fontToFileMap2.put(fontName, pathFile);
1534                 ArrayList<String> fonts = familyToFontListMap2.get(familyLC);
1535                 if (fonts == null) {
1536                     fonts = new ArrayList<String>();
1537                 } else {
1538                     fonts = new ArrayList<String>(fonts);
1539                 }
1540                 fonts.add(fontName);
1541                 familyToFontListMap2.put(familyLC, fonts);
1542             }
1543         }
1544         if (fontToFileMap2 != null) {
1545             fontToFileMap = fontToFileMap2;
1546             familyToFontListMap = familyToFontListMap2;
1547             fontToFamilyNameMap = fontToFamilyNameMap2;
1548         }
1549     }
1550 
1551     private void resolveFontFiles(HashSet<String> unmappedFiles,
1552                                   ArrayList<String> unmappedFonts) {
1553 
1554         Locale l = SunToolkit.getStartupLocale();
1555 
1556         for (String file : unmappedFiles) {
1557             try {
1558                 int fn = 0;
1559                 TrueTypeFont ttf;
1560                 String fullPath = getPathName(file);
1561                 if (FontUtilities.isLogging()) {
1562                     FontUtilities.getLogger()
1563                                    .info("Trying to resolve file " + fullPath);
1564                 }
1565                 do {
1566                     ttf = new TrueTypeFont(fullPath, null, fn++, false);
1567                     //  prefer the font's locale name.
1568                     String fontName = ttf.getFontName(l).toLowerCase();
1569                     if (unmappedFonts.contains(fontName)) {
1570                         fontToFileMap.put(fontName, file);
1571                         unmappedFonts.remove(fontName);
1572                         if (FontUtilities.isLogging()) {
1573                             FontUtilities.getLogger()
1574                                   .info("Resolved absent registry entry for " +
1575                                         fontName + " located in " + fullPath);
1576                         }
1577                     }
1578                 }
1579                 while (fn < ttf.getFontCount());
1580             } catch (Exception e) {
1581             }
1582         }
1583     }
1584 
1585     /* Hardwire the English names and expected file names of fonts
1586      * commonly used at start up. Avoiding until later even the small
1587      * cost of calling platform APIs to locate these can help.
1588      * The code that registers these fonts needs to "bail" if any
1589      * of the files do not exist, so it will verify the existence of
1590      * all non-null file names first.
1591      * They are added in to a map with nominally the first
1592      * word in the name of the family as the key. In all the cases
1593      * we are using the family name is a single word, and as is
1594      * more or less required the family name is the initial sequence
1595      * in a full name. So lookup first finds the matching description,
1596      * then registers the whole family, returning the right font.
1597      */
1598     public static class FamilyDescription {
1599         public String familyName;
1600         public String plainFullName;
1601         public String boldFullName;
1602         public String italicFullName;
1603         public String boldItalicFullName;
1604         public String plainFileName;
1605         public String boldFileName;
1606         public String italicFileName;
1607         public String boldItalicFileName;
1608     }
1609 
1610     static HashMap<String, FamilyDescription> platformFontMap;
1611 
1612     /**
1613      * default implementation does nothing.
1614      */
1615     public HashMap<String, FamilyDescription> populateHardcodedFileNameMap() {
1616         return new HashMap<String, FamilyDescription>(0);
1617     }
1618 
1619     Font2D findFontFromPlatformMap(String lcName, int style) {
1620         if (platformFontMap == null) {
1621             platformFontMap = populateHardcodedFileNameMap();
1622         }
1623 
1624         if (platformFontMap == null || platformFontMap.size() == 0) {
1625             return null;
1626         }
1627 
1628         int spaceIndex = lcName.indexOf(' ');
1629         String firstWord = lcName;
1630         if (spaceIndex > 0) {
1631             firstWord = lcName.substring(0, spaceIndex);
1632         }
1633 
1634         FamilyDescription fd = platformFontMap.get(firstWord);
1635         if (fd == null) {
1636             return null;
1637         }
1638         /* Once we've established that its at least the first word,
1639          * we need to dig deeper to make sure its a match for either
1640          * a full name, or the family name, to make sure its not
1641          * a request for some other font that just happens to start
1642          * with the same first word.
1643          */
1644         int styleIndex = -1;
1645         if (lcName.equalsIgnoreCase(fd.plainFullName)) {
1646             styleIndex = 0;
1647         } else if (lcName.equalsIgnoreCase(fd.boldFullName)) {
1648             styleIndex = 1;
1649         } else if (lcName.equalsIgnoreCase(fd.italicFullName)) {
1650             styleIndex = 2;
1651         } else if (lcName.equalsIgnoreCase(fd.boldItalicFullName)) {
1652             styleIndex = 3;
1653         }
1654         if (styleIndex == -1 && !lcName.equalsIgnoreCase(fd.familyName)) {
1655             return null;
1656         }
1657 
1658         String plainFile = null, boldFile = null,
1659             italicFile = null, boldItalicFile = null;
1660 
1661         boolean failure = false;
1662         /* In a terminal server config, its possible that getPathName()
1663          * will return null, if the file doesn't exist, hence the null
1664          * checks on return. But in the normal client config we need to
1665          * follow this up with a check to see if all the files really
1666          * exist for the non-null paths.
1667          */
1668          getPlatformFontDirs(noType1Font);
1669 
1670         if (fd.plainFileName != null) {
1671             plainFile = getPathName(fd.plainFileName);
1672             if (plainFile == null) {
1673                 failure = true;
1674             }
1675         }
1676 
1677         if (fd.boldFileName != null) {
1678             boldFile = getPathName(fd.boldFileName);
1679             if (boldFile == null) {
1680                 failure = true;
1681             }
1682         }
1683 
1684         if (fd.italicFileName != null) {
1685             italicFile = getPathName(fd.italicFileName);
1686             if (italicFile == null) {
1687                 failure = true;
1688             }
1689         }
1690 
1691         if (fd.boldItalicFileName != null) {
1692             boldItalicFile = getPathName(fd.boldItalicFileName);
1693             if (boldItalicFile == null) {
1694                 failure = true;
1695             }
1696         }
1697 
1698         if (failure) {
1699             if (FontUtilities.isLogging()) {
1700                 FontUtilities.getLogger().
1701                     info("Hardcoded file missing looking for " + lcName);
1702             }
1703             platformFontMap.remove(firstWord);
1704             return null;
1705         }
1706 
1707         /* Some of these may be null,as not all styles have to exist */
1708         final String[] files = {
1709             plainFile, boldFile, italicFile, boldItalicFile } ;
1710 
1711         failure = java.security.AccessController.doPrivileged(
1712                  new java.security.PrivilegedAction<Boolean>() {
1713                      public Boolean run() {
1714                          for (int i=0; i<files.length; i++) {
1715                              if (files[i] == null) {
1716                                  continue;
1717                              }
1718                              File f = new File(files[i]);
1719                              if (!f.exists()) {
1720                                  return Boolean.TRUE;
1721                              }
1722                          }
1723                          return Boolean.FALSE;
1724                      }
1725                  });
1726 
1727         if (failure) {
1728             if (FontUtilities.isLogging()) {
1729                 FontUtilities.getLogger().
1730                     info("Hardcoded file missing looking for " + lcName);
1731             }
1732             platformFontMap.remove(firstWord);
1733             return null;
1734         }
1735 
1736         /* If we reach here we know that we have all the files we
1737          * expect, so all should be fine so long as the contents
1738          * are what we'd expect. Now on to registering the fonts.
1739          * Currently this code only looks for TrueType fonts, so format
1740          * and rank can be specified without looking at the filename.
1741          */
1742         Font2D font = null;
1743         for (int f=0;f<files.length;f++) {
1744             if (files[f] == null) {
1745                 continue;
1746             }
1747             PhysicalFont pf =
1748                 registerFontFile(files[f], null,
1749                                  FONTFORMAT_TRUETYPE, false, Font2D.TTF_RANK);
1750             if (f == styleIndex) {
1751                 font = pf;
1752             }
1753         }
1754 
1755 
1756         /* Two general cases need a bit more work here.
1757          * 1) If font is null, then it was perhaps a request for a
1758          * non-existent font, such as "Tahoma Italic", or a family name -
1759          * where family and full name of the plain font differ.
1760          * Fall back to finding the closest one in the family.
1761          * This could still fail if a client specified "Segoe" instead of
1762          * "Segoe UI".
1763          * 2) The request is of the form "MyFont Bold", style=Font.ITALIC,
1764          * and so we want to see if there's a Bold Italic font, or
1765          * "MyFamily", style=Font.BOLD, and we may have matched the plain,
1766          * but now need to revise that to the BOLD font.
1767          */
1768         FontFamily fontFamily = FontFamily.getFamily(fd.familyName);
1769         if (fontFamily != null) {
1770             if (font == null) {
1771                 font = fontFamily.getFont(style);
1772                 if (font == null) {
1773                     font = fontFamily.getClosestStyle(style);
1774                 }
1775             } else if (style > 0 && style != font.style) {
1776                 style |= font.style;
1777                 font = fontFamily.getFont(style);
1778                 if (font == null) {
1779                     font = fontFamily.getClosestStyle(style);
1780                 }
1781             }
1782         }
1783 
1784         return font;
1785     }
1786     private synchronized HashMap<String,String> getFullNameToFileMap() {
1787         if (fontToFileMap == null) {
1788 
1789             pathDirs = getPlatformFontDirs(noType1Font);
1790 
1791             fontToFileMap = new HashMap<String,String>(100);
1792             fontToFamilyNameMap = new HashMap<String,String>(100);
1793             familyToFontListMap = new HashMap<String,ArrayList<String>>(50);
1794             populateFontFileNameMap(fontToFileMap,
1795                                     fontToFamilyNameMap,
1796                                     familyToFontListMap,
1797                                     Locale.ENGLISH);
1798             if (FontUtilities.isWindows) {
1799                 resolveWindowsFonts();
1800             }
1801             if (FontUtilities.isLogging()) {
1802                 logPlatformFontInfo();
1803             }
1804         }
1805         return fontToFileMap;
1806     }
1807 
1808     private void logPlatformFontInfo() {
1809         PlatformLogger logger = FontUtilities.getLogger();
1810         for (int i=0; i< pathDirs.length;i++) {
1811             logger.info("fontdir="+pathDirs[i]);
1812         }
1813         for (String keyName : fontToFileMap.keySet()) {
1814             logger.info("font="+keyName+" file="+ fontToFileMap.get(keyName));
1815         }
1816         for (String keyName : fontToFamilyNameMap.keySet()) {
1817             logger.info("font="+keyName+" family="+
1818                         fontToFamilyNameMap.get(keyName));
1819         }
1820         for (String keyName : familyToFontListMap.keySet()) {
1821             logger.info("family="+keyName+ " fonts="+
1822                         familyToFontListMap.get(keyName));
1823         }
1824     }
1825 
1826     /* Note this return list excludes logical fonts and JRE fonts */
1827     protected String[] getFontNamesFromPlatform() {
1828         if (getFullNameToFileMap().size() == 0) {
1829             return null;
1830         }
1831         checkForUnreferencedFontFiles();
1832         /* This odd code with TreeMap is used to preserve a historical
1833          * behaviour wrt the sorting order .. */
1834         ArrayList<String> fontNames = new ArrayList<String>();
1835         for (ArrayList<String> a : familyToFontListMap.values()) {
1836             for (String s : a) {
1837                 fontNames.add(s);
1838             }
1839         }
1840         return fontNames.toArray(STR_ARRAY);
1841     }
1842 
1843     public boolean gotFontsFromPlatform() {
1844         return getFullNameToFileMap().size() != 0;
1845     }
1846 
1847     public String getFileNameForFontName(String fontName) {
1848         String fontNameLC = fontName.toLowerCase(Locale.ENGLISH);
1849         return fontToFileMap.get(fontNameLC);
1850     }
1851 
1852     private PhysicalFont registerFontFile(String file) {
1853         if (new File(file).isAbsolute() &&
1854             !registeredFonts.containsKey(file)) {
1855             int fontFormat = FONTFORMAT_NONE;
1856             int fontRank = Font2D.UNKNOWN_RANK;
1857             if (ttFilter.accept(null, file)) {
1858                 fontFormat = FONTFORMAT_TRUETYPE;
1859                 fontRank = Font2D.TTF_RANK;
1860             } else if
1861                 (t1Filter.accept(null, file)) {
1862                 fontFormat = FONTFORMAT_TYPE1;
1863                 fontRank = Font2D.TYPE1_RANK;
1864             }
1865             if (fontFormat == FONTFORMAT_NONE) {
1866                 return null;
1867             }
1868             return registerFontFile(file, null, fontFormat, false, fontRank);
1869         }
1870         return null;
1871     }
1872 
1873     /* Used to register any font files that are found by platform APIs
1874      * that weren't previously found in the standard font locations.
1875      * the isAbsolute() check is needed since that's whats stored in the
1876      * set, and on windows, the fonts in the system font directory that
1877      * are in the fontToFileMap are just basenames. We don't want to try
1878      * to register those again, but we do want to register other registry
1879      * installed fonts.
1880      */
1881     protected void registerOtherFontFiles(HashSet<String> registeredFontFiles) {
1882         if (getFullNameToFileMap().size() == 0) {
1883             return;
1884         }
1885         for (String file : fontToFileMap.values()) {
1886             registerFontFile(file);
1887         }
1888     }
1889 
1890     public boolean
1891         getFamilyNamesFromPlatform(TreeMap<String,String> familyNames,
1892                                    Locale requestedLocale) {
1893         if (getFullNameToFileMap().size() == 0) {
1894             return false;
1895         }
1896         checkForUnreferencedFontFiles();
1897         for (String name : fontToFamilyNameMap.values()) {
1898             familyNames.put(name.toLowerCase(requestedLocale), name);
1899         }
1900         return true;
1901     }
1902 
1903     /* Path may be absolute or a base file name relative to one of
1904      * the platform font directories
1905      */
1906     private String getPathName(final String s) {
1907         File f = new File(s);
1908         if (f.isAbsolute()) {
1909             return s;
1910         } else if (pathDirs.length==1) {
1911             return pathDirs[0] + File.separator + s;
1912         } else {
1913             String path = java.security.AccessController.doPrivileged(
1914                  new java.security.PrivilegedAction<String>() {
1915                      public String run() {
1916                          for (int p=0; p<pathDirs.length; p++) {
1917                              File f = new File(pathDirs[p] +File.separator+ s);
1918                              if (f.exists()) {
1919                                  return f.getAbsolutePath();
1920                              }
1921                          }
1922                          return null;
1923                      }
1924                 });
1925             if (path != null) {
1926                 return path;
1927             }
1928         }
1929         return s; // shouldn't happen, but harmless
1930     }
1931 
1932     /* lcName is required to be lower case for use as a key.
1933      * lcName may be a full name, or a family name, and style may
1934      * be specified in addition to either of these. So be sure to
1935      * get the right one. Since an app *could* ask for "Foo Regular"
1936      * and later ask for "Foo Italic", if we don't register all the
1937      * styles, then logic in findFont2D may try to style the original
1938      * so we register the entire family if we get a match here.
1939      * This is still a big win because this code is invoked where
1940      * otherwise we would register all fonts.
1941      * It's also useful for the case where "Foo Bold" was specified with
1942      * style Font.ITALIC, as we would want in that case to try to return
1943      * "Foo Bold Italic" if it exists, and it is only by locating "Foo Bold"
1944      * and opening it that we really "know" it's Bold, and can look for
1945      * a font that supports that and the italic style.
1946      * The code in here is not overtly windows-specific but in fact it
1947      * is unlikely to be useful as is on other platforms. It is maintained
1948      * in this shared source file to be close to its sole client and
1949      * because so much of the logic is intertwined with the logic in
1950      * findFont2D.
1951      */
1952     private Font2D findFontFromPlatform(String lcName, int style) {
1953         if (getFullNameToFileMap().size() == 0) {
1954             return null;
1955         }
1956 
1957         ArrayList<String> family = null;
1958         String fontFile = null;
1959         String familyName = fontToFamilyNameMap.get(lcName);
1960         if (familyName != null) {
1961             fontFile = fontToFileMap.get(lcName);
1962             family = familyToFontListMap.get
1963                 (familyName.toLowerCase(Locale.ENGLISH));
1964         } else {
1965             family = familyToFontListMap.get(lcName); // is lcName is a family?
1966             if (family != null && family.size() > 0) {
1967                 String lcFontName = family.get(0).toLowerCase(Locale.ENGLISH);
1968                 if (lcFontName != null) {
1969                     familyName = fontToFamilyNameMap.get(lcFontName);
1970                 }
1971             }
1972         }
1973         if (family == null || familyName == null) {
1974             return null;
1975         }
1976         String [] fontList = family.toArray(STR_ARRAY);
1977         if (fontList.length == 0) {
1978             return null;
1979         }
1980 
1981         /* first check that for every font in this family we can find
1982          * a font file. The specific reason for doing this is that
1983          * in at least one case on Windows a font has the face name "David"
1984          * but the registry entry is "David Regular". That is the "unique"
1985          * name of the font but in other cases the registry contains the
1986          * "full" name. See the specifications of name ids 3 and 4 in the
1987          * TrueType 'name' table.
1988          * In general this could cause a problem that we fail to register
1989          * if we all members of a family that we may end up mapping to
1990          * the wrong font member: eg return Bold when Plain is needed.
1991          */
1992         for (int f=0;f<fontList.length;f++) {
1993             String fontNameLC = fontList[f].toLowerCase(Locale.ENGLISH);
1994             String fileName = fontToFileMap.get(fontNameLC);
1995             if (fileName == null) {
1996                 if (FontUtilities.isLogging()) {
1997                     FontUtilities.getLogger()
1998                           .info("Platform lookup : No file for font " +
1999                                 fontList[f] + " in family " +familyName);
2000                 }
2001                 return null;
2002             }
2003         }
2004 
2005         /* Currently this code only looks for TrueType fonts, so format
2006          * and rank can be specified without looking at the filename.
2007          */
2008         PhysicalFont physicalFont = null;
2009         if (fontFile != null) {
2010             physicalFont = registerFontFile(getPathName(fontFile), null,
2011                                             FONTFORMAT_TRUETYPE, false,
2012                                             Font2D.TTF_RANK);
2013         }
2014         /* Register all fonts in this family. */
2015         for (int f=0;f<fontList.length;f++) {
2016             String fontNameLC = fontList[f].toLowerCase(Locale.ENGLISH);
2017             String fileName = fontToFileMap.get(fontNameLC);
2018             if (fontFile != null && fontFile.equals(fileName)) {
2019                 continue;
2020             }
2021             /* Currently this code only looks for TrueType fonts, so format
2022              * and rank can be specified without looking at the filename.
2023              */
2024             registerFontFile(getPathName(fileName), null,
2025                              FONTFORMAT_TRUETYPE, false, Font2D.TTF_RANK);
2026         }
2027 
2028         Font2D font = null;
2029         FontFamily fontFamily = FontFamily.getFamily(familyName);
2030         /* Handle case where request "MyFont Bold", style=Font.ITALIC */
2031         if (physicalFont != null) {
2032             style |= physicalFont.style;
2033         }
2034         if (fontFamily != null) {
2035             font = fontFamily.getFont(style);
2036             if (font == null) {
2037                 font = fontFamily.getClosestStyle(style);
2038             }
2039         }
2040         return font;
2041     }
2042 
2043     private ConcurrentHashMap<String, Font2D> fontNameCache =
2044         new ConcurrentHashMap<String, Font2D>();
2045 
2046     /*
2047      * The client supplies a name and a style.
2048      * The name could be a family name, or a full name.
2049      * A font may exist with the specified style, or it may
2050      * exist only in some other style. For non-native fonts the scaler
2051      * may be able to emulate the required style.
2052      */
2053     public Font2D findFont2D(String name, int style, int fallback) {
2054         String lowerCaseName = name.toLowerCase(Locale.ENGLISH);
2055         String mapName = lowerCaseName + dotStyleStr(style);
2056         Font2D font;
2057 
2058         /* If preferLocaleFonts() or preferProportionalFonts() has been
2059          * called we may be using an alternate set of composite fonts in this
2060          * app context. The presence of a pre-built name map indicates whether
2061          * this is so, and gives access to the alternate composite for the
2062          * name.
2063          */
2064         if (_usingPerAppContextComposites) {
2065             @SuppressWarnings("unchecked")
2066             ConcurrentHashMap<String, Font2D> altNameCache =
2067                 (ConcurrentHashMap<String, Font2D>)
2068                 AppContext.getAppContext().get(CompositeFont.class);
2069             if (altNameCache != null) {
2070                 font = altNameCache.get(mapName);
2071             } else {
2072                 font = null;
2073             }
2074         } else {
2075             font = fontNameCache.get(mapName);
2076         }
2077         if (font != null) {
2078             return font;
2079         }
2080 
2081         if (FontUtilities.isLogging()) {
2082             FontUtilities.getLogger().info("Search for font: " + name);
2083         }
2084 
2085         // The check below is just so that the bitmap fonts being set by
2086         // AWT and Swing thru the desktop properties do not trigger the
2087         // the load fonts case. The two bitmap fonts are now mapped to
2088         // appropriate equivalents for serif and sansserif.
2089         // Note that the cost of this comparison is only for the first
2090         // call until the map is filled.
2091         if (FontUtilities.isWindows) {
2092             if (lowerCaseName.equals("ms sans serif")) {
2093                 name = "sansserif";
2094             } else if (lowerCaseName.equals("ms serif")) {
2095                 name = "serif";
2096             }
2097         }
2098 
2099         /* This isn't intended to support a client passing in the
2100          * string default, but if a client passes in null for the name
2101          * the java.awt.Font class internally substitutes this name.
2102          * So we need to recognise it here to prevent a loadFonts
2103          * on the unrecognised name. The only potential problem with
2104          * this is it would hide any real font called "default"!
2105          * But that seems like a potential problem we can ignore for now.
2106          */
2107         if (lowerCaseName.equals("default")) {
2108             name = "dialog";
2109         }
2110 
2111         /* First see if its a family name. */
2112         FontFamily family = FontFamily.getFamily(name);
2113         if (family != null) {
2114             font = family.getFontWithExactStyleMatch(style);
2115             if (font == null) {
2116                 font = findDeferredFont(name, style);
2117             }
2118             if (font == null) {
2119                 font = family.getFont(style);
2120             }
2121             if (font == null) {
2122                 font = family.getClosestStyle(style);
2123             }
2124             if (font != null) {
2125                 fontNameCache.put(mapName, font);
2126                 return font;
2127             }
2128         }
2129 
2130         /* If it wasn't a family name, it should be a full name of
2131          * either a composite, or a physical font
2132          */
2133         font = fullNameToFont.get(lowerCaseName);
2134         if (font != null) {
2135             /* Check that the requested style matches the matched font's style.
2136              * But also match style automatically if the requested style is
2137              * "plain". This because the existing behaviour is that the fonts
2138              * listed via getAllFonts etc always list their style as PLAIN.
2139              * This does lead to non-commutative behaviours where you might
2140              * start with "Lucida Sans Regular" and ask for a BOLD version
2141              * and get "Lucida Sans DemiBold" but if you ask for the PLAIN
2142              * style of "Lucida Sans DemiBold" you get "Lucida Sans DemiBold".
2143              * This consistent however with what happens if you have a bold
2144              * version of a font and no plain version exists - alg. styling
2145              * doesn't "unbolden" the font.
2146              */
2147             if (font.style == style || style == Font.PLAIN) {
2148                 fontNameCache.put(mapName, font);
2149                 return font;
2150             } else {
2151                 /* If it was a full name like "Lucida Sans Regular", but
2152                  * the style requested is "bold", then we want to see if
2153                  * there's the appropriate match against another font in
2154                  * that family before trying to load all fonts, or applying a
2155                  * algorithmic styling
2156                  */
2157                 family = FontFamily.getFamily(font.getFamilyName(null));
2158                 if (family != null) {
2159                     Font2D familyFont = family.getFont(style|font.style);
2160                     /* We exactly matched the requested style, use it! */
2161                     if (familyFont != null) {
2162                         fontNameCache.put(mapName, familyFont);
2163                         return familyFont;
2164                     } else {
2165                         /* This next call is designed to support the case
2166                          * where bold italic is requested, and if we must
2167                          * style, then base it on either bold or italic -
2168                          * not on plain!
2169                          */
2170                         familyFont = family.getClosestStyle(style|font.style);
2171                         if (familyFont != null) {
2172                             /* The next check is perhaps one
2173                              * that shouldn't be done. ie if we get this
2174                              * far we have probably as close a match as we
2175                              * are going to get. We could load all fonts to
2176                              * see if somehow some parts of the family are
2177                              * loaded but not all of it.
2178                              */
2179                             if (familyFont.canDoStyle(style|font.style)) {
2180                                 fontNameCache.put(mapName, familyFont);
2181                                 return familyFont;
2182                             }
2183                         }
2184                     }
2185                 }
2186             }
2187         }
2188 
2189         if (FontUtilities.isWindows) {
2190 
2191             font = findFontFromPlatformMap(lowerCaseName, style);
2192             if (FontUtilities.isLogging()) {
2193                 FontUtilities.getLogger()
2194                     .info("findFontFromPlatformMap returned " + font);
2195             }
2196             if (font != null) {
2197                 fontNameCache.put(mapName, font);
2198                 return font;
2199             }
2200 
2201             /* Don't want Windows to return a Lucida Sans font from
2202              * C:\Windows\Fonts
2203              */
2204             if (deferredFontFiles.size() > 0) {
2205                 font = findJREDeferredFont(lowerCaseName, style);
2206                 if (font != null) {
2207                     fontNameCache.put(mapName, font);
2208                     return font;
2209                 }
2210             }
2211             font = findFontFromPlatform(lowerCaseName, style);
2212             if (font != null) {
2213                 if (FontUtilities.isLogging()) {
2214                     FontUtilities.getLogger()
2215                           .info("Found font via platform API for request:\"" +
2216                                 name + "\":, style="+style+
2217                                 " found font: " + font);
2218                 }
2219                 fontNameCache.put(mapName, font);
2220                 return font;
2221             }
2222         }
2223 
2224         /* If reach here and no match has been located, then if there are
2225          * uninitialised deferred fonts, load as many of those as needed
2226          * to find the deferred font. If none is found through that
2227          * search continue on.
2228          * There is possibly a minor issue when more than one
2229          * deferred font implements the same font face. Since deferred
2230          * fonts are only those in font configuration files, this is a
2231          * controlled situation, the known case being Solaris euro_fonts
2232          * versions of Arial, Times New Roman, Courier New. However
2233          * the larger font will transparently replace the smaller one
2234          *  - see addToFontList() - when it is needed by the composite font.
2235          */
2236         if (deferredFontFiles.size() > 0) {
2237             font = findDeferredFont(name, style);
2238             if (font != null) {
2239                 fontNameCache.put(mapName, font);
2240                 return font;
2241             }
2242         }
2243 
2244         /* Some apps use deprecated 1.0 names such as helvetica and courier. On
2245          * Solaris these are Type1 fonts in /usr/openwin/lib/X11/fonts/Type1.
2246          * If running on Solaris will register all the fonts in this
2247          * directory.
2248          * May as well register the whole directory without actually testing
2249          * the font name is one of the deprecated names as the next step would
2250          * load all fonts which are in this directory anyway.
2251          * In the event that this lookup is successful it potentially "hides"
2252          * TrueType versions of such fonts that are elsewhere but since they
2253          * do not exist on Solaris this is not a problem.
2254          * Set a flag to indicate we've done this registration to avoid
2255          * repetition and more seriously, to avoid recursion.
2256          */
2257         if (FontUtilities.isSolaris &&!loaded1dot0Fonts) {
2258             /* "timesroman" is a special case since that's not the
2259              * name of any known font on Solaris or elsewhere.
2260              */
2261             if (lowerCaseName.equals("timesroman")) {
2262                 font = findFont2D("serif", style, fallback);
2263                 fontNameCache.put(mapName, font);
2264             }
2265             register1dot0Fonts();
2266             loaded1dot0Fonts = true;
2267             Font2D ff = findFont2D(name, style, fallback);
2268             return ff;
2269         }
2270 
2271         /* We check for application registered fonts before
2272          * explicitly loading all fonts as if necessary the registration
2273          * code will have done so anyway. And we don't want to needlessly
2274          * load the actual files for all fonts.
2275          * Just as for installed fonts we check for family before fullname.
2276          * We do not add these fonts to fontNameCache for the
2277          * app context case which eliminates the overhead of a per context
2278          * cache for these.
2279          */
2280 
2281         if (fontsAreRegistered || fontsAreRegisteredPerAppContext) {
2282             Hashtable<String, FontFamily> familyTable = null;
2283             Hashtable<String, Font2D> nameTable;
2284 
2285             if (fontsAreRegistered) {
2286                 familyTable = createdByFamilyName;
2287                 nameTable = createdByFullName;
2288             } else {
2289                 AppContext appContext = AppContext.getAppContext();
2290                 @SuppressWarnings("unchecked")
2291                 Hashtable<String,FontFamily> tmp1 =
2292                     (Hashtable<String,FontFamily>)appContext.get(regFamilyKey);
2293                 familyTable = tmp1;
2294 
2295                 @SuppressWarnings("unchecked")
2296                 Hashtable<String, Font2D> tmp2 =
2297                     (Hashtable<String,Font2D>)appContext.get(regFullNameKey);
2298                 nameTable = tmp2;
2299             }
2300 
2301             family = familyTable.get(lowerCaseName);
2302             if (family != null) {
2303                 font = family.getFontWithExactStyleMatch(style);
2304                 if (font == null) {
2305                     font = family.getFont(style);
2306                 }
2307                 if (font == null) {
2308                     font = family.getClosestStyle(style);
2309                 }
2310                 if (font != null) {
2311                     if (fontsAreRegistered) {
2312                         fontNameCache.put(mapName, font);
2313                     }
2314                     return font;
2315                 }
2316             }
2317             font = nameTable.get(lowerCaseName);
2318             if (font != null) {
2319                 if (fontsAreRegistered) {
2320                     fontNameCache.put(mapName, font);
2321                 }
2322                 return font;
2323             }
2324         }
2325 
2326         /* If reach here and no match has been located, then if all fonts
2327          * are not yet loaded, do so, and then recurse.
2328          */
2329         if (!loadedAllFonts) {
2330             if (FontUtilities.isLogging()) {
2331                 FontUtilities.getLogger()
2332                                        .info("Load fonts looking for:" + name);
2333             }
2334             loadFonts();
2335             loadedAllFonts = true;
2336             return findFont2D(name, style, fallback);
2337         }
2338 
2339         if (!loadedAllFontFiles) {
2340             if (FontUtilities.isLogging()) {
2341                 FontUtilities.getLogger()
2342                                   .info("Load font files looking for:" + name);
2343             }
2344             loadFontFiles();
2345             loadedAllFontFiles = true;
2346             return findFont2D(name, style, fallback);
2347         }
2348 
2349         /* The primary name is the locale default - ie not US/English but
2350          * whatever is the default in this locale. This is the way it always
2351          * has been but may be surprising to some developers if "Arial Regular"
2352          * were hard-coded in their app and yet "Arial Regular" was not the
2353          * default name. Fortunately for them, as a consequence of the JDK
2354          * supporting returning names and family names for arbitrary locales,
2355          * we also need to support searching all localised names for a match.
2356          * But because this case of the name used to reference a font is not
2357          * the same as the default for this locale is rare, it makes sense to
2358          * search a much shorter list of default locale names and only go to
2359          * a longer list of names in the event that no match was found.
2360          * So add here code which searches localised names too.
2361          * As in 1.4.x this happens only after loading all fonts, which
2362          * is probably the right order.
2363          */
2364         if ((font = findFont2DAllLocales(name, style)) != null) {
2365             fontNameCache.put(mapName, font);
2366             return font;
2367         }
2368 
2369         /* Perhaps its a "compatibility" name - timesroman, helvetica,
2370          * or courier, which 1.0 apps used for logical fonts.
2371          * We look for these "late" after a loadFonts as we must not
2372          * hide real fonts of these names.
2373          * Map these appropriately:
2374          * On windows this means according to the rules specified by the
2375          * FontConfiguration : do it only for encoding==Cp1252
2376          *
2377          * REMIND: this is something we plan to remove.
2378          */
2379         if (FontUtilities.isWindows) {
2380             String compatName =
2381                 getFontConfiguration().getFallbackFamilyName(name, null);
2382             if (compatName != null) {
2383                 font = findFont2D(compatName, style, fallback);
2384                 fontNameCache.put(mapName, font);
2385                 return font;
2386             }
2387         } else if (lowerCaseName.equals("timesroman")) {
2388             font = findFont2D("serif", style, fallback);
2389             fontNameCache.put(mapName, font);
2390             return font;
2391         } else if (lowerCaseName.equals("helvetica")) {
2392             font = findFont2D("sansserif", style, fallback);
2393             fontNameCache.put(mapName, font);
2394             return font;
2395         } else if (lowerCaseName.equals("courier")) {
2396             font = findFont2D("monospaced", style, fallback);
2397             fontNameCache.put(mapName, font);
2398             return font;
2399         }
2400 
2401         if (FontUtilities.isLogging()) {
2402             FontUtilities.getLogger().info("No font found for:" + name);
2403         }
2404 
2405         switch (fallback) {
2406         case PHYSICAL_FALLBACK: return getDefaultPhysicalFont();
2407         case LOGICAL_FALLBACK: return getDefaultLogicalFont(style);
2408         default: return null;
2409         }
2410     }
2411 
2412     /*
2413      * Workaround for apps which are dependent on a font metrics bug
2414      * in JDK 1.1. This is an unsupported win32 private setting.
2415      * Left in for a customer - do not remove.
2416      */
2417     public boolean usePlatformFontMetrics() {
2418         return usePlatformFontMetrics;
2419     }
2420 
2421     public int getNumFonts() {
2422         return physicalFonts.size()+maxCompFont;
2423     }
2424 
2425     private static boolean fontSupportsEncoding(Font font, String encoding) {
2426         return FontUtilities.getFont2D(font).supportsEncoding(encoding);
2427     }
2428 
2429     protected abstract String getFontPath(boolean noType1Fonts);
2430 
2431     Thread fileCloser = null;
2432     Vector<File> tmpFontFiles = null;
2433 
2434     public Font2D[] createFont2D(File fontFile, int fontFormat, boolean all,
2435                                  boolean isCopy, CreatedFontTracker tracker)
2436     throws FontFormatException {
2437 
2438         List<Font2D> fList = new ArrayList<Font2D>();
2439         int cnt = 1;
2440         String fontFilePath = fontFile.getPath();
2441         FileFont font2D = null;
2442         final File fFile = fontFile;
2443         final CreatedFontTracker _tracker = tracker;
2444         try {
2445             switch (fontFormat) {
2446             case Font.TRUETYPE_FONT:
2447                 font2D = new TrueTypeFont(fontFilePath, null, 0, true);
2448                 fList.add(font2D);
2449                 if (!all) {
2450                     break;
2451                 }
2452                 cnt = ((TrueTypeFont)font2D).getFontCount();
2453                 int index = 1;
2454                 while (index < cnt) {
2455                     fList.add(new TrueTypeFont(fontFilePath, null, index++, true));
2456                 }
2457                 break;
2458             case Font.TYPE1_FONT:
2459                 font2D = new Type1Font(fontFilePath, null, isCopy);
2460                 fList.add(font2D);
2461                 break;
2462             default:
2463                 throw new FontFormatException("Unrecognised Font Format");
2464             }
2465         } catch (FontFormatException e) {
2466             if (isCopy) {
2467                 java.security.AccessController.doPrivileged(
2468                      new java.security.PrivilegedAction<Object>() {
2469                           public Object run() {
2470                               if (_tracker != null) {
2471                                   _tracker.subBytes((int)fFile.length());
2472                               }
2473                               fFile.delete();
2474                               return null;
2475                           }
2476                 });
2477             }
2478             throw(e);
2479         }
2480         if (isCopy) {
2481             FileFont.setFileToRemove(fList, fontFile, cnt, tracker);
2482             synchronized (FontManager.class) {
2483 
2484                 if (tmpFontFiles == null) {
2485                     tmpFontFiles = new Vector<File>();
2486                 }
2487                 tmpFontFiles.add(fontFile);
2488 
2489                 if (fileCloser == null) {
2490                     final Runnable fileCloserRunnable = new Runnable() {
2491                       public void run() {
2492                          java.security.AccessController.doPrivileged(
2493                          new java.security.PrivilegedAction<Object>() {
2494                          public Object run() {
2495 
2496                             for (int i=0;i<CHANNELPOOLSIZE;i++) {
2497                                 if (fontFileCache[i] != null) {
2498                                     try {
2499                                         fontFileCache[i].close();
2500                                     } catch (Exception e) {
2501                                     }
2502                                 }
2503                             }
2504                             if (tmpFontFiles != null) {
2505                                 File[] files = new File[tmpFontFiles.size()];
2506                                 files = tmpFontFiles.toArray(files);
2507                                 for (int f=0; f<files.length;f++) {
2508                                     try {
2509                                         files[f].delete();
2510                                     } catch (Exception e) {
2511                                     }
2512                                 }
2513                             }
2514 
2515                             return null;
2516                           }
2517 
2518                           });
2519                       }
2520                     };
2521                     AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
2522                         ThreadGroup rootTG = ThreadGroupUtils.getRootThreadGroup();
2523                         fileCloser = new Thread(rootTG, fileCloserRunnable,
2524                                                 "FileCloser", 0, false);
2525                         fileCloser.setContextClassLoader(null);
2526                         Runtime.getRuntime().addShutdownHook(fileCloser);
2527                         return null;
2528                     });
2529                 }
2530             }
2531         }
2532         return fList.toArray(new Font2D[0]);
2533     }
2534 
2535     /* remind: used in X11GraphicsEnvironment and called often enough
2536      * that we ought to obsolete this code
2537      */
2538     public synchronized String getFullNameByFileName(String fileName) {
2539         PhysicalFont[] physFonts = getPhysicalFonts();
2540         for (int i=0;i<physFonts.length;i++) {
2541             if (physFonts[i].platName.equals(fileName)) {
2542                 return (physFonts[i].getFontName(null));
2543             }
2544         }
2545         return null;
2546     }
2547 
2548     /*
2549      * This is called when font is determined to be invalid/bad.
2550      * It designed to be called (for example) by the font scaler
2551      * when in processing a font file it is discovered to be incorrect.
2552      * This is different than the case where fonts are discovered to
2553      * be incorrect during initial verification, as such fonts are
2554      * never registered.
2555      * Handles to this font held are re-directed to a default font.
2556      * This default may not be an ideal substitute buts it better than
2557      * crashing This code assumes a PhysicalFont parameter as it doesn't
2558      * make sense for a Composite to be "bad".
2559      */
2560     public synchronized void deRegisterBadFont(Font2D font2D) {
2561         if (!(font2D instanceof PhysicalFont)) {
2562             /* We should never reach here, but just in case */
2563             return;
2564         } else {
2565             if (FontUtilities.isLogging()) {
2566                 FontUtilities.getLogger()
2567                                      .severe("Deregister bad font: " + font2D);
2568             }
2569             replaceFont((PhysicalFont)font2D, getDefaultPhysicalFont());
2570         }
2571     }
2572 
2573     /*
2574      * This encapsulates all the work that needs to be done when a
2575      * Font2D is replaced by a different Font2D.
2576      */
2577     public synchronized void replaceFont(PhysicalFont oldFont,
2578                                          PhysicalFont newFont) {
2579 
2580         if (oldFont.handle.font2D != oldFont) {
2581             /* already done */
2582             return;
2583         }
2584 
2585         /* If we try to replace the font with itself, that won't work,
2586          * so pick any alternative physical font
2587          */
2588         if (oldFont == newFont) {
2589             if (FontUtilities.isLogging()) {
2590                 FontUtilities.getLogger()
2591                       .severe("Can't replace bad font with itself " + oldFont);
2592             }
2593             PhysicalFont[] physFonts = getPhysicalFonts();
2594             for (int i=0; i<physFonts.length;i++) {
2595                 if (physFonts[i] != newFont) {
2596                     newFont = physFonts[i];
2597                     break;
2598                 }
2599             }
2600             if (oldFont == newFont) {
2601                 if (FontUtilities.isLogging()) {
2602                     FontUtilities.getLogger()
2603                            .severe("This is bad. No good physicalFonts found.");
2604                 }
2605                 return;
2606             }
2607         }
2608 
2609         /* eliminate references to this font, so it won't be located
2610          * by future callers, and will be eligible for GC when all
2611          * references are removed
2612          */
2613         oldFont.handle.font2D = newFont;
2614         physicalFonts.remove(oldFont.fullName);
2615         fullNameToFont.remove(oldFont.fullName.toLowerCase(Locale.ENGLISH));
2616         FontFamily.remove(oldFont);
2617         if (localeFullNamesToFont != null) {
2618             Map.Entry<?, ?>[] mapEntries = localeFullNamesToFont.entrySet().
2619                 toArray(new Map.Entry<?, ?>[0]);
2620             /* Should I be replacing these, or just I just remove
2621              * the names from the map?
2622              */
2623             for (int i=0; i<mapEntries.length;i++) {
2624                 if (mapEntries[i].getValue() == oldFont) {
2625                     try {
2626                         @SuppressWarnings("unchecked")
2627                         Map.Entry<String, PhysicalFont> tmp = (Map.Entry<String, PhysicalFont>)mapEntries[i];
2628                         tmp.setValue(newFont);
2629                     } catch (Exception e) {
2630                         /* some maps don't support this operation.
2631                          * In this case just give up and remove the entry.
2632                          */
2633                         localeFullNamesToFont.remove(mapEntries[i].getKey());
2634                     }
2635                 }
2636             }
2637         }
2638 
2639         for (int i=0; i<maxCompFont; i++) {
2640             /* Deferred initialization of composites shouldn't be
2641              * a problem for this case, since a font must have been
2642              * initialised to be discovered to be bad.
2643              * Some JRE composites on Solaris use two versions of the same
2644              * font. The replaced font isn't bad, just "smaller" so there's
2645              * no need to make the slot point to the new font.
2646              * Since composites have a direct reference to the Font2D (not
2647              * via a handle) making this substitution is not safe and could
2648              * cause an additional problem and so this substitution is
2649              * warranted only when a font is truly "bad" and could cause
2650              * a crash. So we now replace it only if its being substituted
2651              * with some font other than a fontconfig rank font
2652              * Since in practice a substitution will have the same rank
2653              * this may never happen, but the code is safer even if its
2654              * also now a no-op.
2655              * The only obvious "glitch" from this stems from the current
2656              * implementation that when asked for the number of glyphs in a
2657              * composite it lies and returns the number in slot 0 because
2658              * composite glyphs aren't contiguous. Since we live with that
2659              * we can live with the glitch that depending on how it was
2660              * initialised a composite may return different values for this.
2661              * Fixing the issues with composite glyph ids is tricky as
2662              * there are exclusion ranges and unlike other fonts even the
2663              * true "numGlyphs" isn't a contiguous range. Likely the only
2664              * solution is an API that returns an array of glyph ranges
2665              * which takes precedence over the existing API. That might
2666              * also need to address excluding ranges which represent a
2667              * code point supported by an earlier component.
2668              */
2669             if (newFont.getRank() > Font2D.FONT_CONFIG_RANK) {
2670                 compFonts[i].replaceComponentFont(oldFont, newFont);
2671             }
2672         }
2673     }
2674 
2675     private synchronized void loadLocaleNames() {
2676         if (localeFullNamesToFont != null) {
2677             return;
2678         }
2679         localeFullNamesToFont = new HashMap<String, TrueTypeFont>();
2680         Font2D[] fonts = getRegisteredFonts();
2681         for (int i=0; i<fonts.length; i++) {
2682             if (fonts[i] instanceof TrueTypeFont) {
2683                 TrueTypeFont ttf = (TrueTypeFont)fonts[i];
2684                 String[] fullNames = ttf.getAllFullNames();
2685                 for (int n=0; n<fullNames.length; n++) {
2686                     localeFullNamesToFont.put(fullNames[n], ttf);
2687                 }
2688                 FontFamily family = FontFamily.getFamily(ttf.familyName);
2689                 if (family != null) {
2690                     FontFamily.addLocaleNames(family, ttf.getAllFamilyNames());
2691                 }
2692             }
2693         }
2694     }
2695 
2696     /* This replicate the core logic of findFont2D but operates on
2697      * all the locale names. This hasn't been merged into findFont2D to
2698      * keep the logic simpler and reduce overhead, since this case is
2699      * almost never used. The main case in which it is called is when
2700      * a bogus font name is used and we need to check all possible names
2701      * before returning the default case.
2702      */
2703     private Font2D findFont2DAllLocales(String name, int style) {
2704 
2705         if (FontUtilities.isLogging()) {
2706             FontUtilities.getLogger()
2707                            .info("Searching localised font names for:" + name);
2708         }
2709 
2710         /* If reach here and no match has been located, then if we have
2711          * not yet built the map of localeFullNamesToFont for TT fonts, do so
2712          * now. This method must be called after all fonts have been loaded.
2713          */
2714         if (localeFullNamesToFont == null) {
2715             loadLocaleNames();
2716         }
2717         String lowerCaseName = name.toLowerCase();
2718         Font2D font = null;
2719 
2720         /* First see if its a family name. */
2721         FontFamily family = FontFamily.getLocaleFamily(lowerCaseName);
2722         if (family != null) {
2723           font = family.getFont(style);
2724           if (font == null) {
2725             font = family.getClosestStyle(style);
2726           }
2727           if (font != null) {
2728               return font;
2729           }
2730         }
2731 
2732         /* If it wasn't a family name, it should be a full name. */
2733         synchronized (this) {
2734             font = localeFullNamesToFont.get(name);
2735         }
2736         if (font != null) {
2737             if (font.style == style || style == Font.PLAIN) {
2738                 return font;
2739             } else {
2740                 family = FontFamily.getFamily(font.getFamilyName(null));
2741                 if (family != null) {
2742                     Font2D familyFont = family.getFont(style);
2743                     /* We exactly matched the requested style, use it! */
2744                     if (familyFont != null) {
2745                         return familyFont;
2746                     } else {
2747                         familyFont = family.getClosestStyle(style);
2748                         if (familyFont != null) {
2749                             /* The next check is perhaps one
2750                              * that shouldn't be done. ie if we get this
2751                              * far we have probably as close a match as we
2752                              * are going to get. We could load all fonts to
2753                              * see if somehow some parts of the family are
2754                              * loaded but not all of it.
2755                              * This check is commented out for now.
2756                              */
2757                             if (!familyFont.canDoStyle(style)) {
2758                                 familyFont = null;
2759                             }
2760                             return familyFont;
2761                         }
2762                     }
2763                 }
2764             }
2765         }
2766         return font;
2767     }
2768 
2769     /* Supporting "alternate" composite fonts on 2D graphics objects
2770      * is accessed by the application by calling methods on the local
2771      * GraphicsEnvironment. The overall implementation is described
2772      * in one place, here, since otherwise the implementation is spread
2773      * around it may be difficult to track.
2774      * The methods below call into SunGraphicsEnvironment which creates a
2775      * new FontConfiguration instance. The FontConfiguration class,
2776      * and its platform sub-classes are updated to take parameters requesting
2777      * these behaviours. This is then used to create new composite font
2778      * instances. Since this calls the initCompositeFont method in
2779      * SunGraphicsEnvironment it performs the same initialization as is
2780      * performed normally. There may be some duplication of effort, but
2781      * that code is already written to be able to perform properly if called
2782      * to duplicate work. The main difference is that if we detect we are
2783      * running in an applet/browser/Java plugin environment these new fonts
2784      * are not placed in the "default" maps but into an AppContext instance.
2785      * The font lookup mechanism in java.awt.Font.getFont2D() is also updated
2786      * so that look-up for composite fonts will in that case always
2787      * do a lookup rather than returning a cached result.
2788      * This is inefficient but necessary else singleton java.awt.Font
2789      * instances would not retrieve the correct Font2D for the appcontext.
2790      * sun.font.FontManager.findFont2D is also updated to that it uses
2791      * a name map cache specific to that appcontext.
2792      *
2793      * Getting an AppContext is expensive, so there is a global variable
2794      * that records whether these methods have ever been called and can
2795      * avoid the expense for almost all applications. Once the correct
2796      * CompositeFont is associated with the Font, everything should work
2797      * through existing mechanisms.
2798      * A special case is that GraphicsEnvironment.getAllFonts() must
2799      * return an AppContext specific list.
2800      *
2801      * Calling the methods below is "heavyweight" but it is expected that
2802      * these methods will be called very rarely.
2803      *
2804      * If _usingPerAppContextComposites is true, we are in "applet"
2805      * (eg browser) environment and at least one context has selected
2806      * an alternate composite font behaviour.
2807      * If _usingAlternateComposites is true, we are not in an "applet"
2808      * environment and the (single) application has selected
2809      * an alternate composite font behaviour.
2810      *
2811      * - Printing: The implementation delegates logical fonts to an AWT
2812      * mechanism which cannot use these alternate configurations.
2813      * We can detect that alternate fonts are in use and back-off to 2D, but
2814      * that uses outlines. Much of this can be fixed with additional work
2815      * but that may have to wait. The results should be correct, just not
2816      * optimal.
2817      */
2818     private static final Object altJAFontKey       = new Object();
2819     private static final Object localeFontKey       = new Object();
2820     private static final Object proportionalFontKey = new Object();
2821     private boolean _usingPerAppContextComposites = false;
2822     private boolean _usingAlternateComposites = false;
2823 
2824     /* These values are used only if we are running as a standalone
2825      * application, as determined by maybeMultiAppContext();
2826      */
2827     private static boolean gAltJAFont = false;
2828     private boolean gLocalePref = false;
2829     private boolean gPropPref = false;
2830 
2831     /* This method doesn't check if alternates are selected in this app
2832      * context. Its used by the FontMetrics caching code which in such
2833      * a case cannot retrieve a cached metrics solely on the basis of
2834      * the Font.equals() method since it needs to also check if the Font2D
2835      * is the same.
2836      * We also use non-standard composites for Swing native L&F fonts on
2837      * Windows. In that case the policy is that the metrics reported are
2838      * based solely on the physical font in the first slot which is the
2839      * visible java.awt.Font. So in that case the metrics cache which tests
2840      * the Font does what we want. In the near future when we expand the GTK
2841      * logical font definitions we may need to revisit this if GTK reports
2842      * combined metrics instead. For now though this test can be simple.
2843      */
2844     public boolean maybeUsingAlternateCompositeFonts() {
2845        return _usingAlternateComposites || _usingPerAppContextComposites;
2846     }
2847 
2848     public boolean usingAlternateCompositeFonts() {
2849         return (_usingAlternateComposites ||
2850                 (_usingPerAppContextComposites &&
2851                 AppContext.getAppContext().get(CompositeFont.class) != null));
2852     }
2853 
2854     private static boolean maybeMultiAppContext() {
2855         Boolean appletSM = (Boolean)
2856             java.security.AccessController.doPrivileged(
2857                 new java.security.PrivilegedAction<Object>() {
2858                         public Object run() {
2859                             SecurityManager sm = System.getSecurityManager();
2860                             return sm instanceof sun.applet.AppletSecurity;
2861                         }
2862                     });
2863         return appletSM.booleanValue();
2864     }
2865 
2866     /* Modifies the behaviour of a subsequent call to preferLocaleFonts()
2867      * to use Mincho instead of Gothic for dialoginput in JA locales
2868      * on windows. Not needed on other platforms.
2869      */
2870     public synchronized void useAlternateFontforJALocales() {
2871         if (FontUtilities.isLogging()) {
2872             FontUtilities.getLogger()
2873                 .info("Entered useAlternateFontforJALocales().");
2874         }
2875         if (!FontUtilities.isWindows) {
2876             return;
2877         }
2878 
2879         if (!maybeMultiAppContext()) {
2880             gAltJAFont = true;
2881         } else {
2882             AppContext appContext = AppContext.getAppContext();
2883             appContext.put(altJAFontKey, altJAFontKey);
2884         }
2885     }
2886 
2887     public boolean usingAlternateFontforJALocales() {
2888         if (!maybeMultiAppContext()) {
2889             return gAltJAFont;
2890         } else {
2891             AppContext appContext = AppContext.getAppContext();
2892             return appContext.get(altJAFontKey) == altJAFontKey;
2893         }
2894     }
2895 
2896     public synchronized void preferLocaleFonts() {
2897         if (FontUtilities.isLogging()) {
2898             FontUtilities.getLogger().info("Entered preferLocaleFonts().");
2899         }
2900         /* Test if re-ordering will have any effect */
2901         if (!FontConfiguration.willReorderForStartupLocale()) {
2902             return;
2903         }
2904 
2905         if (!maybeMultiAppContext()) {
2906             if (gLocalePref == true) {
2907                 return;
2908             }
2909             gLocalePref = true;
2910             createCompositeFonts(fontNameCache, gLocalePref, gPropPref);
2911             _usingAlternateComposites = true;
2912         } else {
2913             AppContext appContext = AppContext.getAppContext();
2914             if (appContext.get(localeFontKey) == localeFontKey) {
2915                 return;
2916             }
2917             appContext.put(localeFontKey, localeFontKey);
2918             boolean acPropPref =
2919                 appContext.get(proportionalFontKey) == proportionalFontKey;
2920             ConcurrentHashMap<String, Font2D>
2921                 altNameCache = new ConcurrentHashMap<String, Font2D> ();
2922             /* If there is an existing hashtable, we can drop it. */
2923             appContext.put(CompositeFont.class, altNameCache);
2924             _usingPerAppContextComposites = true;
2925             createCompositeFonts(altNameCache, true, acPropPref);
2926         }
2927     }
2928 
2929     public synchronized void preferProportionalFonts() {
2930         if (FontUtilities.isLogging()) {
2931             FontUtilities.getLogger()
2932                 .info("Entered preferProportionalFonts().");
2933         }
2934         /* If no proportional fonts are configured, there's no need
2935          * to take any action.
2936          */
2937         if (!FontConfiguration.hasMonoToPropMap()) {
2938             return;
2939         }
2940 
2941         if (!maybeMultiAppContext()) {
2942             if (gPropPref == true) {
2943                 return;
2944             }
2945             gPropPref = true;
2946             createCompositeFonts(fontNameCache, gLocalePref, gPropPref);
2947             _usingAlternateComposites = true;
2948         } else {
2949             AppContext appContext = AppContext.getAppContext();
2950             if (appContext.get(proportionalFontKey) == proportionalFontKey) {
2951                 return;
2952             }
2953             appContext.put(proportionalFontKey, proportionalFontKey);
2954             boolean acLocalePref =
2955                 appContext.get(localeFontKey) == localeFontKey;
2956             ConcurrentHashMap<String, Font2D>
2957                 altNameCache = new ConcurrentHashMap<String, Font2D> ();
2958             /* If there is an existing hashtable, we can drop it. */
2959             appContext.put(CompositeFont.class, altNameCache);
2960             _usingPerAppContextComposites = true;
2961             createCompositeFonts(altNameCache, acLocalePref, true);
2962         }
2963     }
2964 
2965     private static HashSet<String> installedNames = null;
2966     private static HashSet<String> getInstalledNames() {
2967         if (installedNames == null) {
2968            Locale l = getSystemStartupLocale();
2969            SunFontManager fontManager = SunFontManager.getInstance();
2970            String[] installedFamilies =
2971                fontManager.getInstalledFontFamilyNames(l);
2972            Font[] installedFonts = fontManager.getAllInstalledFonts();
2973            HashSet<String> names = new HashSet<String>();
2974            for (int i=0; i<installedFamilies.length; i++) {
2975                names.add(installedFamilies[i].toLowerCase(l));
2976            }
2977            for (int i=0; i<installedFonts.length; i++) {
2978                names.add(installedFonts[i].getFontName(l).toLowerCase(l));
2979            }
2980            installedNames = names;
2981         }
2982         return installedNames;
2983     }
2984 
2985     /* Keys are used to lookup per-AppContext Hashtables */
2986     private static final Object regFamilyKey  = new Object();
2987     private static final Object regFullNameKey = new Object();
2988     private Hashtable<String,FontFamily> createdByFamilyName;
2989     private Hashtable<String,Font2D>     createdByFullName;
2990     private boolean fontsAreRegistered = false;
2991     private boolean fontsAreRegisteredPerAppContext = false;
2992 
2993     public boolean registerFont(Font font) {
2994         /* This method should not be called with "null".
2995          * It is the caller's responsibility to ensure that.
2996          */
2997         if (font == null) {
2998             return false;
2999         }
3000 
3001         /* Initialise these objects only once we start to use this API */
3002         synchronized (regFamilyKey) {
3003             if (createdByFamilyName == null) {
3004                 createdByFamilyName = new Hashtable<String,FontFamily>();
3005                 createdByFullName = new Hashtable<String,Font2D>();
3006             }
3007         }
3008 
3009         if (! FontAccess.getFontAccess().isCreatedFont(font)) {
3010             return false;
3011         }
3012         /* We want to ensure that this font cannot override existing
3013          * installed fonts. Check these conditions :
3014          * - family name is not that of an installed font
3015          * - full name is not that of an installed font
3016          * - family name is not the same as the full name of an installed font
3017          * - full name is not the same as the family name of an installed font
3018          * The last two of these may initially look odd but the reason is
3019          * that (unfortunately) Font constructors do not distinuguish these.
3020          * An extreme example of such a problem would be a font which has
3021          * family name "Dialog.Plain" and full name of "Dialog".
3022          * The one arguably overly stringent restriction here is that if an
3023          * application wants to supply a new member of an existing family
3024          * It will get rejected. But since the JRE can perform synthetic
3025          * styling in many cases its not necessary.
3026          * We don't apply the same logic to registered fonts. If apps want
3027          * to do this lets assume they have a reason. It won't cause problems
3028          * except for themselves.
3029          */
3030         HashSet<String> names = getInstalledNames();
3031         Locale l = getSystemStartupLocale();
3032         String familyName = font.getFamily(l).toLowerCase();
3033         String fullName = font.getFontName(l).toLowerCase();
3034         if (names.contains(familyName) || names.contains(fullName)) {
3035             return false;
3036         }
3037 
3038         /* Checks passed, now register the font */
3039         Hashtable<String,FontFamily> familyTable;
3040         Hashtable<String,Font2D> fullNameTable;
3041         if (!maybeMultiAppContext()) {
3042             familyTable = createdByFamilyName;
3043             fullNameTable = createdByFullName;
3044             fontsAreRegistered = true;
3045         } else {
3046             AppContext appContext = AppContext.getAppContext();
3047             @SuppressWarnings("unchecked")
3048             Hashtable<String,FontFamily> tmp1 =
3049                 (Hashtable<String,FontFamily>)appContext.get(regFamilyKey);
3050             familyTable = tmp1;
3051             @SuppressWarnings("unchecked")
3052             Hashtable<String,Font2D> tmp2 =
3053                 (Hashtable<String,Font2D>)appContext.get(regFullNameKey);
3054             fullNameTable = tmp2;
3055 
3056             if (familyTable == null) {
3057                 familyTable = new Hashtable<String,FontFamily>();
3058                 fullNameTable = new Hashtable<String,Font2D>();
3059                 appContext.put(regFamilyKey, familyTable);
3060                 appContext.put(regFullNameKey, fullNameTable);
3061             }
3062             fontsAreRegisteredPerAppContext = true;
3063         }
3064         /* Create the FontFamily and add font to the tables */
3065         Font2D font2D = FontUtilities.getFont2D(font);
3066         int style = font2D.getStyle();
3067         FontFamily family = familyTable.get(familyName);
3068         if (family == null) {
3069             family = new FontFamily(font.getFamily(l));
3070             familyTable.put(familyName, family);
3071         }
3072         /* Remove name cache entries if not using app contexts.
3073          * To accommodate a case where code may have registered first a plain
3074          * family member and then used it and is now registering a bold family
3075          * member, we need to remove all members of the family, so that the
3076          * new style can get picked up rather than continuing to synthesise.
3077          */
3078         if (fontsAreRegistered) {
3079             removeFromCache(family.getFont(Font.PLAIN));
3080             removeFromCache(family.getFont(Font.BOLD));
3081             removeFromCache(family.getFont(Font.ITALIC));
3082             removeFromCache(family.getFont(Font.BOLD|Font.ITALIC));
3083             removeFromCache(fullNameTable.get(fullName));
3084         }
3085         family.setFont(font2D, style);
3086         fullNameTable.put(fullName, font2D);
3087         return true;
3088     }
3089 
3090     /* Remove from the name cache all references to the Font2D */
3091     private void removeFromCache(Font2D font) {
3092         if (font == null) {
3093             return;
3094         }
3095         String[] keys = fontNameCache.keySet().toArray(STR_ARRAY);
3096         for (int k=0; k<keys.length;k++) {
3097             if (fontNameCache.get(keys[k]) == font) {
3098                 fontNameCache.remove(keys[k]);
3099             }
3100         }
3101     }
3102 
3103     // It may look odd to use TreeMap but its more convenient to the caller.
3104     public TreeMap<String, String> getCreatedFontFamilyNames() {
3105 
3106         Hashtable<String,FontFamily> familyTable;
3107         if (fontsAreRegistered) {
3108             familyTable = createdByFamilyName;
3109         } else if (fontsAreRegisteredPerAppContext) {
3110             AppContext appContext = AppContext.getAppContext();
3111             @SuppressWarnings("unchecked")
3112             Hashtable<String,FontFamily> tmp =
3113                 (Hashtable<String,FontFamily>)appContext.get(regFamilyKey);
3114             familyTable = tmp;
3115         } else {
3116             return null;
3117         }
3118 
3119         Locale l = getSystemStartupLocale();
3120         synchronized (familyTable) {
3121             TreeMap<String, String> map = new TreeMap<String, String>();
3122             for (FontFamily f : familyTable.values()) {
3123                 Font2D font2D = f.getFont(Font.PLAIN);
3124                 if (font2D == null) {
3125                     font2D = f.getClosestStyle(Font.PLAIN);
3126                 }
3127                 String name = font2D.getFamilyName(l);
3128                 map.put(name.toLowerCase(l), name);
3129             }
3130             return map;
3131         }
3132     }
3133 
3134     public Font[] getCreatedFonts() {
3135 
3136         Hashtable<String,Font2D> nameTable;
3137         if (fontsAreRegistered) {
3138             nameTable = createdByFullName;
3139         } else if (fontsAreRegisteredPerAppContext) {
3140             AppContext appContext = AppContext.getAppContext();
3141             @SuppressWarnings("unchecked")
3142             Hashtable<String,Font2D> tmp =
3143                 (Hashtable<String,Font2D>)appContext.get(regFullNameKey);
3144             nameTable = tmp;
3145         } else {
3146             return null;
3147         }
3148 
3149         Locale l = getSystemStartupLocale();
3150         synchronized (nameTable) {
3151             Font[] fonts = new Font[nameTable.size()];
3152             int i=0;
3153             for (Font2D font2D : nameTable.values()) {
3154                 fonts[i++] = new Font(font2D.getFontName(l), Font.PLAIN, 1);
3155             }
3156             return fonts;
3157         }
3158     }
3159 
3160 
3161     protected String[] getPlatformFontDirs(boolean noType1Fonts) {
3162 
3163         /* First check if we already initialised path dirs */
3164         if (pathDirs != null) {
3165             return pathDirs;
3166         }
3167 
3168         String path = getPlatformFontPath(noType1Fonts);
3169         StringTokenizer parser =
3170             new StringTokenizer(path, File.pathSeparator);
3171         ArrayList<String> pathList = new ArrayList<String>();
3172         try {
3173             while (parser.hasMoreTokens()) {
3174                 pathList.add(parser.nextToken());
3175             }
3176         } catch (NoSuchElementException e) {
3177         }
3178         pathDirs = pathList.toArray(new String[0]);
3179         return pathDirs;
3180     }
3181 
3182     /**
3183      * Returns an array of two strings. The first element is the
3184      * name of the font. The second element is the file name.
3185      */
3186     protected abstract String[] getDefaultPlatformFont();
3187 
3188     // Begin: Refactored from SunGraphicsEnviroment.
3189 
3190     /*
3191      * helper function for registerFonts
3192      */
3193     private void addDirFonts(String dirName, File dirFile,
3194                              FilenameFilter filter,
3195                              int fontFormat, boolean useJavaRasterizer,
3196                              int fontRank,
3197                              boolean defer, boolean resolveSymLinks) {
3198         String[] ls = dirFile.list(filter);
3199         if (ls == null || ls.length == 0) {
3200             return;
3201         }
3202         String[] fontNames = new String[ls.length];
3203         String[][] nativeNames = new String[ls.length][];
3204         int fontCount = 0;
3205 
3206         for (int i=0; i < ls.length; i++ ) {
3207             File theFile = new File(dirFile, ls[i]);
3208             String fullName = null;
3209             if (resolveSymLinks) {
3210                 try {
3211                     fullName = theFile.getCanonicalPath();
3212                 } catch (IOException e) {
3213                 }
3214             }
3215             if (fullName == null) {
3216                 fullName = dirName + File.separator + ls[i];
3217             }
3218 
3219             // REMIND: case compare depends on platform
3220             if (registeredFontFiles.contains(fullName)) {
3221                 continue;
3222             }
3223 
3224             if (badFonts != null && badFonts.contains(fullName)) {
3225                 if (FontUtilities.debugFonts()) {
3226                     FontUtilities.getLogger()
3227                                          .warning("skip bad font " + fullName);
3228                 }
3229                 continue; // skip this font file.
3230             }
3231 
3232             registeredFontFiles.add(fullName);
3233 
3234             if (FontUtilities.debugFonts()
3235                 && FontUtilities.getLogger().isLoggable(PlatformLogger.Level.INFO)) {
3236                 String message = "Registering font " + fullName;
3237                 String[] natNames = getNativeNames(fullName, null);
3238                 if (natNames == null) {
3239                     message += " with no native name";
3240                 } else {
3241                     message += " with native name(s) " + natNames[0];
3242                     for (int nn = 1; nn < natNames.length; nn++) {
3243                         message += ", " + natNames[nn];
3244                     }
3245                 }
3246                 FontUtilities.getLogger().info(message);
3247             }
3248             fontNames[fontCount] = fullName;
3249             nativeNames[fontCount++] = getNativeNames(fullName, null);
3250         }
3251         registerFonts(fontNames, nativeNames, fontCount, fontFormat,
3252                          useJavaRasterizer, fontRank, defer);
3253         return;
3254     }
3255 
3256     protected String[] getNativeNames(String fontFileName,
3257                                       String platformName) {
3258         return null;
3259     }
3260 
3261     /**
3262      * Returns a file name for the physical font represented by this platform
3263      * font name. The default implementation tries to obtain the file name
3264      * from the font configuration.
3265      * Subclasses may override to provide information from other sources.
3266      */
3267     protected String getFileNameFromPlatformName(String platformFontName) {
3268         return fontConfig.getFileNameFromPlatformName(platformFontName);
3269     }
3270 
3271     /**
3272      * Return the default font configuration.
3273      */
3274     public FontConfiguration getFontConfiguration() {
3275         return fontConfig;
3276     }
3277 
3278     /* A call to this method should be followed by a call to
3279      * registerFontDirs(..)
3280      */
3281     public String getPlatformFontPath(boolean noType1Font) {
3282         if (fontPath == null) {
3283             fontPath = getFontPath(noType1Font);
3284         }
3285         return fontPath;
3286     }
3287 
3288     public static boolean isOpenJDK() {
3289         return FontUtilities.isOpenJDK;
3290     }
3291 
3292     protected void loadFonts() {
3293         if (discoveredAllFonts) {
3294             return;
3295         }
3296         /* Use lock specific to the font system */
3297         synchronized (this) {
3298             if (FontUtilities.debugFonts()) {
3299                 Thread.dumpStack();
3300                 FontUtilities.getLogger()
3301                             .info("SunGraphicsEnvironment.loadFonts() called");
3302             }
3303             initialiseDeferredFonts();
3304 
3305             java.security.AccessController.doPrivileged(
3306                                     new java.security.PrivilegedAction<Object>() {
3307                 public Object run() {
3308                     if (fontPath == null) {
3309                         fontPath = getPlatformFontPath(noType1Font);
3310                         registerFontDirs(fontPath);
3311                     }
3312                     if (fontPath != null) {
3313                         // this will find all fonts including those already
3314                         // registered. But we have checks in place to prevent
3315                         // double registration.
3316                         if (! gotFontsFromPlatform()) {
3317                             registerFontsOnPath(fontPath, false,
3318                                                 Font2D.UNKNOWN_RANK,
3319                                                 false, true);
3320                             loadedAllFontFiles = true;
3321                         }
3322                     }
3323                     registerOtherFontFiles(registeredFontFiles);
3324                     discoveredAllFonts = true;
3325                     return null;
3326                 }
3327             });
3328         }
3329     }
3330 
3331     protected void registerFontDirs(String pathName) {
3332         return;
3333     }
3334 
3335     private void registerFontsOnPath(String pathName,
3336                                      boolean useJavaRasterizer, int fontRank,
3337                                      boolean defer, boolean resolveSymLinks) {
3338 
3339         StringTokenizer parser = new StringTokenizer(pathName,
3340                 File.pathSeparator);
3341         try {
3342             while (parser.hasMoreTokens()) {
3343                 registerFontsInDir(parser.nextToken(),
3344                         useJavaRasterizer, fontRank,
3345                         defer, resolveSymLinks);
3346             }
3347         } catch (NoSuchElementException e) {
3348         }
3349     }
3350 
3351     /* Called to register fall back fonts */
3352     public void registerFontsInDir(String dirName) {
3353         registerFontsInDir(dirName, true, Font2D.JRE_RANK, true, false);
3354     }
3355 
3356     // MACOSX begin -- need to access this in subclass
3357     protected void registerFontsInDir(String dirName, boolean useJavaRasterizer,
3358     // MACOSX end
3359                                     int fontRank,
3360                                     boolean defer, boolean resolveSymLinks) {
3361         File pathFile = new File(dirName);
3362         addDirFonts(dirName, pathFile, ttFilter,
3363                     FONTFORMAT_TRUETYPE, useJavaRasterizer,
3364                     fontRank==Font2D.UNKNOWN_RANK ?
3365                     Font2D.TTF_RANK : fontRank,
3366                     defer, resolveSymLinks);
3367         addDirFonts(dirName, pathFile, t1Filter,
3368                     FONTFORMAT_TYPE1, useJavaRasterizer,
3369                     fontRank==Font2D.UNKNOWN_RANK ?
3370                     Font2D.TYPE1_RANK : fontRank,
3371                     defer, resolveSymLinks);
3372     }
3373 
3374     protected void registerFontDir(String path) {
3375     }
3376 
3377     /**
3378      * Returns file name for default font, either absolute
3379      * or relative as needed by registerFontFile.
3380      */
3381     public synchronized String getDefaultFontFile() {
3382         if (defaultFontFileName == null) {
3383             initDefaultFonts();
3384         }
3385         return defaultFontFileName;
3386     }
3387 
3388     private void initDefaultFonts() {
3389         if (!isOpenJDK()) {
3390             defaultFontName = lucidaFontName;
3391             if (useAbsoluteFontFileNames()) {
3392                 defaultFontFileName =
3393                     jreFontDirName + File.separator + FontUtilities.LUCIDA_FILE_NAME;
3394             } else {
3395                 defaultFontFileName = FontUtilities.LUCIDA_FILE_NAME;
3396             }
3397         }
3398     }
3399 
3400     /**
3401      * Whether registerFontFile expects absolute or relative
3402      * font file names.
3403      */
3404     protected boolean useAbsoluteFontFileNames() {
3405         return true;
3406     }
3407 
3408     /**
3409      * Creates this environment's FontConfiguration.
3410      */
3411     protected abstract FontConfiguration createFontConfiguration();
3412 
3413     public abstract FontConfiguration
3414     createFontConfiguration(boolean preferLocaleFonts,
3415                             boolean preferPropFonts);
3416 
3417     /**
3418      * Returns face name for default font, or null if
3419      * no face names are used for CompositeFontDescriptors
3420      * for this platform.
3421      */
3422     public synchronized String getDefaultFontFaceName() {
3423         if (defaultFontName == null) {
3424             initDefaultFonts();
3425         }
3426         return defaultFontName;
3427     }
3428 
3429     public void loadFontFiles() {
3430         loadFonts();
3431         if (loadedAllFontFiles) {
3432             return;
3433         }
3434         /* Use lock specific to the font system */
3435         synchronized (this) {
3436             if (FontUtilities.debugFonts()) {
3437                 Thread.dumpStack();
3438                 FontUtilities.getLogger().info("loadAllFontFiles() called");
3439             }
3440             java.security.AccessController.doPrivileged(
3441                                     new java.security.PrivilegedAction<Object>() {
3442                 public Object run() {
3443                     if (fontPath == null) {
3444                         fontPath = getPlatformFontPath(noType1Font);
3445                     }
3446                     if (fontPath != null) {
3447                         // this will find all fonts including those already
3448                         // registered. But we have checks in place to prevent
3449                         // double registration.
3450                         registerFontsOnPath(fontPath, false,
3451                                             Font2D.UNKNOWN_RANK,
3452                                             false, true);
3453                     }
3454                     loadedAllFontFiles = true;
3455                     return null;
3456                 }
3457             });
3458         }
3459     }
3460 
3461     /*
3462      * This method asks the font configuration API for all platform names
3463      * used as components of composite/logical fonts and iterates over these
3464      * looking up their corresponding file name and registers these fonts.
3465      * It also ensures that the fonts are accessible via platform APIs.
3466      * The composites themselves are then registered.
3467      */
3468     private void
3469         initCompositeFonts(FontConfiguration fontConfig,
3470                            ConcurrentHashMap<String, Font2D>  altNameCache) {
3471 
3472         if (FontUtilities.isLogging()) {
3473             FontUtilities.getLogger()
3474                             .info("Initialising composite fonts");
3475         }
3476 
3477         int numCoreFonts = fontConfig.getNumberCoreFonts();
3478         String[] fcFonts = fontConfig.getPlatformFontNames();
3479         for (int f=0; f<fcFonts.length; f++) {
3480             String platformFontName = fcFonts[f];
3481             String fontFileName =
3482                 getFileNameFromPlatformName(platformFontName);
3483             String[] nativeNames = null;
3484             if (fontFileName == null
3485                 || fontFileName.equals(platformFontName)) {
3486                 /* No file located, so register using the platform name,
3487                  * i.e. as a native font.
3488                  */
3489                 fontFileName = platformFontName;
3490             } else {
3491                 if (f < numCoreFonts) {
3492                     /* If platform APIs also need to access the font, add it
3493                      * to a set to be registered with the platform too.
3494                      * This may be used to add the parent directory to the X11
3495                      * font path if its not already there. See the docs for the
3496                      * subclass implementation.
3497                      * This is now mainly for the benefit of X11-based AWT
3498                      * But for historical reasons, 2D initialisation code
3499                      * makes these calls.
3500                      * If the fontconfiguration file is properly set up
3501                      * so that all fonts are mapped to files and all their
3502                      * appropriate directories are specified, then this
3503                      * method will be low cost as it will return after
3504                      * a test that finds a null lookup map.
3505                      */
3506                     addFontToPlatformFontPath(platformFontName);
3507                 }
3508                 nativeNames = getNativeNames(fontFileName, platformFontName);
3509             }
3510             /* Uncomment these two lines to "generate" the XLFD->filename
3511              * mappings needed to speed start-up on Solaris.
3512              * Augment this with the appendedpathname and the mappings
3513              * for native (F3) fonts
3514              */
3515             //String platName = platformFontName.replaceAll(" ", "_");
3516             //System.out.println("filename."+platName+"="+fontFileName);
3517             registerFontFile(fontFileName, nativeNames,
3518                              Font2D.FONT_CONFIG_RANK, true);
3519 
3520 
3521         }
3522         /* This registers accumulated paths from the calls to
3523          * addFontToPlatformFontPath(..) and any specified by
3524          * the font configuration. Rather than registering
3525          * the fonts it puts them in a place and form suitable for
3526          * the Toolkit to pick up and use if a toolkit is initialised,
3527          * and if it uses X11 fonts.
3528          */
3529         registerPlatformFontsUsedByFontConfiguration();
3530 
3531         CompositeFontDescriptor[] compositeFontInfo
3532                 = fontConfig.get2DCompositeFontInfo();
3533         for (int i = 0; i < compositeFontInfo.length; i++) {
3534             CompositeFontDescriptor descriptor = compositeFontInfo[i];
3535             String[] componentFileNames = descriptor.getComponentFileNames();
3536             String[] componentFaceNames = descriptor.getComponentFaceNames();
3537 
3538             /* It would be better eventually to handle this in the
3539              * FontConfiguration code which should also remove duplicate slots
3540              */
3541             if (missingFontFiles != null) {
3542                 for (int ii=0; ii<componentFileNames.length; ii++) {
3543                     if (missingFontFiles.contains(componentFileNames[ii])) {
3544                         componentFileNames[ii] = getDefaultFontFile();
3545                         componentFaceNames[ii] = getDefaultFontFaceName();
3546                     }
3547                 }
3548             }
3549 
3550             /* FontConfiguration needs to convey how many fonts it has added
3551              * as fallback component fonts which should not affect metrics.
3552              * The core component count will be the number of metrics slots.
3553              * This does not preclude other mechanisms for adding
3554              * fall back component fonts to the composite.
3555              */
3556             if (altNameCache != null) {
3557                 SunFontManager.registerCompositeFont(
3558                     descriptor.getFaceName(),
3559                     componentFileNames, componentFaceNames,
3560                     descriptor.getCoreComponentCount(),
3561                     descriptor.getExclusionRanges(),
3562                     descriptor.getExclusionRangeLimits(),
3563                     true,
3564                     altNameCache);
3565             } else {
3566                 registerCompositeFont(descriptor.getFaceName(),
3567                                       componentFileNames, componentFaceNames,
3568                                       descriptor.getCoreComponentCount(),
3569                                       descriptor.getExclusionRanges(),
3570                                       descriptor.getExclusionRangeLimits(),
3571                                       true);
3572             }
3573             if (FontUtilities.debugFonts()) {
3574                 FontUtilities.getLogger()
3575                                .info("registered " + descriptor.getFaceName());
3576             }
3577         }
3578     }
3579 
3580     /**
3581      * Notifies graphics environment that the logical font configuration
3582      * uses the given platform font name. The graphics environment may
3583      * use this for platform specific initialization.
3584      */
3585     protected void addFontToPlatformFontPath(String platformFontName) {
3586     }
3587 
3588     protected void registerFontFile(String fontFileName, String[] nativeNames,
3589                                     int fontRank, boolean defer) {
3590 //      REMIND: case compare depends on platform
3591         if (registeredFontFiles.contains(fontFileName)) {
3592             return;
3593         }
3594         int fontFormat;
3595         if (ttFilter.accept(null, fontFileName)) {
3596             fontFormat = FONTFORMAT_TRUETYPE;
3597         } else if (t1Filter.accept(null, fontFileName)) {
3598             fontFormat = FONTFORMAT_TYPE1;
3599         } else {
3600             fontFormat = FONTFORMAT_NATIVE;
3601         }
3602         registeredFontFiles.add(fontFileName);
3603         if (defer) {
3604             registerDeferredFont(fontFileName, fontFileName, nativeNames,
3605                                  fontFormat, false, fontRank);
3606         } else {
3607             registerFontFile(fontFileName, nativeNames, fontFormat, false,
3608                              fontRank);
3609         }
3610     }
3611 
3612     protected void registerPlatformFontsUsedByFontConfiguration() {
3613     }
3614 
3615     /*
3616      * A GE may verify whether a font file used in a fontconfiguration
3617      * exists. If it doesn't then either we may substitute the default
3618      * font, or perhaps elide it altogether from the composite font.
3619      * This makes some sense on windows where the font file is only
3620      * likely to be in one place. But on other OSes, eg Linux, the file
3621      * can move around depending. So there we probably don't want to assume
3622      * its missing and so won't add it to this list.
3623      * If this list - missingFontFiles - is non-null then the composite
3624      * font initialisation logic tests to see if a font file is in that
3625      * set.
3626      * Only one thread should be able to add to this set so we don't
3627      * synchronize.
3628      */
3629     protected void addToMissingFontFileList(String fileName) {
3630         if (missingFontFiles == null) {
3631             missingFontFiles = new HashSet<String>();
3632         }
3633         missingFontFiles.add(fileName);
3634     }
3635 
3636     /*
3637      * This is for use only within getAllFonts().
3638      * Fonts listed in the fontconfig files for windows were all
3639      * on the "deferred" initialisation list. They were registered
3640      * either in the course of the application, or in the call to
3641      * loadFonts() within getAllFonts(). The fontconfig file specifies
3642      * the names of the fonts using the English names. If there's a
3643      * different name in the execution locale, then the platform will
3644      * report that, and we will construct the font with both names, and
3645      * thereby enumerate it twice. This happens for Japanese fonts listed
3646      * in the windows fontconfig, when run in the JA locale. The solution
3647      * is to rely (in this case) on the platform's font->file mapping to
3648      * determine that this name corresponds to a file we already registered.
3649      * This works because
3650      * - we know when we get here all deferred fonts are already initialised
3651      * - when we register a font file, we register all fonts in it.
3652      * - we know the fontconfig fonts are all in the windows registry
3653      */
3654     private boolean isNameForRegisteredFile(String fontName) {
3655         String fileName = getFileNameForFontName(fontName);
3656         if (fileName == null) {
3657             return false;
3658         }
3659         return registeredFontFiles.contains(fileName);
3660     }
3661 
3662     /*
3663      * This invocation is not in a privileged block because
3664      * all privileged operations (reading files and properties)
3665      * was conducted on the creation of the GE
3666      */
3667     public void
3668         createCompositeFonts(ConcurrentHashMap<String, Font2D> altNameCache,
3669                              boolean preferLocale,
3670                              boolean preferProportional) {
3671 
3672         FontConfiguration fontConfig =
3673             createFontConfiguration(preferLocale, preferProportional);
3674         initCompositeFonts(fontConfig, altNameCache);
3675     }
3676 
3677     /**
3678      * Returns all fonts installed in this environment.
3679      */
3680     public Font[] getAllInstalledFonts() {
3681         if (allFonts == null) {
3682             loadFonts();
3683             TreeMap<String, Font2D> fontMapNames = new TreeMap<>();
3684             /* warning: the number of composite fonts could change dynamically
3685              * if applications are allowed to create them. "allfonts" could
3686              * then be stale.
3687              */
3688             Font2D[] allfonts = getRegisteredFonts();
3689             for (int i=0; i < allfonts.length; i++) {
3690                 if (!(allfonts[i] instanceof NativeFont)) {
3691                     fontMapNames.put(allfonts[i].getFontName(null),
3692                                      allfonts[i]);
3693                 }
3694             }
3695 
3696             String[] platformNames = getFontNamesFromPlatform();
3697             if (platformNames != null) {
3698                 for (int i=0; i<platformNames.length; i++) {
3699                     if (!isNameForRegisteredFile(platformNames[i])) {
3700                         fontMapNames.put(platformNames[i], null);
3701                     }
3702                 }
3703             }
3704 
3705             String[] fontNames = null;
3706             if (fontMapNames.size() > 0) {
3707                 fontNames = new String[fontMapNames.size()];
3708                 Object [] keyNames = fontMapNames.keySet().toArray();
3709                 for (int i=0; i < keyNames.length; i++) {
3710                     fontNames[i] = (String)keyNames[i];
3711                 }
3712             }
3713             Font[] fonts = new Font[fontNames.length];
3714             for (int i=0; i < fontNames.length; i++) {
3715                 fonts[i] = new Font(fontNames[i], Font.PLAIN, 1);
3716                 Font2D f2d = fontMapNames.get(fontNames[i]);
3717                 if (f2d  != null) {
3718                     FontAccess.getFontAccess().setFont2D(fonts[i], f2d.handle);
3719                 }
3720             }
3721             allFonts = fonts;
3722         }
3723 
3724         Font []copyFonts = new Font[allFonts.length];
3725         System.arraycopy(allFonts, 0, copyFonts, 0, allFonts.length);
3726         return copyFonts;
3727     }
3728 
3729     /**
3730      * Get a list of installed fonts in the requested {@link Locale}.
3731      * The list contains the fonts Family Names.
3732      * If Locale is null, the default locale is used.
3733      *
3734      * @param requestedLocale, if null the default locale is used.
3735      * @return list of installed fonts in the system.
3736      */
3737     public String[] getInstalledFontFamilyNames(Locale requestedLocale) {
3738         if (requestedLocale == null) {
3739             requestedLocale = Locale.getDefault();
3740         }
3741         if (allFamilies != null && lastDefaultLocale != null &&
3742             requestedLocale.equals(lastDefaultLocale)) {
3743                 String[] copyFamilies = new String[allFamilies.length];
3744                 System.arraycopy(allFamilies, 0, copyFamilies,
3745                                  0, allFamilies.length);
3746                 return copyFamilies;
3747         }
3748 
3749         TreeMap<String,String> familyNames = new TreeMap<String,String>();
3750         //  these names are always there and aren't localised
3751         String str;
3752         str = Font.SERIF;         familyNames.put(str.toLowerCase(), str);
3753         str = Font.SANS_SERIF;    familyNames.put(str.toLowerCase(), str);
3754         str = Font.MONOSPACED;    familyNames.put(str.toLowerCase(), str);
3755         str = Font.DIALOG;        familyNames.put(str.toLowerCase(), str);
3756         str = Font.DIALOG_INPUT;  familyNames.put(str.toLowerCase(), str);
3757 
3758         /* Platform APIs may be used to get the set of available family
3759          * names for the current default locale so long as it is the same
3760          * as the start-up system locale, rather than loading all fonts.
3761          */
3762         if (requestedLocale.equals(getSystemStartupLocale()) &&
3763             getFamilyNamesFromPlatform(familyNames, requestedLocale)) {
3764             /* Augment platform names with JRE font family names */
3765             getJREFontFamilyNames(familyNames, requestedLocale);
3766         } else {
3767             loadFontFiles();
3768             Font2D[] physicalfonts = getPhysicalFonts();
3769             for (int i=0; i < physicalfonts.length; i++) {
3770                 if (!(physicalfonts[i] instanceof NativeFont)) {
3771                     String name =
3772                         physicalfonts[i].getFamilyName(requestedLocale);
3773                     familyNames.put(name.toLowerCase(requestedLocale), name);
3774                 }
3775             }
3776         }
3777 
3778         // Add any native font family names here
3779         addNativeFontFamilyNames(familyNames, requestedLocale);
3780 
3781         String[] retval =  new String[familyNames.size()];
3782         Object [] keyNames = familyNames.keySet().toArray();
3783         for (int i=0; i < keyNames.length; i++) {
3784             retval[i] = familyNames.get(keyNames[i]);
3785         }
3786         if (requestedLocale.equals(Locale.getDefault())) {
3787             lastDefaultLocale = requestedLocale;
3788             allFamilies = new String[retval.length];
3789             System.arraycopy(retval, 0, allFamilies, 0, allFamilies.length);
3790         }
3791         return retval;
3792     }
3793 
3794     // Provides an aperture to add native font family names to the map
3795     protected void addNativeFontFamilyNames(TreeMap<String, String> familyNames, Locale requestedLocale) { }
3796 
3797     public void register1dot0Fonts() {
3798         java.security.AccessController.doPrivileged(
3799                             new java.security.PrivilegedAction<Object>() {
3800             public Object run() {
3801                 String type1Dir = "/usr/openwin/lib/X11/fonts/Type1";
3802                 registerFontsInDir(type1Dir, true, Font2D.TYPE1_RANK,
3803                                    false, false);
3804                 return null;
3805             }
3806         });
3807     }
3808 
3809     /* Really we need only the JRE fonts family names, but there's little
3810      * overhead in doing this the easy way by adding all the currently
3811      * known fonts.
3812      */
3813     protected void getJREFontFamilyNames(TreeMap<String,String> familyNames,
3814                                          Locale requestedLocale) {
3815         registerDeferredJREFonts(jreFontDirName);
3816         Font2D[] physicalfonts = getPhysicalFonts();
3817         for (int i=0; i < physicalfonts.length; i++) {
3818             if (!(physicalfonts[i] instanceof NativeFont)) {
3819                 String name =
3820                     physicalfonts[i].getFamilyName(requestedLocale);
3821                 familyNames.put(name.toLowerCase(requestedLocale), name);
3822             }
3823         }
3824     }
3825 
3826     /**
3827      * Default locale can be changed but we need to know the initial locale
3828      * as that is what is used by native code. Changing Java default locale
3829      * doesn't affect that.
3830      * Returns the locale in use when using native code to communicate
3831      * with platform APIs. On windows this is known as the "system" locale,
3832      * and it is usually the same as the platform locale, but not always,
3833      * so this method also checks an implementation property used only
3834      * on windows and uses that if set.
3835      */
3836     private static Locale systemLocale = null;
3837     private static Locale getSystemStartupLocale() {
3838         if (systemLocale == null) {
3839             systemLocale = (Locale)
3840                 java.security.AccessController.doPrivileged(
3841                                     new java.security.PrivilegedAction<Object>() {
3842             public Object run() {
3843                 /* On windows the system locale may be different than the
3844                  * user locale. This is an unsupported configuration, but
3845                  * in that case we want to return a dummy locale that will
3846                  * never cause a match in the usage of this API. This is
3847                  * important because Windows documents that the family
3848                  * names of fonts are enumerated using the language of
3849                  * the system locale. BY returning a dummy locale in that
3850                  * case we do not use the platform API which would not
3851                  * return us the names we want.
3852                  */
3853                 String fileEncoding = System.getProperty("file.encoding", "");
3854                 String sysEncoding = System.getProperty("sun.jnu.encoding");
3855                 if (sysEncoding != null && !sysEncoding.equals(fileEncoding)) {
3856                     return Locale.ROOT;
3857                 }
3858 
3859                 String language = System.getProperty("user.language", "en");
3860                 String country  = System.getProperty("user.country","");
3861                 String variant  = System.getProperty("user.variant","");
3862                 return new Locale(language, country, variant);
3863             }
3864         });
3865         }
3866         return systemLocale;
3867     }
3868 
3869     void addToPool(FileFont font) {
3870 
3871         FileFont fontFileToClose = null;
3872         int freeSlot = -1;
3873 
3874         synchronized (fontFileCache) {
3875             /* Avoid duplicate entries in the pool, and don't close() it,
3876              * since this method is called only from within open().
3877              * Seeing a duplicate is most likely to happen if the thread
3878              * was interrupted during a read, forcing perhaps repeated
3879              * close and open calls and it eventually it ends up pointing
3880              * at the same slot.
3881              */
3882             for (int i=0;i<CHANNELPOOLSIZE;i++) {
3883                 if (fontFileCache[i] == font) {
3884                     return;
3885                 }
3886                 if (fontFileCache[i] == null && freeSlot < 0) {
3887                     freeSlot = i;
3888                 }
3889             }
3890             if (freeSlot >= 0) {
3891                 fontFileCache[freeSlot] = font;
3892                 return;
3893             } else {
3894                 /* replace with new font. */
3895                 fontFileToClose = fontFileCache[lastPoolIndex];
3896                 fontFileCache[lastPoolIndex] = font;
3897                 /* lastPoolIndex is updated so that the least recently opened
3898                  * file will be closed next.
3899                  */
3900                 lastPoolIndex = (lastPoolIndex+1) % CHANNELPOOLSIZE;
3901             }
3902         }
3903         /* Need to close the font file outside of the synchronized block,
3904          * since its possible some other thread is in an open() call on
3905          * this font file, and could be holding its lock and the pool lock.
3906          * Releasing the pool lock allows that thread to continue, so it can
3907          * then release the lock on this font, allowing the close() call
3908          * below to proceed.
3909          * Also, calling close() is safe because any other thread using
3910          * the font we are closing() synchronizes all reading, so we
3911          * will not close the file while its in use.
3912          */
3913         if (fontFileToClose != null) {
3914             fontFileToClose.close();
3915         }
3916     }
3917 
3918     protected FontUIResource getFontConfigFUIR(String family, int style,
3919                                                int size)
3920     {
3921         return new FontUIResource(family, style, size);
3922     }
3923 }