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