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