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