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