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