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