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