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