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