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