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