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