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