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