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