6869705: Missing files of CR6795908, FontManager refactoring
authorrkennke
Fri, 07 Aug 2009 19:36:28 +0200
changeset 3929 de1a48b7aa41
parent 3928 be186a33df9b
child 3930 738bc2599ca8
6869705: Missing files of CR6795908, FontManager refactoring Reviewed-by: prr, igor
jdk/src/share/classes/sun/font/FontAccess.java
jdk/src/share/classes/sun/font/FontManagerFactory.java
jdk/src/share/classes/sun/font/FontManagerForSGE.java
jdk/src/share/classes/sun/font/FontUtilities.java
jdk/src/share/classes/sun/font/SunFontManager.java
jdk/src/solaris/classes/sun/awt/X11FontManager.java
jdk/src/solaris/classes/sun/font/FontConfigManager.java
jdk/src/windows/classes/sun/awt/Win32FontManager.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/font/FontAccess.java	Fri Aug 07 19:36:28 2009 +0200
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.font;
+
+import java.awt.Font;
+
+public abstract class FontAccess {
+
+    private static FontAccess access;
+    public static synchronized void setFontAccess(FontAccess acc) {
+        if (access != null) {
+            throw new InternalError("Attempt to set FontAccessor twice");
+        }
+        access = acc;
+    }
+
+    public static synchronized FontAccess getFontAccess() {
+        return access;
+    }
+
+    public abstract Font2D getFont2D(Font f);
+    public abstract void setFont2D(Font f, Font2DHandle h);
+    public abstract void setCreatedFont(Font f);
+    public abstract boolean isCreatedFont(Font f);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/font/FontManagerFactory.java	Fri Aug 07 19:36:28 2009 +0200
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.font;
+
+import java.awt.AWTError;
+import java.awt.Font;
+import java.awt.GraphicsEnvironment;
+import java.awt.Toolkit;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import sun.security.action.GetPropertyAction;
+
+
+/**
+ * Factory class used to retrieve a valid FontManager instance for the current
+ * platform.
+ *
+ * A default implementation is given for Linux, Solaris and Windows.
+ * You can alter the behaviour of the {@link #getInstance()} method by setting
+ * the {@code sun.font.fontmanager} property. For example:
+ * {@code sun.font.fontmanager=sun.awt.X11FontManager}
+ */
+public final class FontManagerFactory {
+
+    /** Our singleton instance. */
+    private static FontManager instance = null;
+
+    private static final String DEFAULT_CLASS;
+    static {
+        if (FontUtilities.isWindows)
+            DEFAULT_CLASS = "sun.awt.Win32FontManager";
+        else
+            DEFAULT_CLASS = "sun.awt.X11FontManager";
+    }
+
+    /**
+     * Get a valid FontManager implementation for the current platform.
+     *
+     * @return a valid FontManager instance for the current platform
+     */
+    public static synchronized FontManager getInstance() {
+
+        if (instance != null) {
+            return instance;
+        }
+
+        String fmClassName = AccessController.doPrivileged(
+                new GetPropertyAction("sun.font.fontmanager",
+                                      DEFAULT_CLASS));
+
+        try {
+            @SuppressWarnings("unchecked")
+            ClassLoader cl = (ClassLoader)
+                AccessController.doPrivileged(new PrivilegedAction() {
+                    public Object run() {
+                        return ClassLoader.getSystemClassLoader();
+                    }
+                });
+
+            @SuppressWarnings("unchecked")
+            Class fmClass = Class.forName(fmClassName, true, cl);
+            instance = (FontManager) fmClass.newInstance();
+
+        } catch (ClassNotFoundException ex) {
+            InternalError err = new InternalError();
+            err.initCause(ex);
+            throw err;
+
+        } catch (InstantiationException ex) {
+            InternalError err = new InternalError();
+            err.initCause(ex);
+            throw err;
+
+        } catch (IllegalAccessException ex) {
+            InternalError err = new InternalError();
+            err.initCause(ex);
+            throw err;
+        }
+
+        return instance;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/font/FontManagerForSGE.java	Fri Aug 07 19:36:28 2009 +0200
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.font;
+
+import java.awt.Font;
+import java.util.Locale;
+import java.util.TreeMap;
+
+/**
+ * This is an extension of the {@link FontManager} interface which has to
+ * be implemented on systems that want to use SunGraphicsEnvironment. It
+ * adds a couple of methods that are only required by SGE. Graphics
+ * implementations that use their own GraphicsEnvironment are not required
+ * to implement this and can use plain FontManager instead.
+ */
+public interface FontManagerForSGE extends FontManager {
+
+    /**
+     * Return an array of created Fonts, or null, if no fonts were created yet.
+     */
+    public Font[] getCreatedFonts();
+
+    /**
+     * Similar to getCreatedFonts, but returns a TreeMap of fonts by family name.
+     */
+    public TreeMap<String, String> getCreatedFontFamilyNames();
+
+    /**
+     * Returns all fonts installed in this environment.
+     */
+    public Font[] getAllInstalledFonts();
+
+    public String[] getInstalledFontFamilyNames(Locale requestedLocale);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/font/FontUtilities.java	Fri Aug 07 19:36:28 2009 +0200
@@ -0,0 +1,486 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.font;
+
+import java.awt.Font;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
+import java.security.AccessController;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.swing.plaf.FontUIResource;
+
+import sun.security.action.GetPropertyAction;
+
+/**
+ * A collection of utility methods.
+ */
+public final class FontUtilities {
+
+    public static final boolean isSolaris;
+
+    public static final boolean isLinux;
+
+    public static final boolean isSolaris8;
+
+    public static final boolean isSolaris9;
+
+    public static final boolean isOpenSolaris;
+
+    public static final boolean useT2K;
+
+    public static final boolean isWindows;
+
+    public static final boolean isOpenJDK;
+
+    static final String LUCIDA_FILE_NAME = "LucidaSansRegular.ttf";
+
+    // This static initializer block figures out the OS constants.
+    static {
+
+        String osName = AccessController.doPrivileged(
+                                new GetPropertyAction("os.name", "unknownOS"));
+        isSolaris = osName.startsWith("SunOS");
+
+        isLinux = osName.startsWith("Linux");
+
+        String t2kStr = AccessController.doPrivileged(
+                              new GetPropertyAction("sun.java2d.font.scaler"));
+        if (t2kStr != null) {
+            useT2K = "t2k".equals(t2kStr);
+        } else {
+            useT2K = false;
+        }
+        if (isSolaris) {
+            String version = AccessController.doPrivileged(
+                                   new GetPropertyAction("os.version", "0.0"));
+            isSolaris8 = version.startsWith("5.8");
+            isSolaris9 = version.startsWith("5.9");
+            float ver = Float.parseFloat(version);
+            if (ver > 5.10f) {
+                File f = new File("/etc/release");
+                String line = null;
+                try {
+                    FileInputStream fis = new FileInputStream(f);
+                    InputStreamReader isr = new InputStreamReader(
+                                                            fis, "ISO-8859-1");
+                    BufferedReader br = new BufferedReader(isr);
+                    line = br.readLine();
+                    fis.close();
+                } catch (Exception ex) {
+                    // Nothing to do here.
+                }
+                if (line != null && line.indexOf("OpenSolaris") >= 0) {
+                    isOpenSolaris = true;
+                } else {
+                    isOpenSolaris = false;
+                }
+            } else {
+                isOpenSolaris= false;
+            }
+        } else {
+            isSolaris8 = false;
+            isSolaris9 = false;
+            isOpenSolaris = false;
+        }
+        isWindows = osName.startsWith("Windows");
+        String jreLibDirName = AccessController.doPrivileged(
+               new GetPropertyAction("java.home","")) + File.separator + "lib";
+        String jreFontDirName = jreLibDirName + File.separator + "fonts";
+        File lucidaFile =
+                new File(jreFontDirName + File.separator + LUCIDA_FILE_NAME);
+        isOpenJDK = !lucidaFile.exists();
+    }
+
+    /**
+     * Referenced by code in the JDK which wants to test for the
+     * minimum char code for which layout may be required.
+     * Note that even basic latin text can benefit from ligatures,
+     * eg "ffi" but we presently apply those only if explicitly
+     * requested with TextAttribute.LIGATURES_ON.
+     * The value here indicates the lowest char code for which failing
+     * to invoke layout would prevent acceptable rendering.
+     */
+    public static final int MIN_LAYOUT_CHARCODE = 0x0300;
+
+    /**
+     * Referenced by code in the JDK which wants to test for the
+     * maximum char code for which layout may be required.
+     * Note this does not account for supplementary characters
+     * where the caller interprets 'layout' to mean any case where
+     * one 'char' (ie the java type char) does not map to one glyph
+     */
+    public static final int MAX_LAYOUT_CHARCODE = 0x206F;
+
+    private static boolean debugFonts = false;
+    private static Logger logger = null;
+    private static boolean logging;
+
+    static {
+
+        String debugLevel =
+            System.getProperty("sun.java2d.debugfonts");
+
+        if (debugLevel != null && !debugLevel.equals("false")) {
+            debugFonts = true;
+            logger = Logger.getLogger("sun.java2d");
+            if (debugLevel.equals("warning")) {
+                logger.setLevel(Level.WARNING);
+            } else if (debugLevel.equals("severe")) {
+                logger.setLevel(Level.SEVERE);
+            }
+        }
+
+        if (debugFonts) {
+            logger = Logger.getLogger("sun.java2d", null);
+            logging = logger.getLevel() != Level.OFF;
+        }
+
+    }
+
+    /**
+     * Calls the private getFont2D() method in java.awt.Font objects.
+     *
+     * @param font the font object to call
+     *
+     * @return the Font2D object returned by Font.getFont2D()
+     */
+    public static Font2D getFont2D(Font font) {
+        return FontAccess.getFontAccess().getFont2D(font);
+    }
+
+    /**
+     * If there is anything in the text which triggers a case
+     * where char->glyph does not map 1:1 in straightforward
+     * left->right ordering, then this method returns true.
+     * Scripts which might require it but are not treated as such
+     * due to JDK implementations will not return true.
+     * ie a 'true' return is an indication of the treatment by
+     * the implementation.
+     * Whether supplementary characters should be considered is dependent
+     * on the needs of the caller. Since this method accepts the 'char' type
+     * then such chars are always represented by a pair. From a rendering
+     * perspective these will all (in the cases I know of) still be one
+     * unicode character -> one glyph. But if a caller is using this to
+     * discover any case where it cannot make naive assumptions about
+     * the number of chars, and how to index through them, then it may
+     * need the option to have a 'true' return in such a case.
+     */
+    public static boolean isComplexText(char [] chs, int start, int limit) {
+
+        for (int i = start; i < limit; i++) {
+            if (chs[i] < MIN_LAYOUT_CHARCODE) {
+                continue;
+            }
+            else if (isNonSimpleChar(chs[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /* This is almost the same as the method above, except it takes a
+     * char which means it may include undecoded surrogate pairs.
+     * The distinction is made so that code which needs to identify all
+     * cases in which we do not have a simple mapping from
+     * char->unicode character->glyph can be be identified.
+     * For example measurement cannot simply sum advances of 'chars',
+     * the caret in editable text cannot advance one 'char' at a time, etc.
+     * These callers really are asking for more than whether 'layout'
+     * needs to be run, they need to know if they can assume 1->1
+     * char->glyph mapping.
+     */
+    public static boolean isNonSimpleChar(char ch) {
+        return
+            isComplexCharCode(ch) ||
+            (ch >= CharToGlyphMapper.HI_SURROGATE_START &&
+             ch <= CharToGlyphMapper.LO_SURROGATE_END);
+    }
+
+    /* If the character code falls into any of a number of unicode ranges
+     * where we know that simple left->right layout mapping chars to glyphs
+     * 1:1 and accumulating advances is going to produce incorrect results,
+     * we want to know this so the caller can use a more intelligent layout
+     * approach. A caller who cares about optimum performance may want to
+     * check the first case and skip the method call if its in that range.
+     * Although there's a lot of tests in here, knowing you can skip
+     * CTL saves a great deal more. The rest of the checks are ordered
+     * so that rather than checking explicitly if (>= start & <= end)
+     * which would mean all ranges would need to be checked so be sure
+     * CTL is not needed, the method returns as soon as it recognises
+     * the code point is outside of a CTL ranges.
+     * NOTE: Since this method accepts an 'int' it is asssumed to properly
+     * represent a CHARACTER. ie it assumes the caller has already
+     * converted surrogate pairs into supplementary characters, and so
+     * can handle this case and doesn't need to be told such a case is
+     * 'complex'.
+     */
+    public static boolean isComplexCharCode(int code) {
+
+        if (code < MIN_LAYOUT_CHARCODE || code > MAX_LAYOUT_CHARCODE) {
+            return false;
+        }
+        else if (code <= 0x036f) {
+            // Trigger layout for combining diacriticals 0x0300->0x036f
+            return true;
+        }
+        else if (code < 0x0590) {
+            // No automatic layout for Greek, Cyrillic, Armenian.
+             return false;
+        }
+        else if (code <= 0x06ff) {
+            // Hebrew 0590 - 05ff
+            // Arabic 0600 - 06ff
+            return true;
+        }
+        else if (code < 0x0900) {
+            return false; // Syriac and Thaana
+        }
+        else if (code <= 0x0e7f) {
+            // if Indic, assume shaping for conjuncts, reordering:
+            // 0900 - 097F Devanagari
+            // 0980 - 09FF Bengali
+            // 0A00 - 0A7F Gurmukhi
+            // 0A80 - 0AFF Gujarati
+            // 0B00 - 0B7F Oriya
+            // 0B80 - 0BFF Tamil
+            // 0C00 - 0C7F Telugu
+            // 0C80 - 0CFF Kannada
+            // 0D00 - 0D7F Malayalam
+            // 0D80 - 0DFF Sinhala
+            // 0E00 - 0E7F if Thai, assume shaping for vowel, tone marks
+            return true;
+        }
+        else if (code < 0x1780) {
+            return false;
+        }
+        else if (code <= 0x17ff) { // 1780 - 17FF Khmer
+            return true;
+        }
+        else if (code < 0x200c) {
+            return false;
+        }
+        else if (code <= 0x200d) { //  zwj or zwnj
+            return true;
+        }
+        else if (code >= 0x202a && code <= 0x202e) { // directional control
+            return true;
+        }
+        else if (code >= 0x206a && code <= 0x206f) { // directional control
+            return true;
+        }
+        return false;
+    }
+
+    public static Logger getLogger() {
+        return logger;
+    }
+
+    public static boolean isLogging() {
+        return logging;
+    }
+
+    public static boolean debugFonts() {
+        return debugFonts;
+    }
+
+
+    // The following methods are used by Swing.
+
+    /* Revise the implementation to in fact mean "font is a composite font.
+     * This ensures that Swing components will always benefit from the
+     * fall back fonts
+     */
+    public static boolean fontSupportsDefaultEncoding(Font font) {
+        return getFont2D(font) instanceof CompositeFont;
+    }
+
+    /**
+     * This method is provided for internal and exclusive use by Swing.
+     *
+     * It may be used in conjunction with fontSupportsDefaultEncoding(Font)
+     * In the event that a desktop properties font doesn't directly
+     * support the default encoding, (ie because the host OS supports
+     * adding support for the current locale automatically for native apps),
+     * then Swing calls this method to get a font which  uses the specified
+     * font for the code points it covers, but also supports this locale
+     * just as the standard composite fonts do.
+     * Note: this will over-ride any setting where an application
+     * specifies it prefers locale specific composite fonts.
+     * The logic for this, is that this method is used only where the user or
+     * application has specified that the native L&F be used, and that
+     * we should honour that request to use the same font as native apps use.
+     *
+     * The behaviour of this method is to construct a new composite
+     * Font object that uses the specified physical font as its first
+     * component, and adds all the components of "dialog" as fall back
+     * components.
+     * The method currently assumes that only the size and style attributes
+     * are set on the specified font. It doesn't copy the font transform or
+     * other attributes because they aren't set on a font created from
+     * the desktop. This will need to be fixed if use is broadened.
+     *
+     * Operations such as Font.deriveFont will work properly on the
+     * font returned by this method for deriving a different point size.
+     * Additionally it tries to support a different style by calling
+     * getNewComposite() below. That also supports replacing slot zero
+     * with a different physical font but that is expected to be "rare".
+     * Deriving with a different style is needed because its been shown
+     * that some applications try to do this for Swing FontUIResources.
+     * Also operations such as new Font(font.getFontName(..), Font.PLAIN, 14);
+     * will NOT yield the same result, as the new underlying CompositeFont
+     * cannot be "looked up" in the font registry.
+     * This returns a FontUIResource as that is the Font sub-class needed
+     * by Swing.
+     * Suggested usage is something like :
+     * FontUIResource fuir;
+     * Font desktopFont = getDesktopFont(..);
+     * // NOTE even if fontSupportsDefaultEncoding returns true because
+     * // you get Tahoma and are running in an English locale, you may
+     * // still want to just call getCompositeFontUIResource() anyway
+     * // as only then will you get fallback fonts - eg for CJK.
+     * if (FontManager.fontSupportsDefaultEncoding(desktopFont)) {
+     *   fuir = new FontUIResource(..);
+     * } else {
+     *   fuir = FontManager.getCompositeFontUIResource(desktopFont);
+     * }
+     * return fuir;
+     */
+    public static FontUIResource getCompositeFontUIResource(Font font) {
+
+        FontUIResource fuir =
+            new FontUIResource(font.getName(),font.getStyle(),font.getSize());
+        Font2D font2D = FontUtilities.getFont2D(font);
+
+        if (!(font2D instanceof PhysicalFont)) {
+            /* Swing should only be calling this when a font is obtained
+             * from desktop properties, so should generally be a physical font,
+             * an exception might be for names like "MS Serif" which are
+             * automatically mapped to "Serif", so there's no need to do
+             * anything special in that case. But note that suggested usage
+             * is first to call fontSupportsDefaultEncoding(Font) and this
+             * method should not be called if that were to return true.
+             */
+             return fuir;
+        }
+
+        FontManager fm = FontManagerFactory.getInstance();
+        CompositeFont dialog2D =
+          (CompositeFont) fm.findFont2D("dialog", font.getStyle(), FontManager.NO_FALLBACK);
+        if (dialog2D == null) { /* shouldn't happen */
+            return fuir;
+        }
+        PhysicalFont physicalFont = (PhysicalFont)font2D;
+        CompositeFont compFont = new CompositeFont(physicalFont, dialog2D);
+        FontAccess.getFontAccess().setFont2D(fuir, compFont.handle);
+        /* marking this as a created font is needed as only created fonts
+         * copy their creator's handles.
+         */
+        FontAccess.getFontAccess().setCreatedFont(fuir);
+        return fuir;
+    }
+
+   /* A small "map" from GTK/fontconfig names to the equivalent JDK
+    * logical font name.
+    */
+    private static final String[][] nameMap = {
+        {"sans",       "sansserif"},
+        {"sans-serif", "sansserif"},
+        {"serif",      "serif"},
+        {"monospace",  "monospaced"}
+    };
+
+    public static String mapFcName(String name) {
+        for (int i = 0; i < nameMap.length; i++) {
+            if (name.equals(nameMap[i][0])) {
+                return nameMap[i][1];
+            }
+        }
+        return null;
+    }
+
+
+    /* This is called by Swing passing in a fontconfig family name
+     * such as "sans". In return Swing gets a FontUIResource instance
+     * that has queried fontconfig to resolve the font(s) used for this.
+     * Fontconfig will if asked return a list of fonts to give the largest
+     * possible code point coverage.
+     * For now we use only the first font returned by fontconfig, and
+     * back it up with the most closely matching JDK logical font.
+     * Essentially this means pre-pending what we return now with fontconfig's
+     * preferred physical font. This could lead to some duplication in cases,
+     * if we already included that font later. We probably should remove such
+     * duplicates, but it is not a significant problem. It can be addressed
+     * later as part of creating a Composite which uses more of the
+     * same fonts as fontconfig. At that time we also should pay more
+     * attention to the special rendering instructions fontconfig returns,
+     * such as whether we should prefer embedded bitmaps over antialiasing.
+     * There's no way to express that via a Font at present.
+     */
+    public static FontUIResource getFontConfigFUIR(String fcFamily,
+                                                   int style, int size) {
+
+        String mapped = mapFcName(fcFamily);
+        if (mapped == null) {
+            mapped = "sansserif";
+        }
+
+        FontUIResource fuir;
+        FontManager fm = FontManagerFactory.getInstance();
+        if (fm instanceof SunFontManager) {
+            SunFontManager sfm = (SunFontManager) fm;
+            fuir = sfm.getFontConfigFUIR(mapped, style, size);
+        } else {
+            fuir = new FontUIResource(mapped, style, size);
+        }
+        return fuir;
+    }
+
+
+    /**
+     * Used by windows printing to assess if a font is likely to
+     * be layout compatible with JDK
+     * TrueType fonts should be, but if they have no GPOS table,
+     * but do have a GSUB table, then they are probably older
+     * fonts GDI handles differently.
+     */
+    public static boolean textLayoutIsCompatible(Font font) {
+
+        Font2D font2D = getFont2D(font);
+        if (font2D instanceof TrueTypeFont) {
+            TrueTypeFont ttf = (TrueTypeFont) font2D;
+            return
+                ttf.getDirectoryEntry(TrueTypeFont.GSUBTag) == null ||
+                ttf.getDirectoryEntry(TrueTypeFont.GPOSTag) != null;
+        } else {
+            return false;
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/font/SunFontManager.java	Fri Aug 07 19:36:28 2009 +0200
@@ -0,0 +1,3675 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.font;
+
+import java.awt.Font;
+import java.awt.FontFormatException;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.StringTokenizer;
+import java.util.TreeMap;
+import java.util.Vector;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.swing.plaf.FontUIResource;
+import sun.awt.AppContext;
+import sun.awt.FontConfiguration;
+import sun.awt.SunToolkit;
+import sun.java2d.FontSupport;
+
+/**
+ * The base implementation of the {@link FontManager} interface. It implements
+ * the platform independent, shared parts of OpenJDK's FontManager
+ * implementations. The platform specific parts are declared as abstract
+ * methods that have to be implemented by specific implementations.
+ */
+public abstract class SunFontManager implements FontSupport, FontManagerForSGE {
+
+    private static class TTFilter implements FilenameFilter {
+        public boolean accept(File dir,String name) {
+            /* all conveniently have the same suffix length */
+            int offset = name.length()-4;
+            if (offset <= 0) { /* must be at least A.ttf */
+                return false;
+            } else {
+                return(name.startsWith(".ttf", offset) ||
+                       name.startsWith(".TTF", offset) ||
+                       name.startsWith(".ttc", offset) ||
+                       name.startsWith(".TTC", offset));
+            }
+        }
+    }
+
+    private static class T1Filter implements FilenameFilter {
+        public boolean accept(File dir,String name) {
+            if (noType1Font) {
+                return false;
+            }
+            /* all conveniently have the same suffix length */
+            int offset = name.length()-4;
+            if (offset <= 0) { /* must be at least A.pfa */
+                return false;
+            } else {
+                return(name.startsWith(".pfa", offset) ||
+                       name.startsWith(".pfb", offset) ||
+                       name.startsWith(".PFA", offset) ||
+                       name.startsWith(".PFB", offset));
+            }
+        }
+    }
+
+     private static class TTorT1Filter implements FilenameFilter {
+        public boolean accept(File dir, String name) {
+
+            /* all conveniently have the same suffix length */
+            int offset = name.length()-4;
+            if (offset <= 0) { /* must be at least A.ttf or A.pfa */
+                return false;
+            } else {
+                boolean isTT =
+                    name.startsWith(".ttf", offset) ||
+                    name.startsWith(".TTF", offset) ||
+                    name.startsWith(".ttc", offset) ||
+                    name.startsWith(".TTC", offset);
+                if (isTT) {
+                    return true;
+                } else if (noType1Font) {
+                    return false;
+                } else {
+                    return(name.startsWith(".pfa", offset) ||
+                           name.startsWith(".pfb", offset) ||
+                           name.startsWith(".PFA", offset) ||
+                           name.startsWith(".PFB", offset));
+                }
+            }
+        }
+    }
+
+     public static final int FONTFORMAT_NONE = -1;
+     public static final int FONTFORMAT_TRUETYPE = 0;
+     public static final int FONTFORMAT_TYPE1 = 1;
+     public static final int FONTFORMAT_T2K = 2;
+     public static final int FONTFORMAT_TTC = 3;
+     public static final int FONTFORMAT_COMPOSITE = 4;
+     public static final int FONTFORMAT_NATIVE = 5;
+
+     /* Pool of 20 font file channels chosen because some UTF-8 locale
+      * composite fonts can use up to 16 platform fonts (including the
+      * Lucida fall back). This should prevent channel thrashing when
+      * dealing with one of these fonts.
+      * The pool array stores the fonts, rather than directly referencing
+      * the channels, as the font needs to do the open/close work.
+      */
+     private static final int CHANNELPOOLSIZE = 20;
+     private int lastPoolIndex = 0;
+     private FileFont fontFileCache[] = new FileFont[CHANNELPOOLSIZE];
+
+    /* Need to implement a simple linked list scheme for fast
+     * traversal and lookup.
+     * Also want to "fast path" dialog so there's minimal overhead.
+     */
+    /* There are at exactly 20 composite fonts: 5 faces (but some are not
+     * usually different), in 4 styles. The array may be auto-expanded
+     * later if more are needed, eg for user-defined composites or locale
+     * variants.
+     */
+    private int maxCompFont = 0;
+    private CompositeFont [] compFonts = new CompositeFont[20];
+    private ConcurrentHashMap<String, CompositeFont>
+        compositeFonts = new ConcurrentHashMap<String, CompositeFont>();
+    private ConcurrentHashMap<String, PhysicalFont>
+        physicalFonts = new ConcurrentHashMap<String, PhysicalFont>();
+    private ConcurrentHashMap<String, PhysicalFont>
+        registeredFonts = new ConcurrentHashMap<String, PhysicalFont>();
+
+    /* given a full name find the Font. Remind: there's duplication
+     * here in that this contains the content of compositeFonts +
+     * physicalFonts.
+     */
+    private ConcurrentHashMap<String, Font2D>
+        fullNameToFont = new ConcurrentHashMap<String, Font2D>();
+
+    /* TrueType fonts have localised names. Support searching all
+     * of these before giving up on a name.
+     */
+    private HashMap<String, TrueTypeFont> localeFullNamesToFont;
+
+    private PhysicalFont defaultPhysicalFont;
+
+    static boolean longAddresses;
+    private boolean loaded1dot0Fonts = false;
+    boolean loadedAllFonts = false;
+    boolean loadedAllFontFiles = false;
+    HashMap<String,String> jreFontMap;
+    HashSet<String> jreLucidaFontFiles;
+    String[] jreOtherFontFiles;
+    boolean noOtherJREFontFiles = false; // initial assumption.
+
+    public static final String lucidaFontName = "Lucida Sans Regular";
+    public static String jreLibDirName;
+    public static String jreFontDirName;
+    private static HashSet<String> missingFontFiles = null;
+    private String defaultFontName;
+    private String defaultFontFileName;
+    protected HashSet registeredFontFiles = new HashSet();
+
+    private ArrayList badFonts;
+    /* fontPath is the location of all fonts on the system, excluding the
+     * JRE's own font directory but including any path specified using the
+     * sun.java2d.fontpath property. Together with that property,  it is
+     * initialised by the getPlatformFontPath() method
+     * This call must be followed by a call to registerFontDirs(fontPath)
+     * once any extra debugging path has been appended.
+     */
+    protected String fontPath;
+    private FontConfiguration fontConfig;
+    /* discoveredAllFonts is set to true when all fonts on the font path are
+     * discovered. This usually also implies opening, validating and
+     * registering, but an implementation may be optimized to avold this.
+     * So see also "loadedAllFontFiles"
+     */
+    private boolean discoveredAllFonts = false;
+
+    /* No need to keep consing up new instances - reuse a singleton.
+     * The trade-off is that these objects don't get GC'd.
+     */
+    private static final FilenameFilter ttFilter = new TTFilter();
+    private static final FilenameFilter t1Filter = new T1Filter();
+
+    private Font[] allFonts;
+    private String[] allFamilies; // cache for default locale only
+    private Locale lastDefaultLocale;
+
+    public static boolean noType1Font;
+
+    /* Used to indicate required return type from toArray(..); */
+    private static String[] STR_ARRAY = new String[0];
+
+    /**
+     * Deprecated, unsupported hack - actually invokes a bug!
+     * Left in for a customer, don't remove.
+     */
+    private boolean usePlatformFontMetrics = false;
+
+    /**
+     * Returns the global SunFontManager instance. This is similar to
+     * {@link FontManagerFactory#getInstance()} but it returns a
+     * SunFontManager instance instead. This is only used in internal classes
+     * where we can safely assume that a SunFontManager is to be used.
+     *
+     * @return the global SunFontManager instance
+     */
+    public static SunFontManager getInstance() {
+        FontManager fm = FontManagerFactory.getInstance();
+        return (SunFontManager) fm;
+    }
+
+    public FilenameFilter getTrueTypeFilter() {
+        return ttFilter;
+    }
+
+    public FilenameFilter getType1Filter() {
+        return t1Filter;
+    }
+
+    @Override
+    public boolean usingPerAppContextComposites() {
+        return _usingPerAppContextComposites;
+    }
+
+    private void initJREFontMap() {
+
+        /* Key is familyname+style value as an int.
+         * Value is filename containing the font.
+         * If no mapping exists, it means there is no font file for the style
+         * If the mapping exists but the file doesn't exist in the deferred
+         * list then it means its not installed.
+         * This looks like a lot of code and strings but if it saves even
+         * a single file being opened at JRE start-up there's a big payoff.
+         * Lucida Sans is probably the only important case as the others
+         * are rarely used. Consider removing the other mappings if there's
+         * no evidence they are useful in practice.
+         */
+        jreFontMap = new HashMap<String,String>();
+        jreLucidaFontFiles = new HashSet<String>();
+        if (isOpenJDK()) {
+            return;
+        }
+        /* Lucida Sans Family */
+        jreFontMap.put("lucida sans0",   "LucidaSansRegular.ttf");
+        jreFontMap.put("lucida sans1",   "LucidaSansDemiBold.ttf");
+        /* Lucida Sans full names (map Bold and DemiBold to same file) */
+        jreFontMap.put("lucida sans regular0", "LucidaSansRegular.ttf");
+        jreFontMap.put("lucida sans regular1", "LucidaSansDemiBold.ttf");
+        jreFontMap.put("lucida sans bold1", "LucidaSansDemiBold.ttf");
+        jreFontMap.put("lucida sans demibold1", "LucidaSansDemiBold.ttf");
+
+        /* Lucida Sans Typewriter Family */
+        jreFontMap.put("lucida sans typewriter0",
+                       "LucidaTypewriterRegular.ttf");
+        jreFontMap.put("lucida sans typewriter1", "LucidaTypewriterBold.ttf");
+        /* Typewriter full names (map Bold and DemiBold to same file) */
+        jreFontMap.put("lucida sans typewriter regular0",
+                       "LucidaTypewriter.ttf");
+        jreFontMap.put("lucida sans typewriter regular1",
+                       "LucidaTypewriterBold.ttf");
+        jreFontMap.put("lucida sans typewriter bold1",
+                       "LucidaTypewriterBold.ttf");
+        jreFontMap.put("lucida sans typewriter demibold1",
+                       "LucidaTypewriterBold.ttf");
+
+        /* Lucida Bright Family */
+        jreFontMap.put("lucida bright0", "LucidaBrightRegular.ttf");
+        jreFontMap.put("lucida bright1", "LucidaBrightDemiBold.ttf");
+        jreFontMap.put("lucida bright2", "LucidaBrightItalic.ttf");
+        jreFontMap.put("lucida bright3", "LucidaBrightDemiItalic.ttf");
+        /* Lucida Bright full names (map Bold and DemiBold to same file) */
+        jreFontMap.put("lucida bright regular0", "LucidaBrightRegular.ttf");
+        jreFontMap.put("lucida bright regular1", "LucidaBrightDemiBold.ttf");
+        jreFontMap.put("lucida bright regular2", "LucidaBrightItalic.ttf");
+        jreFontMap.put("lucida bright regular3", "LucidaBrightDemiItalic.ttf");
+        jreFontMap.put("lucida bright bold1", "LucidaBrightDemiBold.ttf");
+        jreFontMap.put("lucida bright bold3", "LucidaBrightDemiItalic.ttf");
+        jreFontMap.put("lucida bright demibold1", "LucidaBrightDemiBold.ttf");
+        jreFontMap.put("lucida bright demibold3","LucidaBrightDemiItalic.ttf");
+        jreFontMap.put("lucida bright italic2", "LucidaBrightItalic.ttf");
+        jreFontMap.put("lucida bright italic3", "LucidaBrightDemiItalic.ttf");
+        jreFontMap.put("lucida bright bold italic3",
+                       "LucidaBrightDemiItalic.ttf");
+        jreFontMap.put("lucida bright demibold italic3",
+                       "LucidaBrightDemiItalic.ttf");
+        for (String ffile : jreFontMap.values()) {
+            jreLucidaFontFiles.add(ffile);
+        }
+    }
+
+    static {
+
+        java.security.AccessController.doPrivileged(
+                                    new java.security.PrivilegedAction() {
+
+           public Object run() {
+               FontManagerNativeLibrary.load();
+
+               // JNI throws an exception if a class/method/field is not found,
+               // so there's no need to do anything explicit here.
+               initIDs();
+
+               switch (StrikeCache.nativeAddressSize) {
+               case 8: longAddresses = true; break;
+               case 4: longAddresses = false; break;
+               default: throw new RuntimeException("Unexpected address size");
+               }
+
+               noType1Font =
+                   "true".equals(System.getProperty("sun.java2d.noType1Font"));
+               jreLibDirName =
+                   System.getProperty("java.home","") + File.separator + "lib";
+               jreFontDirName = jreLibDirName + File.separator + "fonts";
+               File lucidaFile =
+                   new File(jreFontDirName + File.separator + FontUtilities.LUCIDA_FILE_NAME);
+
+               return null;
+           }
+        });
+    }
+
+    public TrueTypeFont getEUDCFont() {
+        // Overridden in Windows.
+        return null;
+    }
+
+    /* Initialise ptrs used by JNI methods */
+    private static native void initIDs();
+
+    @SuppressWarnings("unchecked")
+    protected SunFontManager() {
+
+        initJREFontMap();
+        java.security.AccessController.doPrivileged(
+                new java.security.PrivilegedAction() {
+                    public Object run() {
+                        File badFontFile =
+                            new File(jreFontDirName + File.separator +
+                                     "badfonts.txt");
+                        if (badFontFile.exists()) {
+                            FileInputStream fis = null;
+                            try {
+                                badFonts = new ArrayList();
+                                fis = new FileInputStream(badFontFile);
+                                InputStreamReader isr = new InputStreamReader(fis);
+                                BufferedReader br = new BufferedReader(isr);
+                                while (true) {
+                                    String name = br.readLine();
+                                    if (name == null) {
+                                        break;
+                                    } else {
+                                        if (FontUtilities.debugFonts()) {
+                                            FontUtilities.getLogger().warning("read bad font: " +
+                                                           name);
+                                        }
+                                        badFonts.add(name);
+                                    }
+                                }
+                            } catch (IOException e) {
+                                try {
+                                    if (fis != null) {
+                                        fis.close();
+                                    }
+                                } catch (IOException ioe) {
+                                }
+                            }
+                        }
+
+                        /* Here we get the fonts in jre/lib/fonts and register
+                         * them so they are always available and preferred over
+                         * other fonts. This needs to be registered before the
+                         * composite fonts as otherwise some native font that
+                         * corresponds may be found as we don't have a way to
+                         * handle two fonts of the same name, so the JRE one
+                         * must be the first one registered. Pass "true" to
+                         * registerFonts method as on-screen these JRE fonts
+                         * always go through the T2K rasteriser.
+                         */
+                        if (FontUtilities.isLinux) {
+                            /* Linux font configuration uses these fonts */
+                            registerFontDir(jreFontDirName);
+                        }
+                        registerFontsInDir(jreFontDirName, true, Font2D.JRE_RANK,
+                                           true, false);
+
+                        /* Create the font configuration and get any font path
+                         * that might be specified.
+                         */
+                        fontConfig = createFontConfiguration();
+                        if (isOpenJDK()) {
+                            String[] fontInfo = getDefaultPlatformFont();
+                            defaultFontName = fontInfo[0];
+                            defaultFontFileName = fontInfo[1];
+                        }
+
+                        String extraFontPath = fontConfig.getExtraFontPath();
+
+                        /* In prior releases the debugging font path replaced
+                         * all normally located font directories except for the
+                         * JRE fonts dir. This directory is still always located
+                         * and placed at the head of the path but as an
+                         * augmentation to the previous behaviour the
+                         * changes below allow you to additionally append to
+                         * the font path by starting with append: or prepend by
+                         * starting with a prepend: sign. Eg: to append
+                         * -Dsun.java2d.fontpath=append:/usr/local/myfonts
+                         * and to prepend
+                         * -Dsun.java2d.fontpath=prepend:/usr/local/myfonts Disp
+                         *
+                         * If there is an appendedfontpath it in the font
+                         * configuration it is used instead of searching the
+                         * system for dirs.
+                         * The behaviour of append and prepend is then similar
+                         * to the normal case. ie it goes after what
+                         * you prepend and * before what you append. If the
+                         * sun.java2d.fontpath property is used, but it
+                         * neither the append or prepend syntaxes is used then
+                         * as except for the JRE dir the path is replaced and it
+                         * is up to you to make sure that all the right
+                         * directories are located. This is platform and
+                         * locale-specific so its almost impossible to get
+                         * right, so it should be used with caution.
+                         */
+                        boolean prependToPath = false;
+                        boolean appendToPath = false;
+                        String dbgFontPath =
+                            System.getProperty("sun.java2d.fontpath");
+
+                        if (dbgFontPath != null) {
+                            if (dbgFontPath.startsWith("prepend:")) {
+                                prependToPath = true;
+                                dbgFontPath =
+                                    dbgFontPath.substring("prepend:".length());
+                            } else if (dbgFontPath.startsWith("append:")) {
+                                appendToPath = true;
+                                dbgFontPath =
+                                    dbgFontPath.substring("append:".length());
+                            }
+                        }
+
+                        if (FontUtilities.debugFonts()) {
+                            Logger logger = FontUtilities.getLogger();
+                            logger.info("JRE font directory: " + jreFontDirName);
+                            logger.info("Extra font path: " + extraFontPath);
+                            logger.info("Debug font path: " + dbgFontPath);
+                        }
+
+                        if (dbgFontPath != null) {
+                            /* In debugging mode we register all the paths
+                             * Caution: this is a very expensive call on Solaris:-
+                             */
+                            fontPath = getPlatformFontPath(noType1Font);
+
+                            if (extraFontPath != null) {
+                                fontPath =
+                                    extraFontPath + File.pathSeparator + fontPath;
+                            }
+                            if (appendToPath) {
+                                fontPath =
+                                    fontPath + File.pathSeparator + dbgFontPath;
+                            } else if (prependToPath) {
+                                fontPath =
+                                    dbgFontPath + File.pathSeparator + fontPath;
+                            } else {
+                                fontPath = dbgFontPath;
+                            }
+                            registerFontDirs(fontPath);
+                        } else if (extraFontPath != null) {
+                            /* If the font configuration contains an
+                             * "appendedfontpath" entry, it is interpreted as a
+                             * set of locations that should always be registered.
+                             * It may be additional to locations normally found
+                             * for that place, or it may be locations that need
+                             * to have all their paths registered to locate all
+                             * the needed platform names.
+                             * This is typically when the same .TTF file is
+                             * referenced from multiple font.dir files and all
+                             * of these must be read to find all the native
+                             * (XLFD) names for the font, so that X11 font APIs
+                             * can be used for as many code points as possible.
+                             */
+                            registerFontDirs(extraFontPath);
+                        }
+
+                        /* On Solaris, we need to register the Japanese TrueType
+                         * directory so that we can find the corresponding
+                         * bitmap fonts. This could be done by listing the
+                         * directory in the font configuration file, but we
+                         * don't want to confuse users with this quirk. There
+                         * are no bitmap fonts for other writing systems that
+                         * correspond to TrueType fonts and have matching XLFDs.
+                         * We need to register the bitmap fonts only in
+                         * environments where they're on the X font path, i.e.,
+                         * in the Japanese locale. Note that if the X Toolkit
+                         * is in use the font path isn't set up by JDK, but
+                         * users of a JA locale should have it
+                         * set up already by their login environment.
+                         */
+                        if (FontUtilities.isSolaris && Locale.JAPAN.equals(Locale.getDefault())) {
+                            registerFontDir("/usr/openwin/lib/locale/ja/X11/fonts/TT");
+                        }
+
+                        initCompositeFonts(fontConfig, null);
+
+                        return null;
+                    }
+                });
+
+        boolean platformFont = AccessController.doPrivileged(
+                        new PrivilegedAction<Boolean>() {
+                                public Boolean run() {
+                                        String prop =
+                                                System.getProperty("java2d.font.usePlatformFont");
+                                        String env = System.getenv("JAVA2D_USEPLATFORMFONT");
+                                        return "true".equals(prop) || env != null;
+                                }
+                        });
+
+        if (platformFont) {
+            usePlatformFontMetrics = true;
+            System.out.println("Enabling platform font metrics for win32. This is an unsupported option.");
+            System.out.println("This yields incorrect composite font metrics as reported by 1.1.x releases.");
+            System.out.println("It is appropriate only for use by applications which do not use any Java 2");
+            System.out.println("functionality. This property will be removed in a later release.");
+        }
+    }
+
+    /**
+     * This method is provided for internal and exclusive use by Swing.
+     *
+     * @param font representing a physical font.
+     * @return true if the underlying font is a TrueType or OpenType font
+     * that claims to support the Microsoft Windows encoding corresponding to
+     * the default file.encoding property of this JRE instance.
+     * This narrow value is useful for Swing to decide if the font is useful
+     * for the the Windows Look and Feel, or, if a  composite font should be
+     * used instead.
+     * The information used to make the decision is obtained from
+     * the ulCodePageRange fields in the font.
+     * A caller can use isLogicalFont(Font) in this class before calling
+     * this method and would not need to call this method if that
+     * returns true.
+     */
+//     static boolean fontSupportsDefaultEncoding(Font font) {
+//      String encoding =
+//          (String) java.security.AccessController.doPrivileged(
+//                new sun.security.action.GetPropertyAction("file.encoding"));
+
+//      if (encoding == null || font == null) {
+//          return false;
+//      }
+
+//      encoding = encoding.toLowerCase(Locale.ENGLISH);
+
+//      return FontManager.fontSupportsEncoding(font, encoding);
+//     }
+
+    public Font2DHandle getNewComposite(String family, int style,
+                                        Font2DHandle handle) {
+
+        if (!(handle.font2D instanceof CompositeFont)) {
+            return handle;
+        }
+
+        CompositeFont oldComp = (CompositeFont)handle.font2D;
+        PhysicalFont oldFont = oldComp.getSlotFont(0);
+
+        if (family == null) {
+            family = oldFont.getFamilyName(null);
+        }
+        if (style == -1) {
+            style = oldComp.getStyle();
+        }
+
+        Font2D newFont = findFont2D(family, style, NO_FALLBACK);
+        if (!(newFont instanceof PhysicalFont)) {
+            newFont = oldFont;
+        }
+        PhysicalFont physicalFont = (PhysicalFont)newFont;
+        CompositeFont dialog2D =
+            (CompositeFont)findFont2D("dialog", style, NO_FALLBACK);
+        if (dialog2D == null) { /* shouldn't happen */
+            return handle;
+        }
+        CompositeFont compFont = new CompositeFont(physicalFont, dialog2D);
+        Font2DHandle newHandle = new Font2DHandle(compFont);
+        return newHandle;
+    }
+
+    protected void registerCompositeFont(String compositeName,
+                                      String[] componentFileNames,
+                                      String[] componentNames,
+                                      int numMetricsSlots,
+                                      int[] exclusionRanges,
+                                      int[] exclusionMaxIndex,
+                                      boolean defer) {
+
+        CompositeFont cf = new CompositeFont(compositeName,
+                                             componentFileNames,
+                                             componentNames,
+                                             numMetricsSlots,
+                                             exclusionRanges,
+                                             exclusionMaxIndex, defer, this);
+        addCompositeToFontList(cf, Font2D.FONT_CONFIG_RANK);
+        synchronized (compFonts) {
+            compFonts[maxCompFont++] = cf;
+        }
+    }
+
+    /* This variant is used only when the application specifies
+     * a variant of composite fonts which prefers locale specific or
+     * proportional fonts.
+     */
+    protected static void registerCompositeFont(String compositeName,
+                                                String[] componentFileNames,
+                                                String[] componentNames,
+                                                int numMetricsSlots,
+                                                int[] exclusionRanges,
+                                                int[] exclusionMaxIndex,
+                                                boolean defer,
+                                                ConcurrentHashMap<String, Font2D>
+                                                altNameCache) {
+
+        CompositeFont cf = new CompositeFont(compositeName,
+                                             componentFileNames,
+                                             componentNames,
+                                             numMetricsSlots,
+                                             exclusionRanges,
+                                             exclusionMaxIndex, defer,
+                                             SunFontManager.getInstance());
+
+        /* if the cache has an existing composite for this case, make
+         * its handle point to this new font.
+         * This ensures that when the altNameCache that is passed in
+         * is the global mapNameCache - ie we are running as an application -
+         * that any statically created java.awt.Font instances which already
+         * have a Font2D instance will have that re-directed to the new Font
+         * on subsequent uses. This is particularly important for "the"
+         * default font instance, or similar cases where a UI toolkit (eg
+         * Swing) has cached a java.awt.Font. Note that if Swing is using
+         * a custom composite APIs which update the standard composites have
+         * no effect - this is typically the case only when using the Windows
+         * L&F where these APIs would conflict with that L&F anyway.
+         */
+        Font2D oldFont = (Font2D)
+            altNameCache.get(compositeName.toLowerCase(Locale.ENGLISH));
+        if (oldFont instanceof CompositeFont) {
+            oldFont.handle.font2D = cf;
+        }
+        altNameCache.put(compositeName.toLowerCase(Locale.ENGLISH), cf);
+    }
+
+    private void addCompositeToFontList(CompositeFont f, int rank) {
+
+        if (FontUtilities.isLogging()) {
+            FontUtilities.getLogger().info("Add to Family "+ f.familyName +
+                        ", Font " + f.fullName + " rank="+rank);
+        }
+        f.setRank(rank);
+        compositeFonts.put(f.fullName, f);
+        fullNameToFont.put(f.fullName.toLowerCase(Locale.ENGLISH), f);
+
+        FontFamily family = FontFamily.getFamily(f.familyName);
+        if (family == null) {
+            family = new FontFamily(f.familyName, true, rank);
+        }
+        family.setFont(f, f.style);
+    }
+
+    /*
+     * Systems may have fonts with the same name.
+     * We want to register only one of such fonts (at least until
+     * such time as there might be APIs which can accommodate > 1).
+     * Rank is 1) font configuration fonts, 2) JRE fonts, 3) OT/TT fonts,
+     * 4) Type1 fonts, 5) native fonts.
+     *
+     * If the new font has the same name as the old font, the higher
+     * ranked font gets added, replacing the lower ranked one.
+     * If the fonts are of equal rank, then make a special case of
+     * font configuration rank fonts, which are on closer inspection,
+     * OT/TT fonts such that the larger font is registered. This is
+     * a heuristic since a font may be "larger" in the sense of more
+     * code points, or be a larger "file" because it has more bitmaps.
+     * So it is possible that using filesize may lead to less glyphs, and
+     * using glyphs may lead to lower quality display. Probably number
+     * of glyphs is the ideal, but filesize is information we already
+     * have and is good enough for the known cases.
+     * Also don't want to register fonts that match JRE font families
+     * but are coming from a source other than the JRE.
+     * This will ensure that we will algorithmically style the JRE
+     * plain font and get the same set of glyphs for all styles.
+     *
+     * Note that this method returns a value
+     * if it returns the same object as its argument that means this
+     * font was newly registered.
+     * If it returns a different object it means this font already exists,
+     * and you should use that one.
+     * If it returns null means this font was not registered and none
+     * in that name is registered. The caller must find a substitute
+     */
+    private PhysicalFont addToFontList(PhysicalFont f, int rank) {
+
+        String fontName = f.fullName;
+        String familyName = f.familyName;
+        if (fontName == null || "".equals(fontName)) {
+            return null;
+        }
+        if (compositeFonts.containsKey(fontName)) {
+            /* Don't register any font that has the same name as a composite */
+            return null;
+        }
+        f.setRank(rank);
+        if (!physicalFonts.containsKey(fontName)) {
+            if (FontUtilities.isLogging()) {
+                FontUtilities.getLogger().info("Add to Family "+familyName +
+                            ", Font " + fontName + " rank="+rank);
+            }
+            physicalFonts.put(fontName, f);
+            FontFamily family = FontFamily.getFamily(familyName);
+            if (family == null) {
+                family = new FontFamily(familyName, false, rank);
+                family.setFont(f, f.style);
+            } else if (family.getRank() >= rank) {
+                family.setFont(f, f.style);
+            }
+            fullNameToFont.put(fontName.toLowerCase(Locale.ENGLISH), f);
+            return f;
+        } else {
+            PhysicalFont newFont = f;
+            PhysicalFont oldFont = physicalFonts.get(fontName);
+            if (oldFont == null) {
+                return null;
+            }
+            /* If the new font is of an equal or higher rank, it is a
+             * candidate to replace the current one, subject to further tests.
+             */
+            if (oldFont.getRank() >= rank) {
+
+                /* All fonts initialise their mapper when first
+                 * used. If the mapper is non-null then this font
+                 * has been accessed at least once. In that case
+                 * do not replace it. This may be overly stringent,
+                 * but its probably better not to replace a font that
+                 * someone is already using without a compelling reason.
+                 * Additionally the primary case where it is known
+                 * this behaviour is important is in certain composite
+                 * fonts, and since all the components of a given
+                 * composite are usually initialised together this
+                 * is unlikely. For this to be a problem, there would
+                 * have to be a case where two different composites used
+                 * different versions of the same-named font, and they
+                 * were initialised and used at separate times.
+                 * In that case we continue on and allow the new font to
+                 * be installed, but replaceFont will continue to allow
+                 * the original font to be used in Composite fonts.
+                 */
+                if (oldFont.mapper != null && rank > Font2D.FONT_CONFIG_RANK) {
+                    return oldFont;
+                }
+
+                /* Normally we require a higher rank to replace a font,
+                 * but as a special case, if the two fonts are the same rank,
+                 * and are instances of TrueTypeFont we want the
+                 * more complete (larger) one.
+                 */
+                if (oldFont.getRank() == rank) {
+                    if (oldFont instanceof TrueTypeFont &&
+                        newFont instanceof TrueTypeFont) {
+                        TrueTypeFont oldTTFont = (TrueTypeFont)oldFont;
+                        TrueTypeFont newTTFont = (TrueTypeFont)newFont;
+                        if (oldTTFont.fileSize >= newTTFont.fileSize) {
+                            return oldFont;
+                        }
+                    } else {
+                        return oldFont;
+                    }
+                }
+                /* Don't replace ever JRE fonts.
+                 * This test is in case a font configuration references
+                 * a Lucida font, which has been mapped to a Lucida
+                 * from the host O/S. The assumption here is that any
+                 * such font configuration file is probably incorrect, or
+                 * the host O/S version is for the use of AWT.
+                 * In other words if we reach here, there's a possible
+                 * problem with our choice of font configuration fonts.
+                 */
+                if (oldFont.platName.startsWith(jreFontDirName)) {
+                    if (FontUtilities.isLogging()) {
+                        FontUtilities.getLogger()
+                              .warning("Unexpected attempt to replace a JRE " +
+                                       " font " + fontName + " from " +
+                                        oldFont.platName +
+                                       " with " + newFont.platName);
+                    }
+                    return oldFont;
+                }
+
+                if (FontUtilities.isLogging()) {
+                    FontUtilities.getLogger()
+                          .info("Replace in Family " + familyName +
+                                ",Font " + fontName + " new rank="+rank +
+                                " from " + oldFont.platName +
+                                " with " + newFont.platName);
+                }
+                replaceFont(oldFont, newFont);
+                physicalFonts.put(fontName, newFont);
+                fullNameToFont.put(fontName.toLowerCase(Locale.ENGLISH),
+                                   newFont);
+
+                FontFamily family = FontFamily.getFamily(familyName);
+                if (family == null) {
+                    family = new FontFamily(familyName, false, rank);
+                    family.setFont(newFont, newFont.style);
+                } else if (family.getRank() >= rank) {
+                    family.setFont(newFont, newFont.style);
+                }
+                return newFont;
+            } else {
+                return oldFont;
+            }
+        }
+    }
+
+    public Font2D[] getRegisteredFonts() {
+        PhysicalFont[] physFonts = getPhysicalFonts();
+        int mcf = maxCompFont; /* for MT-safety */
+        Font2D[] regFonts = new Font2D[physFonts.length+mcf];
+        System.arraycopy(compFonts, 0, regFonts, 0, mcf);
+        System.arraycopy(physFonts, 0, regFonts, mcf, physFonts.length);
+        return regFonts;
+    }
+
+    protected PhysicalFont[] getPhysicalFonts() {
+        return physicalFonts.values().toArray(new PhysicalFont[0]);
+    }
+
+
+    /* The class FontRegistrationInfo is used when a client says not
+     * to register a font immediately. This mechanism is used to defer
+     * initialisation of all the components of composite fonts at JRE
+     * start-up. The CompositeFont class is "aware" of this and when it
+     * is first used it asks for the registration of its components.
+     * Also in the event that any physical font is requested the
+     * deferred fonts are initialised before triggering a search of the
+     * system.
+     * Two maps are used. One to track the deferred fonts. The
+     * other to track the fonts that have been initialised through this
+     * mechanism.
+     */
+
+    private static final class FontRegistrationInfo {
+
+        String fontFilePath;
+        String[] nativeNames;
+        int fontFormat;
+        boolean javaRasterizer;
+        int fontRank;
+
+        FontRegistrationInfo(String fontPath, String[] names, int format,
+                             boolean useJavaRasterizer, int rank) {
+            this.fontFilePath = fontPath;
+            this.nativeNames = names;
+            this.fontFormat = format;
+            this.javaRasterizer = useJavaRasterizer;
+            this.fontRank = rank;
+        }
+    }
+
+    private final ConcurrentHashMap<String, FontRegistrationInfo>
+        deferredFontFiles =
+        new ConcurrentHashMap<String, FontRegistrationInfo>();
+    private final ConcurrentHashMap<String, Font2DHandle>
+        initialisedFonts = new ConcurrentHashMap<String, Font2DHandle>();
+
+    /* Remind: possibly enhance initialiseDeferredFonts() to be
+     * optionally given a name and a style and it could stop when it
+     * finds that font - but this would be a problem if two of the
+     * fonts reference the same font face name (cf the Solaris
+     * euro fonts).
+     */
+    protected synchronized void initialiseDeferredFonts() {
+        for (String fileName : deferredFontFiles.keySet()) {
+            initialiseDeferredFont(fileName);
+        }
+    }
+
+    protected synchronized void registerDeferredJREFonts(String jreDir) {
+        for (FontRegistrationInfo info : deferredFontFiles.values()) {
+            if (info.fontFilePath != null &&
+                info.fontFilePath.startsWith(jreDir)) {
+                initialiseDeferredFont(info.fontFilePath);
+            }
+        }
+    }
+
+    public boolean isDeferredFont(String fileName) {
+        return deferredFontFiles.containsKey(fileName);
+    }
+
+    /* We keep a map of the files which contain the Lucida fonts so we
+     * don't need to search for them.
+     * But since we know what fonts these files contain, we can also avoid
+     * opening them to look for a font name we don't recognise - see
+     * findDeferredFont().
+     * For typical cases where the font isn't a JRE one the overhead is
+     * this method call, HashMap.get() and null reference test, then
+     * a boolean test of noOtherJREFontFiles.
+     */
+    public
+    /*private*/ PhysicalFont findJREDeferredFont(String name, int style) {
+
+        PhysicalFont physicalFont;
+        String nameAndStyle = name.toLowerCase(Locale.ENGLISH) + style;
+        String fileName = jreFontMap.get(nameAndStyle);
+        if (fileName != null) {
+            fileName = jreFontDirName + File.separator + fileName;
+            if (deferredFontFiles.get(fileName) != null) {
+                physicalFont = initialiseDeferredFont(fileName);
+                if (physicalFont != null &&
+                    (physicalFont.getFontName(null).equalsIgnoreCase(name) ||
+                     physicalFont.getFamilyName(null).equalsIgnoreCase(name))
+                    && physicalFont.style == style) {
+                    return physicalFont;
+                }
+            }
+        }
+
+        /* Iterate over the deferred font files looking for any in the
+         * jre directory that we didn't recognise, open each of these.
+         * In almost all installations this will quickly fall through
+         * because only the Lucidas will be present and jreOtherFontFiles
+         * will be empty.
+         * noOtherJREFontFiles is used so we can skip this block as soon
+         * as its determined that its not needed - almost always after the
+         * very first time through.
+         */
+        if (noOtherJREFontFiles) {
+            return null;
+        }
+        synchronized (jreLucidaFontFiles) {
+            if (jreOtherFontFiles == null) {
+                HashSet<String> otherFontFiles = new HashSet<String>();
+                for (String deferredFile : deferredFontFiles.keySet()) {
+                    File file = new File(deferredFile);
+                    String dir = file.getParent();
+                    String fname = file.getName();
+                    /* skip names which aren't absolute, aren't in the JRE
+                     * directory, or are known Lucida fonts.
+                     */
+                    if (dir == null ||
+                        !dir.equals(jreFontDirName) ||
+                        jreLucidaFontFiles.contains(fname)) {
+                        continue;
+                    }
+                    otherFontFiles.add(deferredFile);
+                }
+                jreOtherFontFiles = otherFontFiles.toArray(STR_ARRAY);
+                if (jreOtherFontFiles.length == 0) {
+                    noOtherJREFontFiles = true;
+                }
+            }
+
+            for (int i=0; i<jreOtherFontFiles.length;i++) {
+                fileName = jreOtherFontFiles[i];
+                if (fileName == null) {
+                    continue;
+                }
+                jreOtherFontFiles[i] = null;
+                physicalFont = initialiseDeferredFont(fileName);
+                if (physicalFont != null &&
+                    (physicalFont.getFontName(null).equalsIgnoreCase(name) ||
+                     physicalFont.getFamilyName(null).equalsIgnoreCase(name))
+                    && physicalFont.style == style) {
+                    return physicalFont;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /* This skips JRE installed fonts. */
+    private PhysicalFont findOtherDeferredFont(String name, int style) {
+        for (String fileName : deferredFontFiles.keySet()) {
+            File file = new File(fileName);
+            String dir = file.getParent();
+            String fname = file.getName();
+            if (dir != null &&
+                dir.equals(jreFontDirName) &&
+                jreLucidaFontFiles.contains(fname)) {
+                continue;
+            }
+            PhysicalFont physicalFont = initialiseDeferredFont(fileName);
+            if (physicalFont != null &&
+                (physicalFont.getFontName(null).equalsIgnoreCase(name) ||
+                physicalFont.getFamilyName(null).equalsIgnoreCase(name)) &&
+                physicalFont.style == style) {
+                return physicalFont;
+            }
+        }
+        return null;
+    }
+
+    private PhysicalFont findDeferredFont(String name, int style) {
+
+        PhysicalFont physicalFont = findJREDeferredFont(name, style);
+        if (physicalFont != null) {
+            return physicalFont;
+        } else {
+            return findOtherDeferredFont(name, style);
+        }
+    }
+
+    public void registerDeferredFont(String fileNameKey,
+                                     String fullPathName,
+                                     String[] nativeNames,
+                                     int fontFormat,
+                                     boolean useJavaRasterizer,
+                                     int fontRank) {
+        FontRegistrationInfo regInfo =
+            new FontRegistrationInfo(fullPathName, nativeNames, fontFormat,
+                                     useJavaRasterizer, fontRank);
+        deferredFontFiles.put(fileNameKey, regInfo);
+    }
+
+
+    public synchronized
+         PhysicalFont initialiseDeferredFont(String fileNameKey) {
+
+        if (fileNameKey == null) {
+            return null;
+        }
+        if (FontUtilities.isLogging()) {
+            FontUtilities.getLogger()
+                            .info("Opening deferred font file " + fileNameKey);
+        }
+
+        PhysicalFont physicalFont;
+        FontRegistrationInfo regInfo = deferredFontFiles.get(fileNameKey);
+        if (regInfo != null) {
+            deferredFontFiles.remove(fileNameKey);
+            physicalFont = registerFontFile(regInfo.fontFilePath,
+                                            regInfo.nativeNames,
+                                            regInfo.fontFormat,
+                                            regInfo.javaRasterizer,
+                                            regInfo.fontRank);
+
+
+            if (physicalFont != null) {
+                /* Store the handle, so that if a font is bad, we
+                 * retrieve the substituted font.
+                 */
+                initialisedFonts.put(fileNameKey, physicalFont.handle);
+            } else {
+                initialisedFonts.put(fileNameKey,
+                                     getDefaultPhysicalFont().handle);
+            }
+        } else {
+            Font2DHandle handle = initialisedFonts.get(fileNameKey);
+            if (handle == null) {
+                /* Probably shouldn't happen, but just in case */
+                physicalFont = getDefaultPhysicalFont();
+            } else {
+                physicalFont = (PhysicalFont)(handle.font2D);
+            }
+        }
+        return physicalFont;
+    }
+
+    public boolean isRegisteredFontFile(String name) {
+        return registeredFonts.containsKey(name);
+    }
+
+    public PhysicalFont getRegisteredFontFile(String name) {
+        return registeredFonts.get(name);
+    }
+
+    /* Note that the return value from this method is not always
+     * derived from this file, and may be null. See addToFontList for
+     * some explanation of this.
+     */
+    public PhysicalFont registerFontFile(String fileName,
+                                         String[] nativeNames,
+                                         int fontFormat,
+                                         boolean useJavaRasterizer,
+                                         int fontRank) {
+
+        PhysicalFont regFont = registeredFonts.get(fileName);
+        if (regFont != null) {
+            return regFont;
+        }
+
+        PhysicalFont physicalFont = null;
+        try {
+            String name;
+
+            switch (fontFormat) {
+
+            case FONTFORMAT_TRUETYPE:
+                int fn = 0;
+                TrueTypeFont ttf;
+                do {
+                    ttf = new TrueTypeFont(fileName, nativeNames, fn++,
+                                           useJavaRasterizer);
+                    PhysicalFont pf = addToFontList(ttf, fontRank);
+                    if (physicalFont == null) {
+                        physicalFont = pf;
+                    }
+                }
+                while (fn < ttf.getFontCount());
+                break;
+
+            case FONTFORMAT_TYPE1:
+                Type1Font t1f = new Type1Font(fileName, nativeNames);
+                physicalFont = addToFontList(t1f, fontRank);
+                break;
+
+            case FONTFORMAT_NATIVE:
+                NativeFont nf = new NativeFont(fileName, false);
+                physicalFont = addToFontList(nf, fontRank);
+            default:
+
+            }
+            if (FontUtilities.isLogging()) {
+                FontUtilities.getLogger()
+                      .info("Registered file " + fileName + " as font " +
+                            physicalFont + " rank="  + fontRank);
+            }
+        } catch (FontFormatException ffe) {
+            if (FontUtilities.isLogging()) {
+                FontUtilities.getLogger().warning("Unusable font: " +
+                               fileName + " " + ffe.toString());
+            }
+        }
+        if (physicalFont != null &&
+            fontFormat != FONTFORMAT_NATIVE) {
+            registeredFonts.put(fileName, physicalFont);
+        }
+        return physicalFont;
+    }
+
+    public void registerFonts(String[] fileNames,
+                              String[][] nativeNames,
+                              int fontCount,
+                              int fontFormat,
+                              boolean useJavaRasterizer,
+                              int fontRank, boolean defer) {
+
+        for (int i=0; i < fontCount; i++) {
+            if (defer) {
+                registerDeferredFont(fileNames[i],fileNames[i], nativeNames[i],
+                                     fontFormat, useJavaRasterizer, fontRank);
+            } else {
+                registerFontFile(fileNames[i], nativeNames[i],
+                                 fontFormat, useJavaRasterizer, fontRank);
+            }
+        }
+    }
+
+    /*
+     * This is the Physical font used when some other font on the system
+     * can't be located. There has to be at least one font or the font
+     * system is not useful and the graphics environment cannot sustain
+     * the Java platform.
+     */
+    public PhysicalFont getDefaultPhysicalFont() {
+        if (defaultPhysicalFont == null) {
+            /* findFont2D will load all fonts before giving up the search.
+             * If the JRE Lucida isn't found (eg because the JRE fonts
+             * directory is missing), it could find another version of Lucida
+             * from the host system. This is OK because at that point we are
+             * trying to gracefully handle/recover from a system
+             * misconfiguration and this is probably a reasonable substitution.
+             */
+            defaultPhysicalFont = (PhysicalFont)
+                findFont2D("Lucida Sans Regular", Font.PLAIN, NO_FALLBACK);
+            if (defaultPhysicalFont == null) {
+                defaultPhysicalFont = (PhysicalFont)
+                    findFont2D("Arial", Font.PLAIN, NO_FALLBACK);
+            }
+            if (defaultPhysicalFont == null) {
+                /* Because of the findFont2D call above, if we reach here, we
+                 * know all fonts have already been loaded, just accept any
+                 * match at this point. If this fails we are in real trouble
+                 * and I don't know how to recover from there being absolutely
+                 * no fonts anywhere on the system.
+                 */
+                Iterator i = physicalFonts.values().iterator();
+                if (i.hasNext()) {
+                    defaultPhysicalFont = (PhysicalFont)i.next();
+                } else {
+                    throw new Error("Probable fatal error:No fonts found.");
+                }
+            }
+        }
+        return defaultPhysicalFont;
+    }
+
+    public CompositeFont getDefaultLogicalFont(int style) {
+        return (CompositeFont)findFont2D("dialog", style, NO_FALLBACK);
+    }
+
+    /*
+     * return String representation of style prepended with "."
+     * This is useful for performance to avoid unnecessary string operations.
+     */
+    private static String dotStyleStr(int num) {
+        switch(num){
+          case Font.BOLD:
+            return ".bold";
+          case Font.ITALIC:
+            return ".italic";
+          case Font.ITALIC | Font.BOLD:
+            return ".bolditalic";
+          default:
+            return ".plain";
+        }
+    }
+
+    /* This is implemented only on windows and is called from code that
+     * executes only on windows. This isn't pretty but its not a precedent
+     * in this file. This very probably should be cleaned up at some point.
+     */
+    protected void
+        populateFontFileNameMap(HashMap<String,String> fontToFileMap,
+                                HashMap<String,String> fontToFamilyNameMap,
+                                HashMap<String,ArrayList<String>>
+                                familyToFontListMap,
+                                Locale locale) {
+    }
+
+    /* Obtained from Platform APIs (windows only)
+     * Map from lower-case font full name to basename of font file.
+     * Eg "arial bold" -> ARIALBD.TTF.
+     * For TTC files, there is a mapping for each font in the file.
+     */
+    private HashMap<String,String> fontToFileMap = null;
+
+    /* Obtained from Platform APIs (windows only)
+     * Map from lower-case font full name to the name of its font family
+     * Eg "arial bold" -> "Arial"
+     */
+    private HashMap<String,String> fontToFamilyNameMap = null;
+
+    /* Obtained from Platform APIs (windows only)
+     * Map from a lower-case family name to a list of full names of
+     * the member fonts, eg:
+     * "arial" -> ["Arial", "Arial Bold", "Arial Italic","Arial Bold Italic"]
+     */
+    private HashMap<String,ArrayList<String>> familyToFontListMap= null;
+
+    /* The directories which contain platform fonts */
+    private String[] pathDirs = null;
+
+    private boolean haveCheckedUnreferencedFontFiles;
+
+    private String[] getFontFilesFromPath(boolean noType1) {
+        final FilenameFilter filter;
+        if (noType1) {
+            filter = ttFilter;
+        } else {
+            filter = new TTorT1Filter();
+        }
+        return (String[])AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                if (pathDirs.length == 1) {
+                    File dir = new File(pathDirs[0]);
+                    String[] files = dir.list(filter);
+                    if (files == null) {
+                        return new String[0];
+                    }
+                    for (int f=0; f<files.length; f++) {
+                        files[f] = files[f].toLowerCase();
+                    }
+                    return files;
+                } else {
+                    ArrayList<String> fileList = new ArrayList<String>();
+                    for (int i = 0; i< pathDirs.length; i++) {
+                        File dir = new File(pathDirs[i]);
+                        String[] files = dir.list(filter);
+                        if (files == null) {
+                            continue;
+                        }
+                        for (int f=0; f<files.length ; f++) {
+                            fileList.add(files[f].toLowerCase());
+                        }
+                    }
+                    return fileList.toArray(STR_ARRAY);
+                }
+            }
+        });
+    }
+
+    /* This is needed since some windows registry names don't match
+     * the font names.
+     * - UPC styled font names have a double space, but the
+     * registry entry mapping to a file doesn't.
+     * - Marlett is in a hidden file not listed in the registry
+     * - The registry advertises that the file david.ttf contains a
+     * font with the full name "David Regular" when in fact its
+     * just "David".
+     * Directly fix up these known cases as this is faster.
+     * If a font which doesn't match these known cases has no file,
+     * it may be a font that has been temporarily added to the known set
+     * or it may be an installed font with a missing registry entry.
+     * Installed fonts are those in the windows font directories.
+     * Make a best effort attempt to locate these.
+     * We obtain the list of TrueType fonts in these directories and
+     * filter out all the font files we already know about from the registry.
+     * What remains may be "bad" fonts, duplicate fonts, or perhaps the
+     * missing font(s) we are looking for.
+     * Open each of these files to find out.
+     */
+    private void resolveWindowsFonts() {
+
+        ArrayList<String> unmappedFontNames = null;
+        for (String font : fontToFamilyNameMap.keySet()) {
+            String file = fontToFileMap.get(font);
+            if (file == null) {
+                if (font.indexOf("  ") > 0) {
+                    String newName = font.replaceFirst("  ", " ");
+                    file = fontToFileMap.get(newName);
+                    /* If this name exists and isn't for a valid name
+                     * replace the mapping to the file with this font
+                     */
+                    if (file != null &&
+                        !fontToFamilyNameMap.containsKey(newName)) {
+                        fontToFileMap.remove(newName);
+                        fontToFileMap.put(font, file);
+                    }
+                } else if (font.equals("marlett")) {
+                    fontToFileMap.put(font, "marlett.ttf");
+                } else if (font.equals("david")) {
+                    file = fontToFileMap.get("david regular");
+                    if (file != null) {
+                        fontToFileMap.remove("david regular");
+                        fontToFileMap.put("david", file);
+                    }
+                } else {
+                    if (unmappedFontNames == null) {
+                        unmappedFontNames = new ArrayList<String>();
+                    }
+                    unmappedFontNames.add(font);
+                }
+            }
+        }
+
+        if (unmappedFontNames != null) {
+            HashSet<String> unmappedFontFiles = new HashSet<String>();
+
+            /* Every font key in fontToFileMap ought to correspond to a
+             * font key in fontToFamilyNameMap. Entries that don't seem
+             * to correspond are likely fonts that were named differently
+             * by GDI than in the registry. One known cause of this is when
+             * Windows has had its regional settings changed so that from
+             * GDI we get a localised (eg Chinese or Japanese) name for the
+             * font, but the registry retains the English version of the name
+             * that corresponded to the "install" locale for windows.
+             * Since we are in this code block because there are unmapped
+             * font names, we can look to find unused font->file mappings
+             * and then open the files to read the names. We don't generally
+             * want to open font files, as its a performance hit, but this
+             * occurs only for a small number of fonts on specific system
+             * configs - ie is believed that a "true" Japanese windows would
+             * have JA names in the registry too.
+             * Clone fontToFileMap and remove from the clone all keys which
+             * match a fontToFamilyNameMap key. What remains maps to the
+             * files we want to open to find the fonts GDI returned.
+             * A font in such a file is added to the fontToFileMap after
+             * checking its one of the unmappedFontNames we are looking for.
+             * The original name that didn't map is removed from fontToFileMap
+             * so essentially this "fixes up" fontToFileMap to use the same
+             * name as GDI.
+             * Also note that typically the fonts for which this occurs in
+             * CJK locales are TTC fonts and not all fonts in a TTC may have
+             * localised names. Eg MSGOTHIC.TTC contains 3 fonts and one of
+             * them "MS UI Gothic" has no JA name whereas the other two do.
+             * So not every font in these files is unmapped or new.
+             */
+            HashMap<String,String> ffmapCopy =
+                (HashMap<String,String>)(fontToFileMap.clone());
+            for (String key : fontToFamilyNameMap.keySet()) {
+                ffmapCopy.remove(key);
+            }
+            for (String key : ffmapCopy.keySet()) {
+                unmappedFontFiles.add(ffmapCopy.get(key));
+                fontToFileMap.remove(key);
+            }
+
+            resolveFontFiles(unmappedFontFiles, unmappedFontNames);
+
+            /* If there are still unmapped font names, this means there's
+             * something that wasn't in the registry. We need to get all
+             * the font files directly and look at the ones that weren't
+             * found in the registry.
+             */
+            if (unmappedFontNames.size() > 0) {
+
+                /* getFontFilesFromPath() returns all lower case names.
+                 * To compare we also need lower case
+                 * versions of the names from the registry.
+                 */
+                ArrayList<String> registryFiles = new ArrayList<String>();
+
+                for (String regFile : fontToFileMap.values()) {
+                    registryFiles.add(regFile.toLowerCase());
+                }
+                /* We don't look for Type1 files here as windows will
+                 * not enumerate these, so aren't useful in reconciling
+                 * GDI's unmapped files. We do find these later when
+                 * we enumerate all fonts.
+                 */
+                for (String pathFile : getFontFilesFromPath(true)) {
+                    if (!registryFiles.contains(pathFile)) {
+                        unmappedFontFiles.add(pathFile);
+                    }
+                }
+
+                resolveFontFiles(unmappedFontFiles, unmappedFontNames);
+            }
+
+            /* remove from the set of names that will be returned to the
+             * user any fonts that can't be mapped to files.
+             */
+            if (unmappedFontNames.size() > 0) {
+                int sz = unmappedFontNames.size();
+                for (int i=0; i<sz; i++) {
+                    String name = unmappedFontNames.get(i);
+                    String familyName = fontToFamilyNameMap.get(name);
+                    if (familyName != null) {
+                        ArrayList family = familyToFontListMap.get(familyName);
+                        if (family != null) {
+                            if (family.size() <= 1) {
+                                familyToFontListMap.remove(familyName);
+                            }
+                        }
+                    }
+                    fontToFamilyNameMap.remove(name);
+                    if (FontUtilities.isLogging()) {
+                        FontUtilities.getLogger()
+                                             .info("No file for font:" + name);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * In some cases windows may have fonts in the fonts folder that
+     * don't show up in the registry or in the GDI calls to enumerate fonts.
+     * The only way to find these is to list the directory. We invoke this
+     * only in getAllFonts/Families, so most searches for a specific
+     * font that is satisfied by the GDI/registry calls don't take the
+     * additional hit of listing the directory. This hit is small enough
+     * that its not significant in these 'enumerate all the fonts' cases.
+     * The basic approach is to cross-reference the files windows found
+     * with the ones in the directory listing approach, and for each
+     * in the latter list that is missing from the former list, register it.
+     */
+    private synchronized void checkForUnreferencedFontFiles() {
+        if (haveCheckedUnreferencedFontFiles) {
+            return;
+        }
+        haveCheckedUnreferencedFontFiles = true;
+        if (!FontUtilities.isWindows) {
+            return;
+        }
+        /* getFontFilesFromPath() returns all lower case names.
+         * To compare we also need lower case
+         * versions of the names from the registry.
+         */
+        ArrayList<String> registryFiles = new ArrayList<String>();
+        for (String regFile : fontToFileMap.values()) {
+            registryFiles.add(regFile.toLowerCase());
+        }
+
+        /* To avoid any issues with concurrent modification, create
+         * copies of the existing maps, add the new fonts into these
+         * and then replace the references to the old ones with the
+         * new maps. ConcurrentHashmap is another option but its a lot
+         * more changes and with this exception, these maps are intended
+         * to be static.
+         */
+        HashMap<String,String> fontToFileMap2 = null;
+        HashMap<String,String> fontToFamilyNameMap2 = null;
+        HashMap<String,ArrayList<String>> familyToFontListMap2 = null;;
+
+        for (String pathFile : getFontFilesFromPath(false)) {
+            if (!registryFiles.contains(pathFile)) {
+                if (FontUtilities.isLogging()) {
+                    FontUtilities.getLogger()
+                                 .info("Found non-registry file : " + pathFile);
+                }
+                PhysicalFont f = registerFontFile(getPathName(pathFile));
+                if (f == null) {
+                    continue;
+                }
+                if (fontToFileMap2 == null) {
+                    fontToFileMap2 = new HashMap<String,String>(fontToFileMap);
+                    fontToFamilyNameMap2 =
+                        new HashMap<String,String>(fontToFamilyNameMap);
+                    familyToFontListMap2 = new
+                        HashMap<String,ArrayList<String>>(familyToFontListMap);
+                }
+                String fontName = f.getFontName(null);
+                String family = f.getFamilyName(null);
+                String familyLC = family.toLowerCase();
+                fontToFamilyNameMap2.put(fontName, family);
+                fontToFileMap2.put(fontName, pathFile);
+                ArrayList<String> fonts = familyToFontListMap2.get(familyLC);
+                if (fonts == null) {
+                    fonts = new ArrayList<String>();
+                } else {
+                    fonts = new ArrayList<String>(fonts);
+                }
+                fonts.add(fontName);
+                familyToFontListMap2.put(familyLC, fonts);
+            }
+        }
+        if (fontToFileMap2 != null) {
+            fontToFileMap = fontToFileMap2;
+            familyToFontListMap = familyToFontListMap2;
+            fontToFamilyNameMap = fontToFamilyNameMap2;
+        }
+    }
+
+    private void resolveFontFiles(HashSet<String> unmappedFiles,
+                                  ArrayList<String> unmappedFonts) {
+
+        Locale l = SunToolkit.getStartupLocale();
+
+        for (String file : unmappedFiles) {
+            try {
+                int fn = 0;
+                TrueTypeFont ttf;
+                String fullPath = getPathName(file);
+                if (FontUtilities.isLogging()) {
+                    FontUtilities.getLogger()
+                                   .info("Trying to resolve file " + fullPath);
+                }
+                do {
+                    ttf = new TrueTypeFont(fullPath, null, fn++, true);
+                    //  prefer the font's locale name.
+                    String fontName = ttf.getFontName(l).toLowerCase();
+                    if (unmappedFonts.contains(fontName)) {
+                        fontToFileMap.put(fontName, file);
+                        unmappedFonts.remove(fontName);
+                        if (FontUtilities.isLogging()) {
+                            FontUtilities.getLogger()
+                                  .info("Resolved absent registry entry for " +
+                                        fontName + " located in " + fullPath);
+                        }
+                    }
+                }
+                while (fn < ttf.getFontCount());
+            } catch (Exception e) {
+            }
+        }
+    }
+
+    private synchronized HashMap<String,String> getFullNameToFileMap() {
+        if (fontToFileMap == null) {
+
+            pathDirs = getPlatformFontDirs(noType1Font);
+
+            fontToFileMap = new HashMap<String,String>(100);
+            fontToFamilyNameMap = new HashMap<String,String>(100);
+            familyToFontListMap = new HashMap<String,ArrayList<String>>(50);
+            populateFontFileNameMap(fontToFileMap,
+                                    fontToFamilyNameMap,
+                                    familyToFontListMap,
+                                    Locale.ENGLISH);
+            if (FontUtilities.isWindows) {
+                resolveWindowsFonts();
+            }
+            if (FontUtilities.isLogging()) {
+                logPlatformFontInfo();
+            }
+        }
+        return fontToFileMap;
+    }
+
+    private void logPlatformFontInfo() {
+        Logger logger = FontUtilities.getLogger();
+        for (int i=0; i< pathDirs.length;i++) {
+            logger.info("fontdir="+pathDirs[i]);
+        }
+        for (String keyName : fontToFileMap.keySet()) {
+            logger.info("font="+keyName+" file="+ fontToFileMap.get(keyName));
+        }
+        for (String keyName : fontToFamilyNameMap.keySet()) {
+            logger.info("font="+keyName+" family="+
+                        fontToFamilyNameMap.get(keyName));
+        }
+        for (String keyName : familyToFontListMap.keySet()) {
+            logger.info("family="+keyName+ " fonts="+
+                        familyToFontListMap.get(keyName));
+        }
+    }
+
+    /* Note this return list excludes logical fonts and JRE fonts */
+    protected String[] getFontNamesFromPlatform() {
+        if (getFullNameToFileMap().size() == 0) {
+            return null;
+        }
+        checkForUnreferencedFontFiles();
+        /* This odd code with TreeMap is used to preserve a historical
+         * behaviour wrt the sorting order .. */
+        ArrayList<String> fontNames = new ArrayList<String>();
+        for (ArrayList<String> a : familyToFontListMap.values()) {
+            for (String s : a) {
+                fontNames.add(s);
+            }
+        }
+        return fontNames.toArray(STR_ARRAY);
+    }
+
+    public boolean gotFontsFromPlatform() {
+        return getFullNameToFileMap().size() != 0;
+    }
+
+    public String getFileNameForFontName(String fontName) {
+        String fontNameLC = fontName.toLowerCase(Locale.ENGLISH);
+        return fontToFileMap.get(fontNameLC);
+    }
+
+    private PhysicalFont registerFontFile(String file) {
+        if (new File(file).isAbsolute() &&
+            !registeredFonts.contains(file)) {
+            int fontFormat = FONTFORMAT_NONE;
+            int fontRank = Font2D.UNKNOWN_RANK;
+            if (ttFilter.accept(null, file)) {
+                fontFormat = FONTFORMAT_TRUETYPE;
+                fontRank = Font2D.TTF_RANK;
+            } else if
+                (t1Filter.accept(null, file)) {
+                fontFormat = FONTFORMAT_TYPE1;
+                fontRank = Font2D.TYPE1_RANK;
+            }
+            if (fontFormat == FONTFORMAT_NONE) {
+                return null;
+            }
+            return registerFontFile(file, null, fontFormat, false, fontRank);
+        }
+        return null;
+    }
+
+    /* Used to register any font files that are found by platform APIs
+     * that weren't previously found in the standard font locations.
+     * the isAbsolute() check is needed since that's whats stored in the
+     * set, and on windows, the fonts in the system font directory that
+     * are in the fontToFileMap are just basenames. We don't want to try
+     * to register those again, but we do want to register other registry
+     * installed fonts.
+     */
+    protected void registerOtherFontFiles(HashSet registeredFontFiles) {
+        if (getFullNameToFileMap().size() == 0) {
+            return;
+        }
+        for (String file : fontToFileMap.values()) {
+            registerFontFile(file);
+        }
+    }
+
+    public boolean
+        getFamilyNamesFromPlatform(TreeMap<String,String> familyNames,
+                                   Locale requestedLocale) {
+        if (getFullNameToFileMap().size() == 0) {
+            return false;
+        }
+        checkForUnreferencedFontFiles();
+        for (String name : fontToFamilyNameMap.values()) {
+            familyNames.put(name.toLowerCase(requestedLocale), name);
+        }
+        return true;
+    }
+
+    /* Path may be absolute or a base file name relative to one of
+     * the platform font directories
+     */
+    private String getPathName(final String s) {
+        File f = new File(s);
+        if (f.isAbsolute()) {
+            return s;
+        } else if (pathDirs.length==1) {
+            return pathDirs[0] + File.separator + s;
+        } else {
+            String path = java.security.AccessController.doPrivileged(
+                 new java.security.PrivilegedAction<String>() {
+                     public String run() {
+                         for (int p=0; p<pathDirs.length; p++) {
+                             File f = new File(pathDirs[p] +File.separator+ s);
+                             if (f.exists()) {
+                                 return f.getAbsolutePath();
+                             }
+                         }
+                         return null;
+                     }
+                });
+            if (path != null) {
+                return path;
+            }
+        }
+        return s; // shouldn't happen, but harmless
+    }
+
+    /* lcName is required to be lower case for use as a key.
+     * lcName may be a full name, or a family name, and style may
+     * be specified in addition to either of these. So be sure to
+     * get the right one. Since an app *could* ask for "Foo Regular"
+     * and later ask for "Foo Italic", if we don't register all the
+     * styles, then logic in findFont2D may try to style the original
+     * so we register the entire family if we get a match here.
+     * This is still a big win because this code is invoked where
+     * otherwise we would register all fonts.
+     * It's also useful for the case where "Foo Bold" was specified with
+     * style Font.ITALIC, as we would want in that case to try to return
+     * "Foo Bold Italic" if it exists, and it is only by locating "Foo Bold"
+     * and opening it that we really "know" it's Bold, and can look for
+     * a font that supports that and the italic style.
+     * The code in here is not overtly windows-specific but in fact it
+     * is unlikely to be useful as is on other platforms. It is maintained
+     * in this shared source file to be close to its sole client and
+     * because so much of the logic is intertwined with the logic in
+     * findFont2D.
+     */
+    private Font2D findFontFromPlatform(String lcName, int style) {
+        if (getFullNameToFileMap().size() == 0) {
+            return null;
+        }
+
+        ArrayList<String> family = null;
+        String fontFile = null;
+        String familyName = fontToFamilyNameMap.get(lcName);
+        if (familyName != null) {
+            fontFile = fontToFileMap.get(lcName);
+            family = familyToFontListMap.get
+                (familyName.toLowerCase(Locale.ENGLISH));
+        } else {
+            family = familyToFontListMap.get(lcName); // is lcName is a family?
+            if (family != null && family.size() > 0) {
+                String lcFontName = family.get(0).toLowerCase(Locale.ENGLISH);
+                if (lcFontName != null) {
+                    familyName = fontToFamilyNameMap.get(lcFontName);
+                }
+            }
+        }
+        if (family == null || familyName == null) {
+            return null;
+        }
+        String [] fontList = (String[])family.toArray(STR_ARRAY);
+        if (fontList.length == 0) {
+            return null;
+        }
+
+        /* first check that for every font in this family we can find
+         * a font file. The specific reason for doing this is that
+         * in at least one case on Windows a font has the face name "David"
+         * but the registry entry is "David Regular". That is the "unique"
+         * name of the font but in other cases the registry contains the
+         * "full" name. See the specifications of name ids 3 and 4 in the
+         * TrueType 'name' table.
+         * In general this could cause a problem that we fail to register
+         * if we all members of a family that we may end up mapping to
+         * the wrong font member: eg return Bold when Plain is needed.
+         */
+        for (int f=0;f<fontList.length;f++) {
+            String fontNameLC = fontList[f].toLowerCase(Locale.ENGLISH);
+            String fileName = fontToFileMap.get(fontNameLC);
+            if (fileName == null) {
+                if (FontUtilities.isLogging()) {
+                    FontUtilities.getLogger()
+                          .info("Platform lookup : No file for font " +
+                                fontList[f] + " in family " +familyName);
+                }
+                return null;
+            }
+        }
+
+        /* Currently this code only looks for TrueType fonts, so format
+         * and rank can be specified without looking at the filename.
+         */
+        PhysicalFont physicalFont = null;
+        if (fontFile != null) {
+            physicalFont = registerFontFile(getPathName(fontFile), null,
+                                            FONTFORMAT_TRUETYPE, false,
+                                            Font2D.TTF_RANK);
+        }
+        /* Register all fonts in this family. */
+        for (int f=0;f<fontList.length;f++) {
+            String fontNameLC = fontList[f].toLowerCase(Locale.ENGLISH);
+            String fileName = fontToFileMap.get(fontNameLC);
+            if (fontFile != null && fontFile.equals(fileName)) {
+                continue;
+            }
+            /* Currently this code only looks for TrueType fonts, so format
+             * and rank can be specified without looking at the filename.
+             */
+            registerFontFile(getPathName(fileName), null,
+                             FONTFORMAT_TRUETYPE, false, Font2D.TTF_RANK);
+        }
+
+        Font2D font = null;
+        FontFamily fontFamily = FontFamily.getFamily(familyName);
+        /* Handle case where request "MyFont Bold", style=Font.ITALIC */
+        if (physicalFont != null) {
+            style |= physicalFont.style;
+        }
+        if (fontFamily != null) {
+            font = fontFamily.getFont(style);
+            if (font == null) {
+                font = fontFamily.getClosestStyle(style);
+            }
+        }
+        return font;
+    }
+
+    private ConcurrentHashMap<String, Font2D> fontNameCache =
+        new ConcurrentHashMap<String, Font2D>();
+
+    /*
+     * The client supplies a name and a style.
+     * The name could be a family name, or a full name.
+     * A font may exist with the specified style, or it may
+     * exist only in some other style. For non-native fonts the scaler
+     * may be able to emulate the required style.
+     */
+    public Font2D findFont2D(String name, int style, int fallback) {
+        String lowerCaseName = name.toLowerCase(Locale.ENGLISH);
+        String mapName = lowerCaseName + dotStyleStr(style);
+        Font2D font;
+
+        /* If preferLocaleFonts() or preferProportionalFonts() has been
+         * called we may be using an alternate set of composite fonts in this
+         * app context. The presence of a pre-built name map indicates whether
+         * this is so, and gives access to the alternate composite for the
+         * name.
+         */
+        if (_usingPerAppContextComposites) {
+            ConcurrentHashMap<String, Font2D> altNameCache =
+                (ConcurrentHashMap<String, Font2D>)
+                AppContext.getAppContext().get(CompositeFont.class);
+            if (altNameCache != null) {
+                font = (Font2D)altNameCache.get(mapName);
+            } else {
+                font = null;
+            }
+        } else {
+            font = fontNameCache.get(mapName);
+        }
+        if (font != null) {
+            return font;
+        }
+
+        if (FontUtilities.isLogging()) {
+            FontUtilities.getLogger().info("Search for font: " + name);
+        }
+
+        // The check below is just so that the bitmap fonts being set by
+        // AWT and Swing thru the desktop properties do not trigger the
+        // the load fonts case. The two bitmap fonts are now mapped to
+        // appropriate equivalents for serif and sansserif.
+        // Note that the cost of this comparison is only for the first
+        // call until the map is filled.
+        if (FontUtilities.isWindows) {
+            if (lowerCaseName.equals("ms sans serif")) {
+                name = "sansserif";
+            } else if (lowerCaseName.equals("ms serif")) {
+                name = "serif";
+            }
+        }
+
+        /* This isn't intended to support a client passing in the
+         * string default, but if a client passes in null for the name
+         * the java.awt.Font class internally substitutes this name.
+         * So we need to recognise it here to prevent a loadFonts
+         * on the unrecognised name. The only potential problem with
+         * this is it would hide any real font called "default"!
+         * But that seems like a potential problem we can ignore for now.
+         */
+        if (lowerCaseName.equals("default")) {
+            name = "dialog";
+        }
+
+        /* First see if its a family name. */
+        FontFamily family = FontFamily.getFamily(name);
+        if (family != null) {
+            font = family.getFontWithExactStyleMatch(style);
+            if (font == null) {
+                font = findDeferredFont(name, style);
+            }
+            if (font == null) {
+                font = family.getFont(style);
+            }
+            if (font == null) {
+                font = family.getClosestStyle(style);
+            }
+            if (font != null) {
+                fontNameCache.put(mapName, font);
+                return font;
+            }
+        }
+
+        /* If it wasn't a family name, it should be a full name of
+         * either a composite, or a physical font
+         */
+        font = fullNameToFont.get(lowerCaseName);
+        if (font != null) {
+            /* Check that the requested style matches the matched font's style.
+             * But also match style automatically if the requested style is
+             * "plain". This because the existing behaviour is that the fonts
+             * listed via getAllFonts etc always list their style as PLAIN.
+             * This does lead to non-commutative behaviours where you might
+             * start with "Lucida Sans Regular" and ask for a BOLD version
+             * and get "Lucida Sans DemiBold" but if you ask for the PLAIN
+             * style of "Lucida Sans DemiBold" you get "Lucida Sans DemiBold".
+             * This consistent however with what happens if you have a bold
+             * version of a font and no plain version exists - alg. styling
+             * doesn't "unbolden" the font.
+             */
+            if (font.style == style || style == Font.PLAIN) {
+                fontNameCache.put(mapName, font);
+                return font;
+            } else {
+                /* If it was a full name like "Lucida Sans Regular", but
+                 * the style requested is "bold", then we want to see if
+                 * there's the appropriate match against another font in
+                 * that family before trying to load all fonts, or applying a
+                 * algorithmic styling
+                 */
+                family = FontFamily.getFamily(font.getFamilyName(null));
+                if (family != null) {
+                    Font2D familyFont = family.getFont(style|font.style);
+                    /* We exactly matched the requested style, use it! */
+                    if (familyFont != null) {
+                        fontNameCache.put(mapName, familyFont);
+                        return familyFont;
+                    } else {
+                        /* This next call is designed to support the case
+                         * where bold italic is requested, and if we must
+                         * style, then base it on either bold or italic -
+                         * not on plain!
+                         */
+                        familyFont = family.getClosestStyle(style|font.style);
+                        if (familyFont != null) {
+                            /* The next check is perhaps one
+                             * that shouldn't be done. ie if we get this
+                             * far we have probably as close a match as we
+                             * are going to get. We could load all fonts to
+                             * see if somehow some parts of the family are
+                             * loaded but not all of it.
+                             */
+                            if (familyFont.canDoStyle(style|font.style)) {
+                                fontNameCache.put(mapName, familyFont);
+                                return familyFont;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        if (FontUtilities.isWindows) {
+            /* Don't want Windows to return a Lucida Sans font from
+             * C:\Windows\Fonts
+             */
+            if (deferredFontFiles.size() > 0) {
+                font = findJREDeferredFont(lowerCaseName, style);
+                if (font != null) {
+                    fontNameCache.put(mapName, font);
+                    return font;
+                }
+            }
+            font = findFontFromPlatform(lowerCaseName, style);
+            if (font != null) {
+                if (FontUtilities.isLogging()) {
+                    FontUtilities.getLogger()
+                          .info("Found font via platform API for request:\"" +
+                                name + "\":, style="+style+
+                                " found font: " + font);
+                }
+                fontNameCache.put(mapName, font);
+                return font;
+            }
+        }
+
+        /* If reach here and no match has been located, then if there are
+         * uninitialised deferred fonts, load as many of those as needed
+         * to find the deferred font. If none is found through that
+         * search continue on.
+         * There is possibly a minor issue when more than one
+         * deferred font implements the same font face. Since deferred
+         * fonts are only those in font configuration files, this is a
+         * controlled situation, the known case being Solaris euro_fonts
+         * versions of Arial, Times New Roman, Courier New. However
+         * the larger font will transparently replace the smaller one
+         *  - see addToFontList() - when it is needed by the composite font.
+         */
+        if (deferredFontFiles.size() > 0) {
+            font = findDeferredFont(name, style);
+            if (font != null) {
+                fontNameCache.put(mapName, font);
+                return font;
+            }
+        }
+
+        /* Some apps use deprecated 1.0 names such as helvetica and courier. On
+         * Solaris these are Type1 fonts in /usr/openwin/lib/X11/fonts/Type1.
+         * If running on Solaris will register all the fonts in this
+         * directory.
+         * May as well register the whole directory without actually testing
+         * the font name is one of the deprecated names as the next step would
+         * load all fonts which are in this directory anyway.
+         * In the event that this lookup is successful it potentially "hides"
+         * TrueType versions of such fonts that are elsewhere but since they
+         * do not exist on Solaris this is not a problem.
+         * Set a flag to indicate we've done this registration to avoid
+         * repetition and more seriously, to avoid recursion.
+         */
+        if (FontUtilities.isSolaris &&!loaded1dot0Fonts) {
+            /* "timesroman" is a special case since that's not the
+             * name of any known font on Solaris or elsewhere.
+             */
+            if (lowerCaseName.equals("timesroman")) {
+                font = findFont2D("serif", style, fallback);
+                fontNameCache.put(mapName, font);
+            }
+            register1dot0Fonts();
+            loaded1dot0Fonts = true;
+            Font2D ff = findFont2D(name, style, fallback);
+            return ff;
+        }
+
+        /* We check for application registered fonts before
+         * explicitly loading all fonts as if necessary the registration
+         * code will have done so anyway. And we don't want to needlessly
+         * load the actual files for all fonts.
+         * Just as for installed fonts we check for family before fullname.
+         * We do not add these fonts to fontNameCache for the
+         * app context case which eliminates the overhead of a per context
+         * cache for these.
+         */
+
+        if (fontsAreRegistered || fontsAreRegisteredPerAppContext) {
+            Hashtable<String, FontFamily> familyTable = null;
+            Hashtable<String, Font2D> nameTable;
+
+            if (fontsAreRegistered) {
+                familyTable = createdByFamilyName;
+                nameTable = createdByFullName;
+            } else {
+                AppContext appContext = AppContext.getAppContext();
+                familyTable =
+                    (Hashtable<String,FontFamily>)appContext.get(regFamilyKey);
+                nameTable =
+                    (Hashtable<String,Font2D>)appContext.get(regFullNameKey);
+            }
+
+            family = familyTable.get(lowerCaseName);
+            if (family != null) {
+                font = family.getFontWithExactStyleMatch(style);
+                if (font == null) {
+                    font = family.getFont(style);
+                }
+                if (font == null) {
+                    font = family.getClosestStyle(style);
+                }
+                if (font != null) {
+                    if (fontsAreRegistered) {
+                        fontNameCache.put(mapName, font);
+                    }
+                    return font;
+                }
+            }
+            font = nameTable.get(lowerCaseName);
+            if (font != null) {
+                if (fontsAreRegistered) {
+                    fontNameCache.put(mapName, font);
+                }
+                return font;
+            }
+        }
+
+        /* If reach here and no match has been located, then if all fonts
+         * are not yet loaded, do so, and then recurse.
+         */
+        if (!loadedAllFonts) {
+            if (FontUtilities.isLogging()) {
+                FontUtilities.getLogger()
+                                       .info("Load fonts looking for:" + name);
+            }
+            loadFonts();
+            loadedAllFonts = true;
+            return findFont2D(name, style, fallback);
+        }
+
+        if (!loadedAllFontFiles) {
+            if (FontUtilities.isLogging()) {
+                FontUtilities.getLogger()
+                                  .info("Load font files looking for:" + name);
+            }
+            loadFontFiles();
+            loadedAllFontFiles = true;
+            return findFont2D(name, style, fallback);
+        }
+
+        /* The primary name is the locale default - ie not US/English but
+         * whatever is the default in this locale. This is the way it always
+         * has been but may be surprising to some developers if "Arial Regular"
+         * were hard-coded in their app and yet "Arial Regular" was not the
+         * default name. Fortunately for them, as a consequence of the JDK
+         * supporting returning names and family names for arbitrary locales,
+         * we also need to support searching all localised names for a match.
+         * But because this case of the name used to reference a font is not
+         * the same as the default for this locale is rare, it makes sense to
+         * search a much shorter list of default locale names and only go to
+         * a longer list of names in the event that no match was found.
+         * So add here code which searches localised names too.
+         * As in 1.4.x this happens only after loading all fonts, which
+         * is probably the right order.
+         */
+        if ((font = findFont2DAllLocales(name, style)) != null) {
+            fontNameCache.put(mapName, font);
+            return font;
+        }
+
+        /* Perhaps its a "compatibility" name - timesroman, helvetica,
+         * or courier, which 1.0 apps used for logical fonts.
+         * We look for these "late" after a loadFonts as we must not
+         * hide real fonts of these names.
+         * Map these appropriately:
+         * On windows this means according to the rules specified by the
+         * FontConfiguration : do it only for encoding==Cp1252
+         *
+         * REMIND: this is something we plan to remove.
+         */
+        if (FontUtilities.isWindows) {
+            String compatName =
+                getFontConfiguration().getFallbackFamilyName(name, null);
+            if (compatName != null) {
+                font = findFont2D(compatName, style, fallback);
+                fontNameCache.put(mapName, font);
+                return font;
+            }
+        } else if (lowerCaseName.equals("timesroman")) {
+            font = findFont2D("serif", style, fallback);
+            fontNameCache.put(mapName, font);
+            return font;
+        } else if (lowerCaseName.equals("helvetica")) {
+            font = findFont2D("sansserif", style, fallback);
+            fontNameCache.put(mapName, font);
+            return font;
+        } else if (lowerCaseName.equals("courier")) {
+            font = findFont2D("monospaced", style, fallback);
+            fontNameCache.put(mapName, font);
+            return font;
+        }
+
+        if (FontUtilities.isLogging()) {
+            FontUtilities.getLogger().info("No font found for:" + name);
+        }
+
+        switch (fallback) {
+        case PHYSICAL_FALLBACK: return getDefaultPhysicalFont();
+        case LOGICAL_FALLBACK: return getDefaultLogicalFont(style);
+        default: return null;
+        }
+    }
+
+    /*
+     * Workaround for apps which are dependent on a font metrics bug
+     * in JDK 1.1. This is an unsupported win32 private setting.
+     * Left in for a customer - do not remove.
+     */
+    public boolean usePlatformFontMetrics() {
+        return usePlatformFontMetrics;
+    }
+
+    public int getNumFonts() {
+        return physicalFonts.size()+maxCompFont;
+    }
+
+    private static boolean fontSupportsEncoding(Font font, String encoding) {
+        return FontUtilities.getFont2D(font).supportsEncoding(encoding);
+    }
+
+    public abstract String getFontPath(boolean noType1Fonts);
+
+    private Thread fileCloser = null;
+    Vector<File> tmpFontFiles = null;
+
+    public Font2D createFont2D(File fontFile, int fontFormat,
+                               boolean isCopy, CreatedFontTracker tracker)
+    throws FontFormatException {
+
+        String fontFilePath = fontFile.getPath();
+        FileFont font2D = null;
+        final File fFile = fontFile;
+        final CreatedFontTracker _tracker = tracker;
+        try {
+            switch (fontFormat) {
+            case Font.TRUETYPE_FONT:
+                font2D = new TrueTypeFont(fontFilePath, null, 0, true);
+                break;
+            case Font.TYPE1_FONT:
+                font2D = new Type1Font(fontFilePath, null, isCopy);
+                break;
+            default:
+                throw new FontFormatException("Unrecognised Font Format");
+            }
+        } catch (FontFormatException e) {
+            if (isCopy) {
+                java.security.AccessController.doPrivileged(
+                     new java.security.PrivilegedAction() {
+                          public Object run() {
+                              if (_tracker != null) {
+                                  _tracker.subBytes((int)fFile.length());
+                              }
+                              fFile.delete();
+                              return null;
+                          }
+                });
+            }
+            throw(e);
+        }
+        if (isCopy) {
+            font2D.setFileToRemove(fontFile, tracker);
+            synchronized (FontManager.class) {
+
+                if (tmpFontFiles == null) {
+                    tmpFontFiles = new Vector<File>();
+                }
+                tmpFontFiles.add(fontFile);
+
+                if (fileCloser == null) {
+                    final Runnable fileCloserRunnable = new Runnable() {
+                      public void run() {
+                         java.security.AccessController.doPrivileged(
+                         new java.security.PrivilegedAction() {
+                         public Object run() {
+
+                            for (int i=0;i<CHANNELPOOLSIZE;i++) {
+                                if (fontFileCache[i] != null) {
+                                    try {
+                                        fontFileCache[i].close();
+                                    } catch (Exception e) {
+                                    }
+                                }
+                            }
+                            if (tmpFontFiles != null) {
+                                File[] files = new File[tmpFontFiles.size()];
+                                files = tmpFontFiles.toArray(files);
+                                for (int f=0; f<files.length;f++) {
+                                    try {
+                                        files[f].delete();
+                                    } catch (Exception e) {
+                                    }
+                                }
+                            }
+
+                            return null;
+                          }
+
+                          });
+                      }
+                    };
+                    java.security.AccessController.doPrivileged(
+                       new java.security.PrivilegedAction() {
+                          public Object run() {
+                              /* The thread must be a member of a thread group
+                               * which will not get GCed before VM exit.
+                               * Make its parent the top-level thread group.
+                               */
+                              ThreadGroup tg =
+                                  Thread.currentThread().getThreadGroup();
+                              for (ThreadGroup tgn = tg;
+                                   tgn != null;
+                                   tg = tgn, tgn = tg.getParent());
+                              fileCloser = new Thread(tg, fileCloserRunnable);
+                              Runtime.getRuntime().addShutdownHook(fileCloser);
+                              return null;
+                          }
+                    });
+                }
+            }
+        }
+        return font2D;
+    }
+
+    /* remind: used in X11GraphicsEnvironment and called often enough
+     * that we ought to obsolete this code
+     */
+    public synchronized String getFullNameByFileName(String fileName) {
+        PhysicalFont[] physFonts = getPhysicalFonts();
+        for (int i=0;i<physFonts.length;i++) {
+            if (physFonts[i].platName.equals(fileName)) {
+                return (physFonts[i].getFontName(null));
+            }
+        }
+        return null;
+    }
+
+    /*
+     * This is called when font is determined to be invalid/bad.
+     * It designed to be called (for example) by the font scaler
+     * when in processing a font file it is discovered to be incorrect.
+     * This is different than the case where fonts are discovered to
+     * be incorrect during initial verification, as such fonts are
+     * never registered.
+     * Handles to this font held are re-directed to a default font.
+     * This default may not be an ideal substitute buts it better than
+     * crashing This code assumes a PhysicalFont parameter as it doesn't
+     * make sense for a Composite to be "bad".
+     */
+    public synchronized void deRegisterBadFont(Font2D font2D) {
+        if (!(font2D instanceof PhysicalFont)) {
+            /* We should never reach here, but just in case */
+            return;
+        } else {
+            if (FontUtilities.isLogging()) {
+                FontUtilities.getLogger()
+                                     .severe("Deregister bad font: " + font2D);
+            }
+            replaceFont((PhysicalFont)font2D, getDefaultPhysicalFont());
+        }
+    }
+
+    /*
+     * This encapsulates all the work that needs to be done when a
+     * Font2D is replaced by a different Font2D.
+     */
+    public synchronized void replaceFont(PhysicalFont oldFont,
+                                         PhysicalFont newFont) {
+
+        if (oldFont.handle.font2D != oldFont) {
+            /* already done */
+            return;
+        }
+
+        /* If we try to replace the font with itself, that won't work,
+         * so pick any alternative physical font
+         */
+        if (oldFont == newFont) {
+            if (FontUtilities.isLogging()) {
+                FontUtilities.getLogger()
+                      .severe("Can't replace bad font with itself " + oldFont);
+            }
+            PhysicalFont[] physFonts = getPhysicalFonts();
+            for (int i=0; i<physFonts.length;i++) {
+                if (physFonts[i] != newFont) {
+                    newFont = physFonts[i];
+                    break;
+                }
+            }
+            if (oldFont == newFont) {
+                if (FontUtilities.isLogging()) {
+                    FontUtilities.getLogger()
+                           .severe("This is bad. No good physicalFonts found.");
+                }
+                return;
+            }
+        }
+
+        /* eliminate references to this font, so it won't be located
+         * by future callers, and will be eligible for GC when all
+         * references are removed
+         */
+        oldFont.handle.font2D = newFont;
+        physicalFonts.remove(oldFont.fullName);
+        fullNameToFont.remove(oldFont.fullName.toLowerCase(Locale.ENGLISH));
+        FontFamily.remove(oldFont);
+
+        if (localeFullNamesToFont != null) {
+            Map.Entry[] mapEntries =
+                (Map.Entry[])localeFullNamesToFont.entrySet().
+                toArray(new Map.Entry[0]);
+            /* Should I be replacing these, or just I just remove
+             * the names from the map?
+             */
+            for (int i=0; i<mapEntries.length;i++) {
+                if (mapEntries[i].getValue() == oldFont) {
+                    try {
+                        mapEntries[i].setValue(newFont);
+                    } catch (Exception e) {
+                        /* some maps don't support this operation.
+                         * In this case just give up and remove the entry.
+                         */
+                        localeFullNamesToFont.remove(mapEntries[i].getKey());
+                    }
+                }
+            }
+        }
+
+        for (int i=0; i<maxCompFont; i++) {
+            /* Deferred initialization of composites shouldn't be
+             * a problem for this case, since a font must have been
+             * initialised to be discovered to be bad.
+             * Some JRE composites on Solaris use two versions of the same
+             * font. The replaced font isn't bad, just "smaller" so there's
+             * no need to make the slot point to the new font.
+             * Since composites have a direct reference to the Font2D (not
+             * via a handle) making this substitution is not safe and could
+             * cause an additional problem and so this substitution is
+             * warranted only when a font is truly "bad" and could cause
+             * a crash. So we now replace it only if its being substituted
+             * with some font other than a fontconfig rank font
+             * Since in practice a substitution will have the same rank
+             * this may never happen, but the code is safer even if its
+             * also now a no-op.
+             * The only obvious "glitch" from this stems from the current
+             * implementation that when asked for the number of glyphs in a
+             * composite it lies and returns the number in slot 0 because
+             * composite glyphs aren't contiguous. Since we live with that
+             * we can live with the glitch that depending on how it was
+             * initialised a composite may return different values for this.
+             * Fixing the issues with composite glyph ids is tricky as
+             * there are exclusion ranges and unlike other fonts even the
+             * true "numGlyphs" isn't a contiguous range. Likely the only
+             * solution is an API that returns an array of glyph ranges
+             * which takes precedence over the existing API. That might
+             * also need to address excluding ranges which represent a
+             * code point supported by an earlier component.
+             */
+            if (newFont.getRank() > Font2D.FONT_CONFIG_RANK) {
+                compFonts[i].replaceComponentFont(oldFont, newFont);
+            }
+        }
+    }
+
+    private synchronized void loadLocaleNames() {
+        if (localeFullNamesToFont != null) {
+            return;
+        }
+        localeFullNamesToFont = new HashMap<String, TrueTypeFont>();
+        Font2D[] fonts = getRegisteredFonts();
+        for (int i=0; i<fonts.length; i++) {
+            if (fonts[i] instanceof TrueTypeFont) {
+                TrueTypeFont ttf = (TrueTypeFont)fonts[i];
+                String[] fullNames = ttf.getAllFullNames();
+                for (int n=0; n<fullNames.length; n++) {
+                    localeFullNamesToFont.put(fullNames[n], ttf);
+                }
+                FontFamily family = FontFamily.getFamily(ttf.familyName);
+                if (family != null) {
+                    FontFamily.addLocaleNames(family, ttf.getAllFamilyNames());
+                }
+            }
+        }
+    }
+
+    /* This replicate the core logic of findFont2D but operates on
+     * all the locale names. This hasn't been merged into findFont2D to
+     * keep the logic simpler and reduce overhead, since this case is
+     * almost never used. The main case in which it is called is when
+     * a bogus font name is used and we need to check all possible names
+     * before returning the default case.
+     */
+    private Font2D findFont2DAllLocales(String name, int style) {
+
+        if (FontUtilities.isLogging()) {
+            FontUtilities.getLogger()
+                           .info("Searching localised font names for:" + name);
+        }
+
+        /* If reach here and no match has been located, then if we have
+         * not yet built the map of localeFullNamesToFont for TT fonts, do so
+         * now. This method must be called after all fonts have been loaded.
+         */
+        if (localeFullNamesToFont == null) {
+            loadLocaleNames();
+        }
+        String lowerCaseName = name.toLowerCase();
+        Font2D font = null;
+
+        /* First see if its a family name. */
+        FontFamily family = FontFamily.getLocaleFamily(lowerCaseName);
+        if (family != null) {
+          font = family.getFont(style);
+          if (font == null) {
+            font = family.getClosestStyle(style);
+          }
+          if (font != null) {
+              return font;
+          }
+        }
+
+        /* If it wasn't a family name, it should be a full name. */
+        synchronized (this) {
+            font = localeFullNamesToFont.get(name);
+        }
+        if (font != null) {
+            if (font.style == style || style == Font.PLAIN) {
+                return font;
+            } else {
+                family = FontFamily.getFamily(font.getFamilyName(null));
+                if (family != null) {
+                    Font2D familyFont = family.getFont(style);
+                    /* We exactly matched the requested style, use it! */
+                    if (familyFont != null) {
+                        return familyFont;
+                    } else {
+                        familyFont = family.getClosestStyle(style);
+                        if (familyFont != null) {
+                            /* The next check is perhaps one
+                             * that shouldn't be done. ie if we get this
+                             * far we have probably as close a match as we
+                             * are going to get. We could load all fonts to
+                             * see if somehow some parts of the family are
+                             * loaded but not all of it.
+                             * This check is commented out for now.
+                             */
+                            if (!familyFont.canDoStyle(style)) {
+                                familyFont = null;
+                            }
+                            return familyFont;
+                        }
+                    }
+                }
+            }
+        }
+        return font;
+    }
+
+    /* Supporting "alternate" composite fonts on 2D graphics objects
+     * is accessed by the application by calling methods on the local
+     * GraphicsEnvironment. The overall implementation is described
+     * in one place, here, since otherwise the implementation is spread
+     * around it may be difficult to track.
+     * The methods below call into SunGraphicsEnvironment which creates a
+     * new FontConfiguration instance. The FontConfiguration class,
+     * and its platform sub-classes are updated to take parameters requesting
+     * these behaviours. This is then used to create new composite font
+     * instances. Since this calls the initCompositeFont method in
+     * SunGraphicsEnvironment it performs the same initialization as is
+     * performed normally. There may be some duplication of effort, but
+     * that code is already written to be able to perform properly if called
+     * to duplicate work. The main difference is that if we detect we are
+     * running in an applet/browser/Java plugin environment these new fonts
+     * are not placed in the "default" maps but into an AppContext instance.
+     * The font lookup mechanism in java.awt.Font.getFont2D() is also updated
+     * so that look-up for composite fonts will in that case always
+     * do a lookup rather than returning a cached result.
+     * This is inefficient but necessary else singleton java.awt.Font
+     * instances would not retrieve the correct Font2D for the appcontext.
+     * sun.font.FontManager.findFont2D is also updated to that it uses
+     * a name map cache specific to that appcontext.
+     *
+     * Getting an AppContext is expensive, so there is a global variable
+     * that records whether these methods have ever been called and can
+     * avoid the expense for almost all applications. Once the correct
+     * CompositeFont is associated with the Font, everything should work
+     * through existing mechanisms.
+     * A special case is that GraphicsEnvironment.getAllFonts() must
+     * return an AppContext specific list.
+     *
+     * Calling the methods below is "heavyweight" but it is expected that
+     * these methods will be called very rarely.
+     *
+     * If _usingPerAppContextComposites is true, we are in "applet"
+     * (eg browser) enviroment and at least one context has selected
+     * an alternate composite font behaviour.
+     * If _usingAlternateComposites is true, we are not in an "applet"
+     * environment and the (single) application has selected
+     * an alternate composite font behaviour.
+     *
+     * - Printing: The implementation delegates logical fonts to an AWT
+     * mechanism which cannot use these alternate configurations.
+     * We can detect that alternate fonts are in use and back-off to 2D, but
+     * that uses outlines. Much of this can be fixed with additional work
+     * but that may have to wait. The results should be correct, just not
+     * optimal.
+     */
+    private static final Object altJAFontKey       = new Object();
+    private static final Object localeFontKey       = new Object();
+    private static final Object proportionalFontKey = new Object();
+    private boolean _usingPerAppContextComposites = false;
+    private boolean _usingAlternateComposites = false;
+
+    /* These values are used only if we are running as a standalone
+     * application, as determined by maybeMultiAppContext();
+     */
+    private static boolean gAltJAFont = false;
+    private boolean gLocalePref = false;
+    private boolean gPropPref = false;
+
+    /* This method doesn't check if alternates are selected in this app
+     * context. Its used by the FontMetrics caching code which in such
+     * a case cannot retrieve a cached metrics solely on the basis of
+     * the Font.equals() method since it needs to also check if the Font2D
+     * is the same.
+     * We also use non-standard composites for Swing native L&F fonts on
+     * Windows. In that case the policy is that the metrics reported are
+     * based solely on the physical font in the first slot which is the
+     * visible java.awt.Font. So in that case the metrics cache which tests
+     * the Font does what we want. In the near future when we expand the GTK
+     * logical font definitions we may need to revisit this if GTK reports
+     * combined metrics instead. For now though this test can be simple.
+     */
+    public boolean maybeUsingAlternateCompositeFonts() {
+       return _usingAlternateComposites || _usingPerAppContextComposites;
+    }
+
+    public boolean usingAlternateCompositeFonts() {
+        return (_usingAlternateComposites ||
+                (_usingPerAppContextComposites &&
+                AppContext.getAppContext().get(CompositeFont.class) != null));
+    }
+
+    private static boolean maybeMultiAppContext() {
+        Boolean appletSM = (Boolean)
+            java.security.AccessController.doPrivileged(
+                new java.security.PrivilegedAction() {
+                        public Object run() {
+                            SecurityManager sm = System.getSecurityManager();
+                            return new Boolean
+                                (sm instanceof sun.applet.AppletSecurity);
+                        }
+                    });
+        return appletSM.booleanValue();
+    }
+
+    /* Modifies the behaviour of a subsequent call to preferLocaleFonts()
+     * to use Mincho instead of Gothic for dialoginput in JA locales
+     * on windows. Not needed on other platforms.
+     */
+    public synchronized void useAlternateFontforJALocales() {
+
+        if (!FontUtilities.isWindows) {
+            return;
+        }
+
+        if (!maybeMultiAppContext()) {
+            gAltJAFont = true;
+        } else {
+            AppContext appContext = AppContext.getAppContext();
+            appContext.put(altJAFontKey, altJAFontKey);
+        }
+    }
+
+    public boolean usingAlternateFontforJALocales() {
+        if (!maybeMultiAppContext()) {
+            return gAltJAFont;
+        } else {
+            AppContext appContext = AppContext.getAppContext();
+            return appContext.get(altJAFontKey) == altJAFontKey;
+        }
+    }
+
+    public synchronized void preferLocaleFonts() {
+
+        /* Test if re-ordering will have any effect */
+        if (!FontConfiguration.willReorderForStartupLocale()) {
+            return;
+        }
+
+        if (!maybeMultiAppContext()) {
+            if (gLocalePref == true) {
+                return;
+            }
+            gLocalePref = true;
+            createCompositeFonts(fontNameCache, gLocalePref, gPropPref);
+            _usingAlternateComposites = true;
+        } else {
+            AppContext appContext = AppContext.getAppContext();
+            if (appContext.get(localeFontKey) == localeFontKey) {
+                return;
+            }
+            appContext.put(localeFontKey, localeFontKey);
+            boolean acPropPref =
+                appContext.get(proportionalFontKey) == proportionalFontKey;
+            ConcurrentHashMap<String, Font2D>
+                altNameCache = new ConcurrentHashMap<String, Font2D> ();
+            /* If there is an existing hashtable, we can drop it. */
+            appContext.put(CompositeFont.class, altNameCache);
+            _usingPerAppContextComposites = true;
+            createCompositeFonts(altNameCache, true, acPropPref);
+        }
+    }
+
+    public synchronized void preferProportionalFonts() {
+
+        /* If no proportional fonts are configured, there's no need
+         * to take any action.
+         */
+        if (!FontConfiguration.hasMonoToPropMap()) {
+            return;
+        }
+
+        if (!maybeMultiAppContext()) {
+            if (gPropPref == true) {
+                return;
+            }
+            gPropPref = true;
+            createCompositeFonts(fontNameCache, gLocalePref, gPropPref);
+            _usingAlternateComposites = true;
+        } else {
+            AppContext appContext = AppContext.getAppContext();
+            if (appContext.get(proportionalFontKey) == proportionalFontKey) {
+                return;
+            }
+            appContext.put(proportionalFontKey, proportionalFontKey);
+            boolean acLocalePref =
+                appContext.get(localeFontKey) == localeFontKey;
+            ConcurrentHashMap<String, Font2D>
+                altNameCache = new ConcurrentHashMap<String, Font2D> ();
+            /* If there is an existing hashtable, we can drop it. */
+            appContext.put(CompositeFont.class, altNameCache);
+            _usingPerAppContextComposites = true;
+            createCompositeFonts(altNameCache, acLocalePref, true);
+        }
+    }
+
+    private static HashSet<String> installedNames = null;
+    private static HashSet<String> getInstalledNames() {
+        if (installedNames == null) {
+           Locale l = getSystemStartupLocale();
+           SunFontManager fontManager = SunFontManager.getInstance();
+           String[] installedFamilies =
+               fontManager.getInstalledFontFamilyNames(l);
+           Font[] installedFonts = fontManager.getAllInstalledFonts();
+           HashSet<String> names = new HashSet<String>();
+           for (int i=0; i<installedFamilies.length; i++) {
+               names.add(installedFamilies[i].toLowerCase(l));
+           }
+           for (int i=0; i<installedFonts.length; i++) {
+               names.add(installedFonts[i].getFontName(l).toLowerCase(l));
+           }
+           installedNames = names;
+        }
+        return installedNames;
+    }
+
+    /* Keys are used to lookup per-AppContext Hashtables */
+    private static final Object regFamilyKey  = new Object();
+    private static final Object regFullNameKey = new Object();
+    private Hashtable<String,FontFamily> createdByFamilyName;
+    private Hashtable<String,Font2D>     createdByFullName;
+    private boolean fontsAreRegistered = false;
+    private boolean fontsAreRegisteredPerAppContext = false;
+
+    public boolean registerFont(Font font) {
+        /* This method should not be called with "null".
+         * It is the caller's responsibility to ensure that.
+         */
+        if (font == null) {
+            return false;
+        }
+
+        /* Initialise these objects only once we start to use this API */
+        synchronized (regFamilyKey) {
+            if (createdByFamilyName == null) {
+                createdByFamilyName = new Hashtable<String,FontFamily>();
+                createdByFullName = new Hashtable<String,Font2D>();
+            }
+        }
+
+        if (! FontAccess.getFontAccess().isCreatedFont(font)) {
+            return false;
+        }
+        /* We want to ensure that this font cannot override existing
+         * installed fonts. Check these conditions :
+         * - family name is not that of an installed font
+         * - full name is not that of an installed font
+         * - family name is not the same as the full name of an installed font
+         * - full name is not the same as the family name of an installed font
+         * The last two of these may initially look odd but the reason is
+         * that (unfortunately) Font constructors do not distinuguish these.
+         * An extreme example of such a problem would be a font which has
+         * family name "Dialog.Plain" and full name of "Dialog".
+         * The one arguably overly stringent restriction here is that if an
+         * application wants to supply a new member of an existing family
+         * It will get rejected. But since the JRE can perform synthetic
+         * styling in many cases its not necessary.
+         * We don't apply the same logic to registered fonts. If apps want
+         * to do this lets assume they have a reason. It won't cause problems
+         * except for themselves.
+         */
+        HashSet<String> names = getInstalledNames();
+        Locale l = getSystemStartupLocale();
+        String familyName = font.getFamily(l).toLowerCase();
+        String fullName = font.getFontName(l).toLowerCase();
+        if (names.contains(familyName) || names.contains(fullName)) {
+            return false;
+        }
+
+        /* Checks passed, now register the font */
+        Hashtable<String,FontFamily> familyTable;
+        Hashtable<String,Font2D> fullNameTable;
+        if (!maybeMultiAppContext()) {
+            familyTable = createdByFamilyName;
+            fullNameTable = createdByFullName;
+            fontsAreRegistered = true;
+        } else {
+            AppContext appContext = AppContext.getAppContext();
+            familyTable =
+                (Hashtable<String,FontFamily>)appContext.get(regFamilyKey);
+            fullNameTable =
+                (Hashtable<String,Font2D>)appContext.get(regFullNameKey);
+            if (familyTable == null) {
+                familyTable = new Hashtable<String,FontFamily>();
+                fullNameTable = new Hashtable<String,Font2D>();
+                appContext.put(regFamilyKey, familyTable);
+                appContext.put(regFullNameKey, fullNameTable);
+            }
+            fontsAreRegisteredPerAppContext = true;
+        }
+        /* Create the FontFamily and add font to the tables */
+        Font2D font2D = FontUtilities.getFont2D(font);
+        int style = font2D.getStyle();
+        FontFamily family = familyTable.get(familyName);
+        if (family == null) {
+            family = new FontFamily(font.getFamily(l));
+            familyTable.put(familyName, family);
+        }
+        /* Remove name cache entries if not using app contexts.
+         * To accommodate a case where code may have registered first a plain
+         * family member and then used it and is now registering a bold family
+         * member, we need to remove all members of the family, so that the
+         * new style can get picked up rather than continuing to synthesise.
+         */
+        if (fontsAreRegistered) {
+            removeFromCache(family.getFont(Font.PLAIN));
+            removeFromCache(family.getFont(Font.BOLD));
+            removeFromCache(family.getFont(Font.ITALIC));
+            removeFromCache(family.getFont(Font.BOLD|Font.ITALIC));
+            removeFromCache(fullNameTable.get(fullName));
+        }
+        family.setFont(font2D, style);
+        fullNameTable.put(fullName, font2D);
+        return true;
+    }
+
+    /* Remove from the name cache all references to the Font2D */
+    private void removeFromCache(Font2D font) {
+        if (font == null) {
+            return;
+        }
+        String[] keys = (String[])(fontNameCache.keySet().toArray(STR_ARRAY));
+        for (int k=0; k<keys.length;k++) {
+            if (fontNameCache.get(keys[k]) == font) {
+                fontNameCache.remove(keys[k]);
+            }
+        }
+    }
+
+    // It may look odd to use TreeMap but its more convenient to the caller.
+    public TreeMap<String, String> getCreatedFontFamilyNames() {
+
+        Hashtable<String,FontFamily> familyTable;
+        if (fontsAreRegistered) {
+            familyTable = createdByFamilyName;
+        } else if (fontsAreRegisteredPerAppContext) {
+            AppContext appContext = AppContext.getAppContext();
+            familyTable =
+                (Hashtable<String,FontFamily>)appContext.get(regFamilyKey);
+        } else {
+            return null;
+        }
+
+        Locale l = getSystemStartupLocale();
+        synchronized (familyTable) {
+            TreeMap<String, String> map = new TreeMap<String, String>();
+            for (FontFamily f : familyTable.values()) {
+                Font2D font2D = f.getFont(Font.PLAIN);
+                if (font2D == null) {
+                    font2D = f.getClosestStyle(Font.PLAIN);
+                }
+                String name = font2D.getFamilyName(l);
+                map.put(name.toLowerCase(l), name);
+            }
+            return map;
+        }
+    }
+
+    public Font[] getCreatedFonts() {
+
+        Hashtable<String,Font2D> nameTable;
+        if (fontsAreRegistered) {
+            nameTable = createdByFullName;
+        } else if (fontsAreRegisteredPerAppContext) {
+            AppContext appContext = AppContext.getAppContext();
+            nameTable =
+                (Hashtable<String,Font2D>)appContext.get(regFullNameKey);
+        } else {
+            return null;
+        }
+
+        Locale l = getSystemStartupLocale();
+        synchronized (nameTable) {
+            Font[] fonts = new Font[nameTable.size()];
+            int i=0;
+            for (Font2D font2D : nameTable.values()) {
+                fonts[i++] = new Font(font2D.getFontName(l), Font.PLAIN, 1);
+            }
+            return fonts;
+        }
+    }
+
+    protected String[] getPlatformFontDirs(boolean noType1Fonts) {
+        String path = getFontPath(true);
+        StringTokenizer parser =
+            new StringTokenizer(path, File.pathSeparator);
+        ArrayList<String> pathList = new ArrayList<String>();
+        try {
+            while (parser.hasMoreTokens()) {
+                pathList.add(parser.nextToken());
+            }
+        } catch (NoSuchElementException e) {
+        }
+        return pathList.toArray(new String[0]);
+    }
+
+    /**
+     * Returns an array of two strings. The first element is the
+     * name of the font. The second element is the file name.
+     */
+    public abstract String[] getDefaultPlatformFont();
+
+    // Begin: Refactored from SunGraphicsEnviroment.
+
+    /*
+     * helper function for registerFonts
+     */
+    private void addDirFonts(String dirName, File dirFile,
+                             FilenameFilter filter,
+                             int fontFormat, boolean useJavaRasterizer,
+                             int fontRank,
+                             boolean defer, boolean resolveSymLinks) {
+        String[] ls = dirFile.list(filter);
+        if (ls == null || ls.length == 0) {
+            return;
+        }
+        String[] fontNames = new String[ls.length];
+        String[][] nativeNames = new String[ls.length][];
+        int fontCount = 0;
+
+        for (int i=0; i < ls.length; i++ ) {
+            File theFile = new File(dirFile, ls[i]);
+            String fullName = null;
+            if (resolveSymLinks) {
+                try {
+                    fullName = theFile.getCanonicalPath();
+                } catch (IOException e) {
+                }
+            }
+            if (fullName == null) {
+                fullName = dirName + File.separator + ls[i];
+            }
+
+            // REMIND: case compare depends on platform
+            if (registeredFontFiles.contains(fullName)) {
+                continue;
+            }
+
+            if (badFonts != null && badFonts.contains(fullName)) {
+                if (FontUtilities.debugFonts()) {
+                    FontUtilities.getLogger()
+                                         .warning("skip bad font " + fullName);
+                }
+                continue; // skip this font file.
+            }
+
+            registeredFontFiles.add(fullName);
+
+            if (FontUtilities.debugFonts()
+                && FontUtilities.getLogger().isLoggable(Level.INFO)) {
+                String message = "Registering font " + fullName;
+                String[] natNames = getNativeNames(fullName, null);
+                if (natNames == null) {
+                    message += " with no native name";
+                } else {
+                    message += " with native name(s) " + natNames[0];
+                    for (int nn = 1; nn < natNames.length; nn++) {
+                        message += ", " + natNames[nn];
+                    }
+                }
+                FontUtilities.getLogger().info(message);
+            }
+            fontNames[fontCount] = fullName;
+            nativeNames[fontCount++] = getNativeNames(fullName, null);
+        }
+        registerFonts(fontNames, nativeNames, fontCount, fontFormat,
+                         useJavaRasterizer, fontRank, defer);
+        return;
+    }
+
+    protected String[] getNativeNames(String fontFileName,
+                                      String platformName) {
+        return null;
+    }
+
+    /**
+     * Returns a file name for the physical font represented by this platform
+     * font name. The default implementation tries to obtain the file name
+     * from the font configuration.
+     * Subclasses may override to provide information from other sources.
+     */
+    protected String getFileNameFromPlatformName(String platformFontName) {
+        return fontConfig.getFileNameFromPlatformName(platformFontName);
+    }
+
+    /**
+     * Return the default font configuration.
+     */
+    public FontConfiguration getFontConfiguration() {
+        return fontConfig;
+    }
+
+    /* A call to this method should be followed by a call to
+     * registerFontDirs(..)
+     */
+    protected String getPlatformFontPath(boolean noType1Font) {
+        if (fontPath == null) {
+            fontPath = getFontPath(noType1Font);
+        }
+        return fontPath;
+    }
+
+    public static boolean isOpenJDK() {
+        return FontUtilities.isOpenJDK;
+    }
+
+    protected void loadFonts() {
+        if (discoveredAllFonts) {
+            return;
+        }
+        /* Use lock specific to the font system */
+        synchronized (lucidaFontName) {
+            if (FontUtilities.debugFonts()) {
+                Thread.dumpStack();
+                FontUtilities.getLogger()
+                            .info("SunGraphicsEnvironment.loadFonts() called");
+            }
+            initialiseDeferredFonts();
+
+            java.security.AccessController.doPrivileged(
+                                    new java.security.PrivilegedAction() {
+                public Object run() {
+                    if (fontPath == null) {
+                        fontPath = getPlatformFontPath(noType1Font);
+                        registerFontDirs(fontPath);
+                    }
+                    if (fontPath != null) {
+                        // this will find all fonts including those already
+                        // registered. But we have checks in place to prevent
+                        // double registration.
+                        if (! gotFontsFromPlatform()) {
+                            registerFontsOnPath(fontPath, false,
+                                                Font2D.UNKNOWN_RANK,
+                                                false, true);
+                            loadedAllFontFiles = true;
+                        }
+                    }
+                    registerOtherFontFiles(registeredFontFiles);
+                    discoveredAllFonts = true;
+                    return null;
+                }
+            });
+        }
+    }
+
+    protected void registerFontDirs(String pathName) {
+        return;
+    }
+
+    private void registerFontsOnPath(String pathName,
+                                     boolean useJavaRasterizer, int fontRank,
+                                     boolean defer, boolean resolveSymLinks) {
+
+        StringTokenizer parser = new StringTokenizer(pathName,
+                File.pathSeparator);
+        try {
+            while (parser.hasMoreTokens()) {
+                registerFontsInDir(parser.nextToken(),
+                        useJavaRasterizer, fontRank,
+                        defer, resolveSymLinks);
+            }
+        } catch (NoSuchElementException e) {
+        }
+    }
+
+    /* Called to register fall back fonts */
+    public void registerFontsInDir(String dirName) {
+        registerFontsInDir(dirName, true, Font2D.JRE_RANK, true, false);
+    }
+
+    private void registerFontsInDir(String dirName, boolean useJavaRasterizer,
+                                    int fontRank,
+                                    boolean defer, boolean resolveSymLinks) {
+        File pathFile = new File(dirName);
+        addDirFonts(dirName, pathFile, ttFilter,
+                    FONTFORMAT_TRUETYPE, useJavaRasterizer,
+                    fontRank==Font2D.UNKNOWN_RANK ?
+                    Font2D.TTF_RANK : fontRank,
+                    defer, resolveSymLinks);
+        addDirFonts(dirName, pathFile, t1Filter,
+                    FONTFORMAT_TYPE1, useJavaRasterizer,
+                    fontRank==Font2D.UNKNOWN_RANK ?
+                    Font2D.TYPE1_RANK : fontRank,
+                    defer, resolveSymLinks);
+    }
+
+    protected void registerFontDir(String path) {
+    }
+
+    /**
+     * Returns file name for default font, either absolute
+     * or relative as needed by registerFontFile.
+     */
+    public synchronized String getDefaultFontFile() {
+        if (defaultFontFileName == null) {
+            initDefaultFonts();
+        }
+        return defaultFontFileName;
+    }
+
+    private void initDefaultFonts() {
+        if (!isOpenJDK()) {
+            defaultFontName = lucidaFontName;
+            if (useAbsoluteFontFileNames()) {
+                defaultFontFileName =
+                    jreFontDirName + File.separator + FontUtilities.LUCIDA_FILE_NAME;
+            } else {
+                defaultFontFileName = FontUtilities.LUCIDA_FILE_NAME;
+            }
+        }
+    }
+
+    /**
+     * Whether registerFontFile expects absolute or relative
+     * font file names.
+     */
+    protected boolean useAbsoluteFontFileNames() {
+        return true;
+    }
+
+    /**
+     * Creates this environment's FontConfiguration.
+     */
+    protected abstract FontConfiguration createFontConfiguration();
+
+    public abstract FontConfiguration
+    createFontConfiguration(boolean preferLocaleFonts,
+                            boolean preferPropFonts);
+
+    /**
+     * Returns face name for default font, or null if
+     * no face names are used for CompositeFontDescriptors
+     * for this platform.
+     */
+    public synchronized String getDefaultFontFaceName() {
+        if (defaultFontName == null) {
+            initDefaultFonts();
+        }
+        return defaultFontName;
+    }
+
+    public void loadFontFiles() {
+        loadFonts();
+        if (loadedAllFontFiles) {
+            return;
+        }
+        /* Use lock specific to the font system */
+        synchronized (lucidaFontName) {
+            if (FontUtilities.debugFonts()) {
+                Thread.dumpStack();
+                FontUtilities.getLogger().info("loadAllFontFiles() called");
+            }
+            java.security.AccessController.doPrivileged(
+                                    new java.security.PrivilegedAction() {
+                public Object run() {
+                    if (fontPath == null) {
+                        fontPath = getPlatformFontPath(noType1Font);
+                    }
+                    if (fontPath != null) {
+                        // this will find all fonts including those already
+                        // registered. But we have checks in place to prevent
+                        // double registration.
+                        registerFontsOnPath(fontPath, false,
+                                            Font2D.UNKNOWN_RANK,
+                                            false, true);
+                    }
+                    loadedAllFontFiles = true;
+                    return null;
+                }
+            });
+        }
+    }
+
+    /*
+     * This method asks the font configuration API for all platform names
+     * used as components of composite/logical fonts and iterates over these
+     * looking up their corresponding file name and registers these fonts.
+     * It also ensures that the fonts are accessible via platform APIs.
+     * The composites themselves are then registered.
+     */
+    private void
+        initCompositeFonts(FontConfiguration fontConfig,
+                           ConcurrentHashMap<String, Font2D>  altNameCache) {
+
+        int numCoreFonts = fontConfig.getNumberCoreFonts();
+        String[] fcFonts = fontConfig.getPlatformFontNames();
+        for (int f=0; f<fcFonts.length; f++) {
+            String platformFontName = fcFonts[f];
+            String fontFileName =
+                getFileNameFromPlatformName(platformFontName);
+            String[] nativeNames = null;
+            if (fontFileName == null
+                || fontFileName.equals(platformFontName)) {
+                /* No file located, so register using the platform name,
+                 * i.e. as a native font.
+                 */
+                fontFileName = platformFontName;
+            } else {
+                if (f < numCoreFonts) {
+                    /* If platform APIs also need to access the font, add it
+                     * to a set to be registered with the platform too.
+                     * This may be used to add the parent directory to the X11
+                     * font path if its not already there. See the docs for the
+                     * subclass implementation.
+                     * This is now mainly for the benefit of X11-based AWT
+                     * But for historical reasons, 2D initialisation code
+                     * makes these calls.
+                     * If the fontconfiguration file is properly set up
+                     * so that all fonts are mapped to files and all their
+                     * appropriate directories are specified, then this
+                     * method will be low cost as it will return after
+                     * a test that finds a null lookup map.
+                     */
+                    addFontToPlatformFontPath(platformFontName);
+                }
+                nativeNames = getNativeNames(fontFileName, platformFontName);
+            }
+            /* Uncomment these two lines to "generate" the XLFD->filename
+             * mappings needed to speed start-up on Solaris.
+             * Augment this with the appendedpathname and the mappings
+             * for native (F3) fonts
+             */
+            //String platName = platformFontName.replaceAll(" ", "_");
+            //System.out.println("filename."+platName+"="+fontFileName);
+            registerFontFile(fontFileName, nativeNames,
+                             Font2D.FONT_CONFIG_RANK, true);
+
+
+        }
+        /* This registers accumulated paths from the calls to
+         * addFontToPlatformFontPath(..) and any specified by
+         * the font configuration. Rather than registering
+         * the fonts it puts them in a place and form suitable for
+         * the Toolkit to pick up and use if a toolkit is initialised,
+         * and if it uses X11 fonts.
+         */
+        registerPlatformFontsUsedByFontConfiguration();
+
+        CompositeFontDescriptor[] compositeFontInfo
+                = fontConfig.get2DCompositeFontInfo();
+        for (int i = 0; i < compositeFontInfo.length; i++) {
+            CompositeFontDescriptor descriptor = compositeFontInfo[i];
+            String[] componentFileNames = descriptor.getComponentFileNames();
+            String[] componentFaceNames = descriptor.getComponentFaceNames();
+
+            /* It would be better eventually to handle this in the
+             * FontConfiguration code which should also remove duplicate slots
+             */
+            if (missingFontFiles != null) {
+                for (int ii=0; ii<componentFileNames.length; ii++) {
+                    if (missingFontFiles.contains(componentFileNames[ii])) {
+                        componentFileNames[ii] = getDefaultFontFile();
+                        componentFaceNames[ii] = getDefaultFontFaceName();
+                    }
+                }
+            }
+
+            /* FontConfiguration needs to convey how many fonts it has added
+             * as fallback component fonts which should not affect metrics.
+             * The core component count will be the number of metrics slots.
+             * This does not preclude other mechanisms for adding
+             * fall back component fonts to the composite.
+             */
+            if (altNameCache != null) {
+                SunFontManager.registerCompositeFont(
+                    descriptor.getFaceName(),
+                    componentFileNames, componentFaceNames,
+                    descriptor.getCoreComponentCount(),
+                    descriptor.getExclusionRanges(),
+                    descriptor.getExclusionRangeLimits(),
+                    true,
+                    altNameCache);
+            } else {
+                registerCompositeFont(descriptor.getFaceName(),
+                                      componentFileNames, componentFaceNames,
+                                      descriptor.getCoreComponentCount(),
+                                      descriptor.getExclusionRanges(),
+                                      descriptor.getExclusionRangeLimits(),
+                                      true);
+            }
+            if (FontUtilities.debugFonts()) {
+                FontUtilities.getLogger()
+                               .info("registered " + descriptor.getFaceName());
+            }
+        }
+    }
+
+    /**
+     * Notifies graphics environment that the logical font configuration
+     * uses the given platform font name. The graphics environment may
+     * use this for platform specific initialization.
+     */
+    protected void addFontToPlatformFontPath(String platformFontName) {
+    }
+
+    protected void registerFontFile(String fontFileName, String[] nativeNames,
+                                    int fontRank, boolean defer) {
+//      REMIND: case compare depends on platform
+        if (registeredFontFiles.contains(fontFileName)) {
+            return;
+        }
+        int fontFormat;
+        if (ttFilter.accept(null, fontFileName)) {
+            fontFormat = FONTFORMAT_TRUETYPE;
+        } else if (t1Filter.accept(null, fontFileName)) {
+            fontFormat = FONTFORMAT_TYPE1;
+        } else {
+            fontFormat = FONTFORMAT_NATIVE;
+        }
+        registeredFontFiles.add(fontFileName);
+        if (defer) {
+            registerDeferredFont(fontFileName, fontFileName, nativeNames,
+                                 fontFormat, false, fontRank);
+        } else {
+            registerFontFile(fontFileName, nativeNames, fontFormat, false,
+                             fontRank);
+        }
+    }
+
+    protected void registerPlatformFontsUsedByFontConfiguration() {
+    }
+
+    /*
+     * A GE may verify whether a font file used in a fontconfiguration
+     * exists. If it doesn't then either we may substitute the default
+     * font, or perhaps elide it altogether from the composite font.
+     * This makes some sense on windows where the font file is only
+     * likely to be in one place. But on other OSes, eg Linux, the file
+     * can move around depending. So there we probably don't want to assume
+     * its missing and so won't add it to this list.
+     * If this list - missingFontFiles - is non-null then the composite
+     * font initialisation logic tests to see if a font file is in that
+     * set.
+     * Only one thread should be able to add to this set so we don't
+     * synchronize.
+     */
+    protected void addToMissingFontFileList(String fileName) {
+        if (missingFontFiles == null) {
+            missingFontFiles = new HashSet<String>();
+        }
+        missingFontFiles.add(fileName);
+    }
+
+    /*
+     * This is for use only within getAllFonts().
+     * Fonts listed in the fontconfig files for windows were all
+     * on the "deferred" initialisation list. They were registered
+     * either in the course of the application, or in the call to
+     * loadFonts() within getAllFonts(). The fontconfig file specifies
+     * the names of the fonts using the English names. If there's a
+     * different name in the execution locale, then the platform will
+     * report that, and we will construct the font with both names, and
+     * thereby enumerate it twice. This happens for Japanese fonts listed
+     * in the windows fontconfig, when run in the JA locale. The solution
+     * is to rely (in this case) on the platform's font->file mapping to
+     * determine that this name corresponds to a file we already registered.
+     * This works because
+     * - we know when we get here all deferred fonts are already initialised
+     * - when we register a font file, we register all fonts in it.
+     * - we know the fontconfig fonts are all in the windows registry
+     */
+    private boolean isNameForRegisteredFile(String fontName) {
+        String fileName = getFileNameForFontName(fontName);
+        if (fileName == null) {
+            return false;
+        }
+        return registeredFontFiles.contains(fileName);
+    }
+
+    /*
+     * This invocation is not in a privileged block because
+     * all privileged operations (reading files and properties)
+     * was conducted on the creation of the GE
+     */
+    public void
+        createCompositeFonts(ConcurrentHashMap<String, Font2D> altNameCache,
+                             boolean preferLocale,
+                             boolean preferProportional) {
+
+        FontConfiguration fontConfig =
+            createFontConfiguration(preferLocale, preferProportional);
+        initCompositeFonts(fontConfig, altNameCache);
+    }
+
+    /**
+     * Returns all fonts installed in this environment.
+     */
+    public Font[] getAllInstalledFonts() {
+        if (allFonts == null) {
+            loadFonts();
+            TreeMap fontMapNames = new TreeMap();
+            /* warning: the number of composite fonts could change dynamically
+             * if applications are allowed to create them. "allfonts" could
+             * then be stale.
+             */
+            Font2D[] allfonts = getRegisteredFonts();
+            for (int i=0; i < allfonts.length; i++) {
+                if (!(allfonts[i] instanceof NativeFont)) {
+                    fontMapNames.put(allfonts[i].getFontName(null),
+                                     allfonts[i]);
+                }
+            }
+
+            String[] platformNames = getFontNamesFromPlatform();
+            if (platformNames != null) {
+                for (int i=0; i<platformNames.length; i++) {
+                    if (!isNameForRegisteredFile(platformNames[i])) {
+                        fontMapNames.put(platformNames[i], null);
+                    }
+                }
+            }
+
+            String[] fontNames = null;
+            if (fontMapNames.size() > 0) {
+                fontNames = new String[fontMapNames.size()];
+                Object [] keyNames = fontMapNames.keySet().toArray();
+                for (int i=0; i < keyNames.length; i++) {
+                    fontNames[i] = (String)keyNames[i];
+                }
+            }
+            Font[] fonts = new Font[fontNames.length];
+            for (int i=0; i < fontNames.length; i++) {
+                fonts[i] = new Font(fontNames[i], Font.PLAIN, 1);
+                Font2D f2d = (Font2D)fontMapNames.get(fontNames[i]);
+                if (f2d  != null) {
+                    FontAccess.getFontAccess().setFont2D(fonts[i], f2d.handle);
+                }
+            }
+            allFonts = fonts;
+        }
+
+        Font []copyFonts = new Font[allFonts.length];
+        System.arraycopy(allFonts, 0, copyFonts, 0, allFonts.length);
+        return copyFonts;
+    }
+
+    /**
+     * Get a list of installed fonts in the requested {@link Locale}.
+     * The list contains the fonts Family Names.
+     * If Locale is null, the default locale is used.
+     *
+     * @param requestedLocale, if null the default locale is used.
+     * @return list of installed fonts in the system.
+     */
+    public String[] getInstalledFontFamilyNames(Locale requestedLocale) {
+        if (requestedLocale == null) {
+            requestedLocale = Locale.getDefault();
+        }
+        if (allFamilies != null && lastDefaultLocale != null &&
+            requestedLocale.equals(lastDefaultLocale)) {
+                String[] copyFamilies = new String[allFamilies.length];
+                System.arraycopy(allFamilies, 0, copyFamilies,
+                                 0, allFamilies.length);
+                return copyFamilies;
+        }
+
+        TreeMap<String,String> familyNames = new TreeMap<String,String>();
+        //  these names are always there and aren't localised
+        String str;
+        str = Font.SERIF;         familyNames.put(str.toLowerCase(), str);
+        str = Font.SANS_SERIF;    familyNames.put(str.toLowerCase(), str);
+        str = Font.MONOSPACED;    familyNames.put(str.toLowerCase(), str);
+        str = Font.DIALOG;        familyNames.put(str.toLowerCase(), str);
+        str = Font.DIALOG_INPUT;  familyNames.put(str.toLowerCase(), str);
+
+        /* Platform APIs may be used to get the set of available family
+         * names for the current default locale so long as it is the same
+         * as the start-up system locale, rather than loading all fonts.
+         */
+        if (requestedLocale.equals(getSystemStartupLocale()) &&
+            getFamilyNamesFromPlatform(familyNames, requestedLocale)) {
+            /* Augment platform names with JRE font family names */
+            getJREFontFamilyNames(familyNames, requestedLocale);
+        } else {
+            loadFontFiles();
+            Font2D[] physicalfonts = getPhysicalFonts();
+            for (int i=0; i < physicalfonts.length; i++) {
+                if (!(physicalfonts[i] instanceof NativeFont)) {
+                    String name =
+                        physicalfonts[i].getFamilyName(requestedLocale);
+                    familyNames.put(name.toLowerCase(requestedLocale), name);
+                }
+            }
+        }
+
+        String[] retval =  new String[familyNames.size()];
+        Object [] keyNames = familyNames.keySet().toArray();
+        for (int i=0; i < keyNames.length; i++) {
+            retval[i] = (String)familyNames.get(keyNames[i]);
+        }
+        if (requestedLocale.equals(Locale.getDefault())) {
+            lastDefaultLocale = requestedLocale;
+            allFamilies = new String[retval.length];
+            System.arraycopy(retval, 0, allFamilies, 0, allFamilies.length);
+        }
+        return retval;
+    }
+
+    public void register1dot0Fonts() {
+        java.security.AccessController.doPrivileged(
+                            new java.security.PrivilegedAction() {
+            public Object run() {
+                String type1Dir = "/usr/openwin/lib/X11/fonts/Type1";
+                registerFontsInDir(type1Dir, true, Font2D.TYPE1_RANK,
+                                   false, false);
+                return null;
+            }
+        });
+    }
+
+    /* Really we need only the JRE fonts family names, but there's little
+     * overhead in doing this the easy way by adding all the currently
+     * known fonts.
+     */
+    protected void getJREFontFamilyNames(TreeMap<String,String> familyNames,
+                                         Locale requestedLocale) {
+        registerDeferredJREFonts(jreFontDirName);
+        Font2D[] physicalfonts = getPhysicalFonts();
+        for (int i=0; i < physicalfonts.length; i++) {
+            if (!(physicalfonts[i] instanceof NativeFont)) {
+                String name =
+                    physicalfonts[i].getFamilyName(requestedLocale);
+                familyNames.put(name.toLowerCase(requestedLocale), name);
+            }
+        }
+    }
+
+    /**
+     * Default locale can be changed but we need to know the initial locale
+     * as that is what is used by native code. Changing Java default locale
+     * doesn't affect that.
+     * Returns the locale in use when using native code to communicate
+     * with platform APIs. On windows this is known as the "system" locale,
+     * and it is usually the same as the platform locale, but not always,
+     * so this method also checks an implementation property used only
+     * on windows and uses that if set.
+     */
+    private static Locale systemLocale = null;
+    private static Locale getSystemStartupLocale() {
+        if (systemLocale == null) {
+            systemLocale = (Locale)
+                java.security.AccessController.doPrivileged(
+                                    new java.security.PrivilegedAction() {
+            public Object run() {
+                /* On windows the system locale may be different than the
+                 * user locale. This is an unsupported configuration, but
+                 * in that case we want to return a dummy locale that will
+                 * never cause a match in the usage of this API. This is
+                 * important because Windows documents that the family
+                 * names of fonts are enumerated using the language of
+                 * the system locale. BY returning a dummy locale in that
+                 * case we do not use the platform API which would not
+                 * return us the names we want.
+                 */
+                String fileEncoding = System.getProperty("file.encoding", "");
+                String sysEncoding = System.getProperty("sun.jnu.encoding");
+                if (sysEncoding != null && !sysEncoding.equals(fileEncoding)) {
+                    return Locale.ROOT;
+                }
+
+                String language = System.getProperty("user.language", "en");
+                String country  = System.getProperty("user.country","");
+                String variant  = System.getProperty("user.variant","");
+                return new Locale(language, country, variant);
+            }
+        });
+        }
+        return systemLocale;
+    }
+
+    void addToPool(FileFont font) {
+
+        FileFont fontFileToClose = null;
+        int freeSlot = -1;
+
+        synchronized (fontFileCache) {
+            /* Avoid duplicate entries in the pool, and don't close() it,
+             * since this method is called only from within open().
+             * Seeing a duplicate is most likely to happen if the thread
+             * was interrupted during a read, forcing perhaps repeated
+             * close and open calls and it eventually it ends up pointing
+             * at the same slot.
+             */
+            for (int i=0;i<CHANNELPOOLSIZE;i++) {
+                if (fontFileCache[i] == font) {
+                    return;
+                }
+                if (fontFileCache[i] == null && freeSlot < 0) {
+                    freeSlot = i;
+                }
+            }
+            if (freeSlot >= 0) {
+                fontFileCache[freeSlot] = font;
+                return;
+            } else {
+                /* replace with new font. */
+                fontFileToClose = fontFileCache[lastPoolIndex];
+                fontFileCache[lastPoolIndex] = font;
+                /* lastPoolIndex is updated so that the least recently opened
+                 * file will be closed next.
+                 */
+                lastPoolIndex = (lastPoolIndex+1) % CHANNELPOOLSIZE;
+            }
+        }
+        /* Need to close the font file outside of the synchronized block,
+         * since its possible some other thread is in an open() call on
+         * this font file, and could be holding its lock and the pool lock.
+         * Releasing the pool lock allows that thread to continue, so it can
+         * then release the lock on this font, allowing the close() call
+         * below to proceed.
+         * Also, calling close() is safe because any other thread using
+         * the font we are closing() synchronizes all reading, so we
+         * will not close the file while its in use.
+         */
+        if (fontFileToClose != null) {
+            fontFileToClose.close();
+        }
+    }
+
+    protected FontUIResource getFontConfigFUIR(String family, int style,
+                                               int size)
+    {
+        return new FontUIResource(family, style, size);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/awt/X11FontManager.java	Fri Aug 07 19:36:28 2009 +0200
@@ -0,0 +1,850 @@
+package sun.awt;
+
+import java.awt.GraphicsEnvironment;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.StreamTokenizer;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import java.util.logging.Logger;
+
+import javax.swing.plaf.FontUIResource;
+import sun.awt.motif.MFontConfiguration;
+import sun.font.CompositeFont;
+import sun.font.FontManager;
+import sun.font.SunFontManager;
+import sun.font.FontConfigManager;
+import sun.font.FcFontConfiguration;
+import sun.font.FontAccess;
+import sun.font.FontUtilities;
+import sun.font.NativeFont;
+
+/**
+ * The X11 implementation of {@link FontManager}.
+ */
+public class X11FontManager extends SunFontManager {
+
+    // constants identifying XLFD and font ID fields
+    private static final int FOUNDRY_FIELD = 1;
+    private static final int FAMILY_NAME_FIELD = 2;
+    private static final int WEIGHT_NAME_FIELD = 3;
+    private static final int SLANT_FIELD = 4;
+    private static final int SETWIDTH_NAME_FIELD = 5;
+    private static final int ADD_STYLE_NAME_FIELD = 6;
+    private static final int PIXEL_SIZE_FIELD = 7;
+    private static final int POINT_SIZE_FIELD = 8;
+    private static final int RESOLUTION_X_FIELD = 9;
+    private static final int RESOLUTION_Y_FIELD = 10;
+    private static final int SPACING_FIELD = 11;
+    private static final int AVERAGE_WIDTH_FIELD = 12;
+    private static final int CHARSET_REGISTRY_FIELD = 13;
+    private static final int CHARSET_ENCODING_FIELD = 14;
+
+    /*
+     * fontNameMap is a map from a fontID (which is a substring of an XLFD like
+     * "-monotype-arial-bold-r-normal-iso8859-7")
+     * to font file path like
+     * /usr/openwin/lib/locale/iso_8859_7/X11/fonts/TrueType/ArialBoldItalic.ttf
+     * It's used in a couple of methods like
+     * getFileNameFomPlatformName(..) to help locate the font file.
+     * We use this substring of a full XLFD because the font configuration files
+     * define the XLFDs in a way that's easier to make into a request.
+     * E.g., the -0-0-0-0-p-0- reported by X is -*-%d-*-*-p-*- in the font
+     * configuration files. We need to remove that part for comparisons.
+     */
+    private static Map fontNameMap = new HashMap();
+
+    /*
+     * xlfdMap is a map from a platform path like
+     * /usr/openwin/lib/locale/ja/X11/fonts/TT/HG-GothicB.ttf to an XLFD like
+     * "-ricoh-hg gothic b-medium-r-normal--0-0-0-0-m-0-jisx0201.1976-0"
+     * Because there may be multiple native names, because the font is used
+     * to support multiple X encodings for example, the value of an entry in
+     * this map is always a vector where we store all the native names.
+     * For fonts which we don't understand the key isn't a pathname, its
+     * the full XLFD string like :-
+     * "-ricoh-hg gothic b-medium-r-normal--0-0-0-0-m-0-jisx0201.1976-0"
+     */
+    private static Map xlfdMap = new HashMap();
+
+    /* xFontDirsMap is also a map from a font ID to a font filepath.
+     * The difference from fontNameMap is just that it does not have
+     * resolved symbolic links. Normally this is not interesting except
+     * that we need to know the directory in which a font was found to
+     * add it to the X font server path, since although the files may
+     * be linked, the fonts.dir is different and specific to the encoding
+     * handled by that directory. This map is nulled out after use to free
+     * heap space. If the optimal path is taken, such that all fonts in
+     * font configuration files are referenced by filename, then the font
+     * dir can be directly derived as its parent directory.
+     * If a font is used by two XLFDs, each corresponding to a different
+     * X11 font directory, then precautions must be taken to include both
+     * directories.
+     */
+     private static Map xFontDirsMap;
+
+     /*
+      * This is the set of font directories needed to be on the X font path
+      * to enable AWT heavyweights to find all of the font configuration fonts.
+      * It is populated by :
+      * - awtfontpath entries in the fontconfig.properties
+      * - parent directories of "core" fonts used in the fontconfig.properties
+      * - looking up font dirs in the xFontDirsMap where the key is a fontID
+      *   (cut down version of the XLFD read from the font configuration file).
+      * This set is nulled out after use to free heap space.
+      */
+     private static HashSet<String> fontConfigDirs = null;
+
+    /* These maps are used on Linux where we reference the Lucida oblique
+     * fonts in fontconfig files even though they aren't in the standard
+     * font directory. This explicitly remaps the XLFDs for these to the
+     * correct base font. This is needed to prevent composite fonts from
+     * defaulting to the Lucida Sans which is a bad substitute for the
+     * monospaced Lucida Sans Typewriter. Also these maps prevent the
+     * JRE from doing wasted work at start up.
+     */
+    HashMap<String, String> oblmap = null;
+
+
+    /*
+     * Used to eliminate redundant work. When a font directory is
+     * registered it added to this list. Subsequent registrations for the
+     * same directory can then be skipped by checking this Map.
+     * Access to this map is not synchronised here since creation
+     * of the singleton GE instance is already synchronised and that is
+     * the only code path that accesses this map.
+     */
+     private static HashMap registeredDirs = new HashMap();
+
+     /* Array of directories to be added to the X11 font path.
+      * Used by static method called from Toolkits which use X11 fonts.
+      * Specifically this means MToolkit
+      */
+     private static String[] fontdirs = null;
+
+    private static String[] defaultPlatformFont = null;
+
+    private FontConfigManager fcManager = null;
+
+    public static X11FontManager getInstance() {
+        return (X11FontManager) SunFontManager.getInstance();
+    }
+
+    /**
+     * Takes family name property in the following format:
+     * "-linotype-helvetica-medium-r-normal-sans-*-%d-*-*-p-*-iso8859-1"
+     * and returns the name of the corresponding physical font.
+     * This code is used to resolve font configuration fonts, and expects
+     * only to get called for these fonts.
+     */
+    @Override
+    public String getFileNameFromPlatformName(String platName) {
+
+        /* If the FontConfig file doesn't use xlfds, or its
+         * FcFontConfiguration, this may be already a file name.
+         */
+        if (platName.startsWith("/")) {
+            return platName;
+        }
+
+        String fileName = null;
+        String fontID = specificFontIDForName(platName);
+
+        /* If the font filename has been explicitly assigned in the
+         * font configuration file, use it. This avoids accessing
+         * the wrong fonts on Linux, where different fonts (some
+         * of which may not be usable by 2D) may share the same
+         * specific font ID. It may also speed up the lookup.
+         */
+        fileName = super.getFileNameFromPlatformName(platName);
+        if (fileName != null) {
+            if (isHeadless() && fileName.startsWith("-")) {
+                /* if it's headless, no xlfd should be used */
+                    return null;
+            }
+            if (fileName.startsWith("/")) {
+                /* If a path is assigned in the font configuration file,
+                 * it is required that the config file also specify using the
+                 * new awtfontpath key the X11 font directories
+                 * which must be added to the X11 font path to support
+                 * AWT access to that font. For that reason we no longer
+                 * have code here to add the parent directory to the list
+                 * of font config dirs, since the parent directory may not
+                 * be sufficient if fonts are symbolically linked to a
+                 * different directory.
+                 *
+                 * Add this XLFD (platform name) to the list of known
+                 * ones for this file.
+                 */
+                Vector xVal = (Vector) xlfdMap.get(fileName);
+                if (xVal == null) {
+                    /* Try to be robust on Linux distros which move fonts
+                     * around by verifying that the fileName represents a
+                     * file that exists.  If it doesn't, set it to null
+                     * to trigger a search.
+                     */
+                    if (getFontConfiguration().needToSearchForFile(fileName)) {
+                        fileName = null;
+                    }
+                    if (fileName != null) {
+                        xVal = new Vector();
+                        xVal.add(platName);
+                        xlfdMap.put(fileName, xVal);
+                    }
+                } else {
+                    if (!xVal.contains(platName)) {
+                        xVal.add(platName);
+                    }
+                }
+            }
+            if (fileName != null) {
+                fontNameMap.put(fontID, fileName);
+                return fileName;
+            }
+        }
+
+        if (fontID != null) {
+            fileName = (String)fontNameMap.get(fontID);
+            /* On Linux check for the Lucida Oblique fonts */
+            if (fileName == null && FontUtilities.isLinux && !isOpenJDK()) {
+                if (oblmap == null) {
+                    initObliqueLucidaFontMap();
+                }
+                String oblkey = getObliqueLucidaFontID(fontID);
+                if (oblkey != null) {
+                    fileName = oblmap.get(oblkey);
+                }
+            }
+            if (fontPath == null &&
+                (fileName == null || !fileName.startsWith("/"))) {
+                if (FontUtilities.debugFonts()) {
+                    FontUtilities.getLogger()
+                          .warning("** Registering all font paths because " +
+                                   "can't find file for " + platName);
+                }
+                fontPath = getPlatformFontPath(noType1Font);
+                registerFontDirs(fontPath);
+                if (FontUtilities.debugFonts()) {
+                    FontUtilities.getLogger()
+                            .warning("** Finished registering all font paths");
+                }
+                fileName = (String)fontNameMap.get(fontID);
+            }
+            if (fileName == null && !isHeadless()) {
+                /* Query X11 directly to see if this font is available
+                 * as a native font.
+                 */
+                fileName = getX11FontName(platName);
+            }
+            if (fileName == null) {
+                fontID = switchFontIDForName(platName);
+                fileName = (String)fontNameMap.get(fontID);
+            }
+            if (fileName != null) {
+                fontNameMap.put(fontID, fileName);
+            }
+        }
+        return fileName;
+    }
+
+    @Override
+    protected String[] getNativeNames(String fontFileName,
+            String platformName) {
+        Vector nativeNames;
+        if ((nativeNames=(Vector)xlfdMap.get(fontFileName))==null) {
+            if (platformName == null) {
+                return null;
+            } else {
+                /* back-stop so that at least the name used in the
+                 * font configuration file is known as a native name
+                 */
+                String []natNames = new String[1];
+                natNames[0] = platformName;
+                return natNames;
+            }
+        } else {
+            int len = nativeNames.size();
+            return (String[])nativeNames.toArray(new String[len]);
+        }
+    }
+
+    /* NOTE: this method needs to be executed in a privileged context.
+     * The superclass constructor which is the primary caller of
+     * this method executes entirely in such a context. Additionally
+     * the loadFonts() method does too. So all should be well.
+
+     */
+    @Override
+    protected void registerFontDir(String path) {
+        /* fonts.dir file format looks like :-
+         * 47
+         * Arial.ttf -monotype-arial-regular-r-normal--0-0-0-0-p-0-iso8859-1
+         * Arial-Bold.ttf -monotype-arial-bold-r-normal--0-0-0-0-p-0-iso8859-1
+         * ...
+         */
+        if (FontUtilities.debugFonts()) {
+            FontUtilities.getLogger().info("ParseFontDir " + path);
+        }
+        File fontsDotDir = new File(path + File.separator + "fonts.dir");
+        FileReader fr = null;
+        try {
+            if (fontsDotDir.canRead()) {
+                fr = new FileReader(fontsDotDir);
+                BufferedReader br = new BufferedReader(fr, 8192);
+                StreamTokenizer st = new StreamTokenizer(br);
+                st.eolIsSignificant(true);
+                int ttype = st.nextToken();
+                if (ttype == StreamTokenizer.TT_NUMBER) {
+                    int numEntries = (int)st.nval;
+                    ttype = st.nextToken();
+                    if (ttype == StreamTokenizer.TT_EOL) {
+                        st.resetSyntax();
+                        st.wordChars(32, 127);
+                        st.wordChars(128 + 32, 255);
+                        st.whitespaceChars(0, 31);
+
+                        for (int i=0; i < numEntries; i++) {
+                            ttype = st.nextToken();
+                            if (ttype == StreamTokenizer.TT_EOF) {
+                                break;
+                            }
+                            if (ttype != StreamTokenizer.TT_WORD) {
+                                break;
+                            }
+                            int breakPos = st.sval.indexOf(' ');
+                            if (breakPos <= 0) {
+                                /* On TurboLinux 8.0 a fonts.dir file had
+                                 * a line with integer value "24" which
+                                 * appeared to be the number of remaining
+                                 * entries in the file. This didn't add to
+                                 * the value on the first line of the file.
+                                 * Seemed like XFree86 didn't like this line
+                                 * much either. It failed to parse the file.
+                                 * Ignore lines like this completely, and
+                                 * don't let them count as an entry.
+                                 */
+                                numEntries++;
+                                ttype = st.nextToken();
+                                if (ttype != StreamTokenizer.TT_EOL) {
+                                    break;
+                                }
+
+                                continue;
+                            }
+                            if (st.sval.charAt(0) == '!') {
+                                /* TurboLinux 8.0 comment line: ignore.
+                                 * can't use st.commentChar('!') to just
+                                 * skip because this line mustn't count
+                                 * against numEntries.
+                                 */
+                                numEntries++;
+                                ttype = st.nextToken();
+                                if (ttype != StreamTokenizer.TT_EOL) {
+                                    break;
+                                }
+                                continue;
+                            }
+                            String fileName = st.sval.substring(0, breakPos);
+                            /* TurboLinux 8.0 uses some additional syntax to
+                             * indicate algorithmic styling values.
+                             * Ignore ':' separated files at the beginning
+                             * of the fileName
+                             */
+                            int lastColon = fileName.lastIndexOf(':');
+                            if (lastColon > 0) {
+                                if (lastColon+1 >= fileName.length()) {
+                                    continue;
+                                }
+                                fileName = fileName.substring(lastColon+1);
+                            }
+                            String fontPart = st.sval.substring(breakPos+1);
+                            String fontID = specificFontIDForName(fontPart);
+                            String sVal = (String) fontNameMap.get(fontID);
+
+                            if (FontUtilities.debugFonts()) {
+                                Logger logger = FontUtilities.getLogger();
+                                logger.info("file=" + fileName +
+                                            " xlfd=" + fontPart);
+                                logger.info("fontID=" + fontID +
+                                            " sVal=" + sVal);
+                            }
+                            String fullPath = null;
+                            try {
+                                File file = new File(path,fileName);
+                                /* we may have a resolved symbolic link
+                                 * this becomes important for an xlfd we
+                                 * still need to know the location it was
+                                 * found to update the X server font path
+                                 * for use by AWT heavyweights - and when 2D
+                                 * wants to use the native rasteriser.
+                                 */
+                                if (xFontDirsMap == null) {
+                                    xFontDirsMap = new HashMap();
+                                }
+                                xFontDirsMap.put(fontID, path);
+                                fullPath = file.getCanonicalPath();
+                            } catch (IOException e) {
+                                fullPath = path + File.separator + fileName;
+                            }
+                            Vector xVal = (Vector) xlfdMap.get(fullPath);
+                            if (FontUtilities.debugFonts()) {
+                                FontUtilities.getLogger()
+                                      .info("fullPath=" + fullPath +
+                                            " xVal=" + xVal);
+                            }
+                            if ((xVal == null || !xVal.contains(fontPart)) &&
+                                (sVal == null) || !sVal.startsWith("/")) {
+                                if (FontUtilities.debugFonts()) {
+                                    FontUtilities.getLogger()
+                                          .info("Map fontID:"+fontID +
+                                                "to file:" + fullPath);
+                                }
+                                fontNameMap.put(fontID, fullPath);
+                                if (xVal == null) {
+                                    xVal = new Vector();
+                                    xlfdMap.put (fullPath, xVal);
+                                }
+                                xVal.add(fontPart);
+                            }
+
+                            ttype = st.nextToken();
+                            if (ttype != StreamTokenizer.TT_EOL) {
+                                break;
+                            }
+                        }
+                    }
+                }
+                fr.close();
+            }
+        } catch (IOException ioe1) {
+        } finally {
+            if (fr != null) {
+                try {
+                    fr.close();
+                }  catch (IOException ioe2) {
+                }
+            }
+        }
+    }
+
+    @Override
+    public void loadFonts() {
+        super.loadFonts();
+        /* These maps are greatly expanded during a loadFonts but
+         * can be reset to their initial state afterwards.
+         * Since preferLocaleFonts() and preferProportionalFonts() will
+         * trigger a partial repopulating from the FontConfiguration
+         * it has to be the inital (empty) state for the latter two, not
+         * simply nulling out.
+         * xFontDirsMap is a special case in that the implementation
+         * will typically not ever need to initialise it so it can be null.
+         */
+        xFontDirsMap = null;
+        xlfdMap = new HashMap(1);
+        fontNameMap = new HashMap(1);
+    }
+
+    private String getObliqueLucidaFontID(String fontID) {
+        if (fontID.startsWith("-lucidasans-medium-i-normal") ||
+            fontID.startsWith("-lucidasans-bold-i-normal") ||
+            fontID.startsWith("-lucidatypewriter-medium-i-normal") ||
+            fontID.startsWith("-lucidatypewriter-bold-i-normal")) {
+            return fontID.substring(0, fontID.indexOf("-i-"));
+        } else {
+            return null;
+        }
+    }
+
+    private static String getX11FontName(String platName) {
+        String xlfd = platName.replaceAll("%d", "*");
+        if (NativeFont.fontExists(xlfd)) {
+            return xlfd;
+        } else {
+            return null;
+        }
+    }
+
+    private void initObliqueLucidaFontMap() {
+        oblmap = new HashMap<String, String>();
+        oblmap.put("-lucidasans-medium",
+                   jreLibDirName+"/fonts/LucidaSansRegular.ttf");
+        oblmap.put("-lucidasans-bold",
+                   jreLibDirName+"/fonts/LucidaSansDemiBold.ttf");
+        oblmap.put("-lucidatypewriter-medium",
+                   jreLibDirName+"/fonts/LucidaTypewriterRegular.ttf");
+        oblmap.put("-lucidatypewriter-bold",
+                   jreLibDirName+"/fonts/LucidaTypewriterBold.ttf");
+    }
+
+    private boolean isHeadless() {
+        GraphicsEnvironment ge =
+            GraphicsEnvironment.getLocalGraphicsEnvironment();
+        return GraphicsEnvironment.isHeadless();
+    }
+
+    private String specificFontIDForName(String name) {
+
+        int[] hPos = new int[14];
+        int hyphenCnt = 1;
+        int pos = 1;
+
+        while (pos != -1 && hyphenCnt < 14) {
+            pos = name.indexOf('-', pos);
+            if (pos != -1) {
+                hPos[hyphenCnt++] = pos;
+                    pos++;
+            }
+        }
+
+        if (hyphenCnt != 14) {
+            if (FontUtilities.debugFonts()) {
+                FontUtilities.getLogger()
+                    .severe("Font Configuration Font ID is malformed:" + name);
+            }
+            return name; // what else can we do?
+        }
+
+        StringBuffer sb =
+            new StringBuffer(name.substring(hPos[FAMILY_NAME_FIELD-1],
+                                            hPos[SETWIDTH_NAME_FIELD]));
+        sb.append(name.substring(hPos[CHARSET_REGISTRY_FIELD-1]));
+        String retval = sb.toString().toLowerCase (Locale.ENGLISH);
+        return retval;
+    }
+
+    private String switchFontIDForName(String name) {
+
+        int[] hPos = new int[14];
+        int hyphenCnt = 1;
+        int pos = 1;
+
+        while (pos != -1 && hyphenCnt < 14) {
+            pos = name.indexOf('-', pos);
+            if (pos != -1) {
+                hPos[hyphenCnt++] = pos;
+                    pos++;
+            }
+        }
+
+        if (hyphenCnt != 14) {
+            if (FontUtilities.debugFonts()) {
+                FontUtilities.getLogger()
+                    .severe("Font Configuration Font ID is malformed:" + name);
+            }
+            return name; // what else can we do?
+        }
+
+        String slant = name.substring(hPos[SLANT_FIELD-1]+1,
+                                           hPos[SLANT_FIELD]);
+        String family = name.substring(hPos[FAMILY_NAME_FIELD-1]+1,
+                                           hPos[FAMILY_NAME_FIELD]);
+        String registry = name.substring(hPos[CHARSET_REGISTRY_FIELD-1]+1,
+                                           hPos[CHARSET_REGISTRY_FIELD]);
+        String encoding = name.substring(hPos[CHARSET_ENCODING_FIELD-1]+1);
+
+        if (slant.equals("i")) {
+            slant = "o";
+        } else if (slant.equals("o")) {
+            slant = "i";
+        }
+        // workaround for #4471000
+        if (family.equals("itc zapfdingbats")
+            && registry.equals("sun")
+            && encoding.equals("fontspecific")){
+            registry = "adobe";
+        }
+        StringBuffer sb =
+            new StringBuffer(name.substring(hPos[FAMILY_NAME_FIELD-1],
+                                            hPos[SLANT_FIELD-1]+1));
+        sb.append(slant);
+        sb.append(name.substring(hPos[SLANT_FIELD],
+                                 hPos[SETWIDTH_NAME_FIELD]+1));
+        sb.append(registry);
+        sb.append(name.substring(hPos[CHARSET_ENCODING_FIELD-1]));
+        String retval = sb.toString().toLowerCase (Locale.ENGLISH);
+        return retval;
+    }
+
+    /**
+     * Returns the face name for the given XLFD.
+     */
+    public String getFileNameFromXLFD(String name) {
+        String fileName = null;
+        String fontID = specificFontIDForName(name);
+        if (fontID != null) {
+            fileName = (String)fontNameMap.get(fontID);
+            if (fileName == null) {
+                fontID = switchFontIDForName(name);
+                fileName = (String)fontNameMap.get(fontID);
+            }
+            if (fileName == null) {
+                fileName = getDefaultFontFile();
+            }
+        }
+        return fileName;
+    }
+
+    /* Register just the paths, (it doesn't register the fonts).
+     * If a font configuration file has specified a baseFontPath
+     * fontPath is just those directories, unless on usage we
+     * find it doesn't contain what we need for the logical fonts.
+     * Otherwise, we register all the paths on Solaris, because
+     * the fontPath we have here is the complete one from
+     * parsing /var/sadm/install/contents, not just
+     * what's on the X font path (may be this should be
+     * changed).
+     * But for now what it means is that if we didn't do
+     * this then if the font weren't listed anywhere on the
+     * less complete font path we'd trigger loadFonts which
+     * actually registers the fonts. This may actually be
+     * the right thing tho' since that would also set up
+     * the X font path without which we wouldn't be able to
+     * display some "native" fonts.
+     * So something to revisit is that probably fontPath
+     * here ought to be only the X font path + jre font dir.
+     * loadFonts should have a separate native call to
+     * get the rest of the platform font path.
+     *
+     * Registering the directories can now be avoided in the
+     * font configuration initialisation when filename entries
+     * exist in the font configuration file for all fonts.
+     * (Perhaps a little confusingly a filename entry is
+     * actually keyed using the XLFD used in the font entries,
+     * and it maps *to* a real filename).
+     * In the event any are missing, registration of all
+     * directories will be invoked to find the real files.
+     *
+     * But registering the directory performed other
+     * functions such as filling in the map of all native names
+     * for the font. So when this method isn't invoked, they still
+     * must be found. This is mitigated by getNativeNames now
+     * being able to return at least the platform name, but mostly
+     * by ensuring that when a filename key is found, that
+     * xlfd key is stored as one of the set of platform names
+     * for the font. Its a set because typical font configuration
+     * files reference the same CJK font files using multiple
+     * X11 encodings. For the code that adds this to the map
+     * see X11GE.getFileNameFromPlatformName(..)
+     * If you don't get all of these then some code points may
+     * not use the Xserver, and will not get the PCF bitmaps
+     * that are available for some point sizes.
+     * So, in the event that there is such a problem,
+     * unconditionally making this call may be necessary, at
+     * some cost to JRE start-up
+     */
+    @Override
+    protected void registerFontDirs(String pathName) {
+
+        StringTokenizer parser = new StringTokenizer(pathName,
+                                                     File.pathSeparator);
+        try {
+            while (parser.hasMoreTokens()) {
+                String dirPath = parser.nextToken();
+                if (dirPath != null && !registeredDirs.containsKey(dirPath)) {
+                    registeredDirs.put(dirPath, null);
+                    registerFontDir(dirPath);
+                }
+            }
+        } catch (NoSuchElementException e) {
+        }
+    }
+
+    // An X font spec (xlfd) includes an encoding. The same TrueType font file
+    // may be referenced from different X font directories in font.dir files
+    // to support use in multiple encodings by X apps.
+    // So for the purposes of font configuration logical fonts where AWT
+    // heavyweights need to access the font via X APIs we need to ensure that
+    // the directory for precisely the encodings needed by this are added to
+    // the x font path. This requires that we note the platform names
+    // specified in font configuration files and use that to identify the
+    // X font directory that contains a font.dir file for that platform name
+    // and add it to the X font path (if display is local)
+    // Here we make use of an already built map of xlfds to font locations
+    // to add the font location to the set of those required to build the
+    // x font path needed by AWT.
+    // These are added to the x font path later.
+    // All this is necessary because on Solaris the font.dir directories
+    // may contain not real font files, but symbolic links to the actual
+    // location but that location is not suitable for the x font path, since
+    // it probably doesn't have a font.dir at all and certainly not one
+    // with the required encodings
+    // If the fontconfiguration file is properly set up so that all fonts
+    // are mapped to files then we will never trigger initialising
+    // xFontDirsMap (it will be null). In this case the awtfontpath entries
+    // must specify all the X11 directories needed by AWT.
+    @Override
+    protected void addFontToPlatformFontPath(String platformName) {
+        // Lazily initialize fontConfigDirs.
+        getPlatformFontPathFromFontConfig();
+        if (xFontDirsMap != null) {
+            String fontID = specificFontIDForName(platformName);
+            String dirName = (String)xFontDirsMap.get(fontID);
+            if (dirName != null) {
+                fontConfigDirs.add(dirName);
+            }
+        }
+        return;
+    }
+
+    private void getPlatformFontPathFromFontConfig() {
+        if (fontConfigDirs == null) {
+            fontConfigDirs = getFontConfiguration().getAWTFontPathSet();
+            if (FontUtilities.debugFonts() && fontConfigDirs != null) {
+                String[] names = fontConfigDirs.toArray(new String[0]);
+                for (int i=0;i<names.length;i++) {
+                    FontUtilities.getLogger().info("awtfontpath : " + names[i]);
+                }
+            }
+        }
+    }
+
+    @Override
+    protected void registerPlatformFontsUsedByFontConfiguration() {
+        // Lazily initialize fontConfigDirs.
+        getPlatformFontPathFromFontConfig();
+        if (fontConfigDirs == null) {
+            return;
+        }
+        if (FontUtilities.isLinux) {
+            fontConfigDirs.add(jreLibDirName+File.separator+"oblique-fonts");
+        }
+        fontdirs = (String[])fontConfigDirs.toArray(new String[0]);
+    }
+
+    /* Called by MToolkit to set the X11 font path */
+    public static void setNativeFontPath() {
+        if (fontdirs == null) {
+            return;
+        }
+
+        // need to register these individually rather than by one call
+        // to ensure that one bad directory doesn't cause all to be rejected
+        for (int i=0; i<fontdirs.length; i++) {
+            if (FontUtilities.debugFonts()) {
+                FontUtilities.getLogger().info("Add " + fontdirs[i] + " to X11 fontpath");
+            }
+            setNativeFontPath(fontdirs[i]);
+        }
+    }
+
+    private synchronized static native void setNativeFontPath(String fontPath);
+
+
+    // Implements SunGraphicsEnvironment.createFontConfiguration.
+    protected FontConfiguration createFontConfiguration() {
+        /* The logic here decides whether to use a preconfigured
+         * fontconfig.properties file, or synthesise one using platform APIs.
+         * On Solaris (as opposed to OpenSolaris) we try to use the
+         * pre-configured ones, but if the files it specifies are missing
+         * we fail-safe to synthesising one. This might happen if Solaris
+         * changes its fonts.
+         * For OpenSolaris I don't expect us to ever create fontconfig files,
+         * so it will always synthesise. Note that if we misidentify
+         * OpenSolaris as Solaris, then the test for the presence of
+         * Solaris-only font files will correct this.
+         * For Linux we require an exact match of distro and version to
+         * use the preconfigured file, and also that it points to
+         * existent fonts.
+         * If synthesising fails, we fall back to any preconfigured file
+         * and do the best we can. For the commercial JDK this will be
+         * fine as it includes the Lucida fonts. OpenJDK should not hit
+         * this as the synthesis should always work on its platforms.
+         */
+        FontConfiguration mFontConfig = new MFontConfiguration(this);
+        if (FontUtilities.isOpenSolaris ||
+            (FontUtilities.isLinux &&
+             (!mFontConfig.foundOsSpecificFile() ||
+              !mFontConfig.fontFilesArePresent()) ||
+             (FontUtilities.isSolaris && !mFontConfig.fontFilesArePresent()))) {
+            FcFontConfiguration fcFontConfig =
+                new FcFontConfiguration(this);
+            if (fcFontConfig.init()) {
+                return fcFontConfig;
+            }
+        }
+        mFontConfig.init();
+        return mFontConfig;
+    }
+    public FontConfiguration
+        createFontConfiguration(boolean preferLocaleFonts,
+                                boolean preferPropFonts) {
+
+        return new MFontConfiguration(this,
+                                      preferLocaleFonts, preferPropFonts);
+    }
+
+    public synchronized native String getFontPath(boolean noType1Fonts);
+
+    public String[] getDefaultPlatformFont() {
+        if (defaultPlatformFont != null) {
+            return defaultPlatformFont;
+        }
+        String[] info = new String[2];
+        getFontConfigManager().initFontConfigFonts(false);
+        FontConfigManager.FcCompFont[] fontConfigFonts =
+            getFontConfigManager().getFontConfigFonts();
+        for (int i=0; i<fontConfigFonts.length; i++) {
+            if ("sans".equals(fontConfigFonts[i].fcFamily) &&
+                0 == fontConfigFonts[i].style) {
+                info[0] = fontConfigFonts[i].firstFont.familyName;
+                info[1] = fontConfigFonts[i].firstFont.fontFile;
+                break;
+            }
+        }
+        /* Absolute last ditch attempt in the face of fontconfig problems.
+         * If we didn't match, pick the first, or just make something
+         * up so we don't NPE.
+         */
+        if (info[0] == null) {
+            if (fontConfigFonts.length > 0 &&
+                fontConfigFonts[0].firstFont.fontFile != null) {
+                info[0] = fontConfigFonts[0].firstFont.familyName;
+                info[1] = fontConfigFonts[0].firstFont.fontFile;
+            } else {
+                info[0] = "Dialog";
+                info[1] = "/dialog.ttf";
+            }
+        }
+        defaultPlatformFont = info;
+        return defaultPlatformFont;
+    }
+
+    public synchronized FontConfigManager getFontConfigManager() {
+
+        if (fcManager == null) {
+            fcManager = new FontConfigManager();
+        }
+
+        return fcManager;
+    }
+
+    @Override
+    protected FontUIResource getFontConfigFUIR(String family, int style, int size) {
+
+        CompositeFont font2D = getFontConfigManager().getFontConfigFont(family, style);
+
+        if (font2D == null) { // Not expected, just a precaution.
+           return new FontUIResource(family, style, size);
+        }
+
+        /* The name of the font will be that of the physical font in slot,
+         * but by setting the handle to that of the CompositeFont it
+         * renders as that CompositeFont.
+         * It also needs to be marked as a created font which is the
+         * current mechanism to signal that deriveFont etc must copy
+         * the handle from the original font.
+         */
+        FontUIResource fuir =
+            new FontUIResource(font2D.getFamilyName(null), style, size);
+        FontAccess.getFontAccess().setFont2D(fuir, font2D.handle);
+        FontAccess.getFontAccess().setCreatedFont(fuir);
+        return fuir;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/font/FontConfigManager.java	Fri Aug 07 19:36:28 2009 +0200
@@ -0,0 +1,454 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.font;
+
+import java.util.Locale;
+import java.util.logging.Logger;
+
+import sun.awt.SunHints;
+import sun.awt.SunToolkit;
+
+/**
+ * Small utility class to manage FontConfig.
+ */
+public class FontConfigManager {
+
+    static boolean fontConfigFailed = false;
+
+    /* This is populated by native */
+    private static final FontConfigInfo fcInfo = new FontConfigInfo();
+
+    /* Begin support for GTK Look and Feel - query libfontconfig and
+     * return a composite Font to Swing that uses the desktop font(s).
+     */
+
+    /* These next three classes are just data structures.
+     */
+    public static class FontConfigFont {
+        public String familyName;        // eg Bitstream Vera Sans
+        public String styleStr;          // eg Bold
+        public String fullName;          // eg Bitstream Vera Sans Bold
+        public String fontFile;          // eg /usr/X11/lib/fonts/foo.ttf
+    }
+
+    public static class FcCompFont {
+        public String fcName;            // eg sans
+        public String fcFamily;          // eg sans
+        public String jdkName;           // eg sansserif
+        public int style;                // eg 0=PLAIN
+        public FontConfigFont firstFont;
+        public FontConfigFont[] allFonts;
+        //boolean preferBitmaps;    // if embedded bitmaps preferred over AA
+        public CompositeFont compFont;   // null if not yet created/known.
+    }
+
+    public static class FontConfigInfo {
+        public int fcVersion;
+        public String[] cacheDirs = new String[4];
+    }
+
+    /* fontconfig recognises slants roman, italic, as well as oblique,
+     * and a slew of weights, where the ones that matter here are
+     * regular and bold.
+     * To fully qualify what we want, we can for example ask for (eg)
+     * Font.PLAIN             : "serif:regular:roman"
+     * Font.BOLD              : "serif:bold:roman"
+     * Font.ITALIC            : "serif:regular:italic"
+     * Font.BOLD|Font.ITALIC  : "serif:bold:italic"
+     */
+    private static String[] fontConfigNames = {
+        "sans:regular:roman",
+        "sans:bold:roman",
+        "sans:regular:italic",
+        "sans:bold:italic",
+
+        "serif:regular:roman",
+        "serif:bold:roman",
+        "serif:regular:italic",
+        "serif:bold:italic",
+
+        "monospace:regular:roman",
+        "monospace:bold:roman",
+        "monospace:regular:italic",
+        "monospace:bold:italic",
+    };
+
+    /* This array has the array elements created in Java code and is
+     * passed down to native to be filled in.
+     */
+    private FcCompFont[] fontConfigFonts;
+
+    /**
+     * Instantiates a new FontConfigManager getting the default instance
+     * of FontManager from the FontManagerFactory.
+     */
+    public FontConfigManager() {
+    }
+
+    public static String[] getFontConfigNames() {
+        return fontConfigNames;
+    }
+
+    /* Called from code that needs to know what are the AA settings
+     * that apps using FC would pick up for the default desktop font.
+     * Note apps can change the default desktop font. etc, so this
+     * isn't certain to be right but its going to correct for most cases.
+     * Native return values map to the text aa values in sun.awt.SunHints.
+     * which is used to look up the renderinghint value object.
+     */
+    public static Object getFontConfigAAHint() {
+        return getFontConfigAAHint("sans");
+    }
+
+    /* This is public solely so that for debugging purposes it can be called
+     * with other names, which might (eg) include a size, eg "sans-24"
+     * The return value is a text aa rendering hint value.
+     * Normally we should call the no-args version.
+     */
+    public static Object getFontConfigAAHint(String fcFamily) {
+        if (FontUtilities.isWindows) {
+            return null;
+        } else {
+            int hint = getFontConfigAASettings(getFCLocaleStr(), fcFamily);
+            if (hint < 0) {
+                return null;
+            } else {
+                return SunHints.Value.get(SunHints.INTKEY_TEXT_ANTIALIASING,
+                                          hint);
+            }
+        }
+    }
+
+
+    private static String getFCLocaleStr() {
+        Locale l = SunToolkit.getStartupLocale();
+        String localeStr = l.getLanguage();
+        String country = l.getCountry();
+        if (!country.equals("")) {
+            localeStr = localeStr + "-" + country;
+        }
+        return localeStr;
+    }
+
+    /* This does cause the native libfontconfig to be loaded and unloaded,
+     * but it does not incur the overhead of initialisation of its
+     * data structures, so shouldn't have a measurable impact.
+     */
+    public static native int getFontConfigVersion();
+
+    /* This can be made public if it's needed to force a re-read
+     * rather than using the cached values. The re-read would be needed
+     * only if some event signalled that the fontconfig has changed.
+     * In that event this method would need to return directly the array
+     * to be used by the caller in case it subsequently changed.
+     */
+    public synchronized void initFontConfigFonts(boolean includeFallbacks) {
+
+        if (fontConfigFonts != null) {
+            if (!includeFallbacks || (fontConfigFonts[0].allFonts != null)) {
+                return;
+            }
+        }
+
+        if (FontUtilities.isWindows || fontConfigFailed) {
+            return;
+        }
+
+        long t0 = 0;
+        if (FontUtilities.isLogging()) {
+            t0 = System.nanoTime();
+        }
+
+        String[] fontConfigNames = FontConfigManager.getFontConfigNames();
+        FcCompFont[] fontArr = new FcCompFont[fontConfigNames.length];
+
+        for (int i = 0; i< fontArr.length; i++) {
+            fontArr[i] = new FcCompFont();
+            fontArr[i].fcName = fontConfigNames[i];
+            int colonPos = fontArr[i].fcName.indexOf(':');
+            fontArr[i].fcFamily = fontArr[i].fcName.substring(0, colonPos);
+            fontArr[i].jdkName = FontUtilities.mapFcName(fontArr[i].fcFamily);
+            fontArr[i].style = i % 4; // depends on array order.
+        }
+        getFontConfig(getFCLocaleStr(), fcInfo, fontArr, includeFallbacks);
+        /* If don't find anything (eg no libfontconfig), then just return */
+        for (int i = 0; i< fontArr.length; i++) {
+            FcCompFont fci = fontArr[i];
+            if (fci.firstFont == null) {
+                if (FontUtilities.isLogging()) {
+                    Logger logger = FontUtilities.getLogger();
+                    logger.info("Fontconfig returned no fonts.");
+                }
+                fontConfigFailed = true;
+                return;
+            }
+        }
+        fontConfigFonts = fontArr;
+
+        if (FontUtilities.isLogging()) {
+
+            Logger logger = FontUtilities.getLogger();
+
+            long t1 = System.nanoTime();
+            logger.info("Time spent accessing fontconfig="
+                        + ((t1 - t0) / 1000000) + "ms.");
+
+            for (int i = 0; i< fontConfigFonts.length; i++) {
+                FcCompFont fci = fontConfigFonts[i];
+                logger.info("FC font " + fci.fcName+" maps to family " +
+                            fci.firstFont.familyName +
+                            " in file " + fci.firstFont.fontFile);
+                if (fci.allFonts != null) {
+                    for (int f=0;f<fci.allFonts.length;f++) {
+                        FontConfigFont fcf = fci.allFonts[f];
+                        logger.info("Family=" + fcf.familyName +
+                                    " Style="+ fcf.styleStr +
+                                    " Fullname="+fcf.fullName +
+                                    " File="+fcf.fontFile);
+                    }
+                }
+            }
+        }
+    }
+
+    public PhysicalFont registerFromFcInfo(FcCompFont fcInfo) {
+
+        SunFontManager fm = SunFontManager.getInstance();
+
+        /* If it's a TTC file we need to know that as we will need to
+         * make sure we return the right font */
+        String fontFile = fcInfo.firstFont.fontFile;
+        int offset = fontFile.length()-4;
+        if (offset <= 0) {
+            return null;
+        }
+        String ext = fontFile.substring(offset).toLowerCase();
+        boolean isTTC = ext.equals(".ttc");
+
+        /* If this file is already registered, can just return its font.
+         * However we do need to check in case it's a TTC as we need
+         * a specific font, so rather than directly returning it, let
+         * findFont2D resolve that.
+         */
+        PhysicalFont physFont = fm.getRegisteredFontFile(fontFile);
+        if (physFont != null) {
+            if (isTTC) {
+                Font2D f2d = fm.findFont2D(fcInfo.firstFont.familyName,
+                                           fcInfo.style,
+                                           FontManager.NO_FALLBACK);
+                if (f2d instanceof PhysicalFont) { /* paranoia */
+                    return (PhysicalFont)f2d;
+                } else {
+                    return null;
+                }
+            } else {
+                return physFont;
+            }
+        }
+
+        /* If the font may hide a JRE font (eg fontconfig says it is
+         * Lucida Sans), we want to use the JRE version, so make it
+         * point to the JRE font.
+         */
+        physFont = fm.findJREDeferredFont(fcInfo.firstFont.familyName,
+                                          fcInfo.style);
+
+        /* It is also possible the font file is on the "deferred" list,
+         * in which case we can just initialise it now.
+         */
+        if (physFont == null &&
+            fm.isDeferredFont(fontFile) == true) {
+            physFont = fm.initialiseDeferredFont(fcInfo.firstFont.fontFile);
+            /* use findFont2D to get the right font from TTC's */
+            if (physFont != null) {
+                if (isTTC) {
+                    Font2D f2d = fm.findFont2D(fcInfo.firstFont.familyName,
+                                               fcInfo.style,
+                                               FontManager.NO_FALLBACK);
+                    if (f2d instanceof PhysicalFont) { /* paranoia */
+                        return (PhysicalFont)f2d;
+                    } else {
+                        return null;
+                    }
+                } else {
+                    return physFont;
+                }
+            }
+        }
+
+        /* In the majority of cases we reach here, and need to determine
+         * the type and rank to register the font.
+         */
+        if (physFont == null) {
+            int fontFormat = SunFontManager.FONTFORMAT_NONE;
+            int fontRank = Font2D.UNKNOWN_RANK;
+
+            if (ext.equals(".ttf") || isTTC) {
+                fontFormat = SunFontManager.FONTFORMAT_TRUETYPE;
+                fontRank = Font2D.TTF_RANK;
+            } else if (ext.equals(".pfa") || ext.equals(".pfb")) {
+                fontFormat = SunFontManager.FONTFORMAT_TYPE1;
+                fontRank = Font2D.TYPE1_RANK;
+            }
+            physFont = fm.registerFontFile(fcInfo.firstFont.fontFile, null,
+                                      fontFormat, true, fontRank);
+        }
+        return physFont;
+    }
+
+    /*
+     * We need to return a Composite font which has as the font in
+     * its first slot one obtained from fontconfig.
+     */
+    public CompositeFont getFontConfigFont(String name, int style) {
+
+        name = name.toLowerCase();
+
+        initFontConfigFonts(false);
+
+        FcCompFont fcInfo = null;
+        for (int i=0; i<fontConfigFonts.length; i++) {
+            if (name.equals(fontConfigFonts[i].fcFamily) &&
+                style == fontConfigFonts[i].style) {
+                fcInfo = fontConfigFonts[i];
+                break;
+            }
+        }
+        if (fcInfo == null) {
+            fcInfo = fontConfigFonts[0];
+        }
+
+        if (FontUtilities.isLogging()) {
+            FontUtilities.getLogger()
+                          .info("FC name=" + name + " style=" + style +
+                                " uses " + fcInfo.firstFont.familyName +
+                                " in file: " + fcInfo.firstFont.fontFile);
+        }
+
+        if (fcInfo.compFont != null) {
+            return fcInfo.compFont;
+        }
+
+        /* jdkFont is going to be used for slots 1..N and as a fallback.
+         * Slot 0 will be the physical font from fontconfig.
+         */
+        FontManager fm = FontManagerFactory.getInstance();
+        CompositeFont jdkFont = (CompositeFont)
+            fm.findFont2D(fcInfo.jdkName, style, FontManager.LOGICAL_FALLBACK);
+
+        if (fcInfo.firstFont.familyName == null ||
+            fcInfo.firstFont.fontFile == null) {
+            return (fcInfo.compFont = jdkFont);
+        }
+
+        /* First, see if the family and exact style is already registered.
+         * If it is, use it. If it's not, then try to register it.
+         * If that registration fails (signalled by null) just return the
+         * regular JDK composite.
+         * Algorithmically styled fonts won't match on exact style, so
+         * will fall through this code, but the regisration code will
+         * find that file already registered and return its font.
+         */
+        FontFamily family = FontFamily.getFamily(fcInfo.firstFont.familyName);
+        PhysicalFont physFont = null;
+        if (family != null) {
+            Font2D f2D = family.getFontWithExactStyleMatch(fcInfo.style);
+            if (f2D instanceof PhysicalFont) {
+                physFont = (PhysicalFont)f2D;
+            }
+        }
+
+        if (physFont == null ||
+            !fcInfo.firstFont.fontFile.equals(physFont.platName)) {
+            physFont = registerFromFcInfo(fcInfo);
+            if (physFont == null) {
+                return (fcInfo.compFont = jdkFont);
+            }
+            family = FontFamily.getFamily(physFont.getFamilyName(null));
+        }
+
+        /* Now register the fonts in the family (the other styles) after
+         * checking that they aren't already registered and are actually in
+         * a different file. They may be the same file in CJK cases.
+         * For cases where they are different font files - eg as is common for
+         * Latin fonts, then we rely on fontconfig to report these correctly.
+         * Assume that all styles of this font are found by fontconfig,
+         * so we can find all the family members which must be registered
+         * together to prevent synthetic styling.
+         */
+        for (int i=0; i<fontConfigFonts.length; i++) {
+            FcCompFont fc = fontConfigFonts[i];
+            if (fc != fcInfo &&
+                physFont.getFamilyName(null).equals(fc.firstFont.familyName) &&
+                !fc.firstFont.fontFile.equals(physFont.platName) &&
+                family.getFontWithExactStyleMatch(fc.style) == null) {
+
+                registerFromFcInfo(fontConfigFonts[i]);
+            }
+        }
+
+        /* Now we have a physical font. We will back this up with the JDK
+         * logical font (sansserif, serif, or monospaced) that corresponds
+         * to the Pango/GTK/FC logical font name.
+         */
+        return (fcInfo.compFont = new CompositeFont(physFont, jdkFont));
+    }
+
+    /**
+     *
+     * @param locale
+     * @param fcFamily
+     * @return
+     */
+    public FcCompFont[] getFontConfigFonts() {
+        return fontConfigFonts;
+    }
+
+    /* Return an array of FcCompFont structs describing the primary
+     * font located for each of fontconfig/GTK/Pango's logical font names.
+     */
+    private static native void getFontConfig(String locale,
+                                             FontConfigInfo fcInfo,
+                                             FcCompFont[] fonts,
+                                             boolean includeFallbacks);
+
+    void populateFontConfig(FcCompFont[] fcInfo) {
+        fontConfigFonts = fcInfo;
+    }
+
+    FcCompFont[] loadFontConfig() {
+        initFontConfigFonts(true);
+        return fontConfigFonts;
+    }
+
+    FontConfigInfo getFontConfigInfo() {
+        initFontConfigFonts(true);
+        return fcInfo;
+    }
+
+    private static native int
+    getFontConfigAASettings(String locale, String fcFamily);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/windows/classes/sun/awt/Win32FontManager.java	Fri Aug 07 19:36:28 2009 +0200
@@ -0,0 +1,280 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+
+package sun.awt;
+
+import java.awt.FontFormatException;
+import java.awt.GraphicsEnvironment;
+import java.io.File;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.NoSuchElementException;
+import java.util.StringTokenizer;
+
+import sun.awt.Win32GraphicsEnvironment;
+import sun.awt.windows.WFontConfiguration;
+import sun.font.FontManager;
+import sun.font.SunFontManager;
+import sun.font.TrueTypeFont;
+import sun.java2d.HeadlessGraphicsEnvironment;
+import sun.java2d.SunGraphicsEnvironment;
+
+/**
+ * The X11 implementation of {@link FontManager}.
+ */
+public class Win32FontManager extends SunFontManager {
+
+    private static String[] defaultPlatformFont = null;
+
+    private static TrueTypeFont eudcFont;
+
+    static {
+
+        AccessController.doPrivileged(new PrivilegedAction() {
+
+                public Object run() {
+                    String eudcFile = getEUDCFontFile();
+                    if (eudcFile != null) {
+                        try {
+                            eudcFont = new TrueTypeFont(eudcFile, null, 0,
+                                                        true);
+                        } catch (FontFormatException e) {
+                        }
+                    }
+                    return null;
+                }
+
+            });
+    }
+
+    /* Used on Windows to obtain from the windows registry the name
+     * of a file containing the system EUFC font. If running in one of
+     * the locales for which this applies, and one is defined, the font
+     * defined by this file is appended to all composite fonts as a
+     * fallback component.
+     */
+    private static native String getEUDCFontFile();
+
+    public Win32FontManager() {
+        super();
+        AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+
+                    /* Register the JRE fonts so that the native platform can
+                     * access them. This is used only on Windows so that when
+                     * printing the printer driver can access the fonts.
+                     */
+                    registerJREFontsWithPlatform(jreFontDirName);
+                    return null;
+                }
+            });
+    }
+
+    /* Unlike the shared code version, this expects a base file name -
+     * not a full path name.
+     * The font configuration file has base file names and the FontConfiguration
+     * class reports these back to the GraphicsEnvironment, so these
+     * are the componentFileNames of CompositeFonts.
+     */
+    protected void registerFontFile(String fontFileName, String[] nativeNames,
+                                    int fontRank, boolean defer) {
+
+        // REMIND: case compare depends on platform
+        if (registeredFontFiles.contains(fontFileName)) {
+            return;
+        }
+        registeredFontFiles.add(fontFileName);
+
+        int fontFormat;
+        if (getTrueTypeFilter().accept(null, fontFileName)) {
+            fontFormat = SunFontManager.FONTFORMAT_TRUETYPE;
+        } else if (getType1Filter().accept(null, fontFileName)) {
+            fontFormat = SunFontManager.FONTFORMAT_TYPE1;
+        } else {
+            /* on windows we don't use/register native fonts */
+            return;
+        }
+
+        if (fontPath == null) {
+            fontPath = getPlatformFontPath(noType1Font);
+        }
+
+        /* Look in the JRE font directory first.
+         * This is playing it safe as we would want to find fonts in the
+         * JRE font directory ahead of those in the system directory
+         */
+        String tmpFontPath = jreFontDirName+File.pathSeparator+fontPath;
+        StringTokenizer parser = new StringTokenizer(tmpFontPath,
+                                                     File.pathSeparator);
+
+        boolean found = false;
+        try {
+            while (!found && parser.hasMoreTokens()) {
+                String newPath = parser.nextToken();
+                File theFile = new File(newPath, fontFileName);
+                if (theFile.canRead()) {
+                    found = true;
+                    String path = theFile.getAbsolutePath();
+                    if (defer) {
+                        registerDeferredFont(fontFileName, path,
+                                             nativeNames,
+                                             fontFormat, true,
+                                             fontRank);
+                    } else {
+                        registerFontFile(path, nativeNames,
+                                         fontFormat, true,
+                                         fontRank);
+                    }
+                    break;
+                }
+            }
+        } catch (NoSuchElementException e) {
+            System.err.println(e);
+        }
+        if (!found) {
+            addToMissingFontFileList(fontFileName);
+        }
+    }
+
+    @Override
+    protected FontConfiguration createFontConfiguration() {
+
+       FontConfiguration fc = new WFontConfiguration(this);
+       fc.init();
+       return fc;
+    }
+
+    @Override
+    public FontConfiguration createFontConfiguration(boolean preferLocaleFonts,
+            boolean preferPropFonts) {
+
+        return new WFontConfiguration(this,
+                                      preferLocaleFonts,preferPropFonts);
+    }
+
+    protected void
+        populateFontFileNameMap(HashMap<String,String> fontToFileMap,
+                                HashMap<String,String> fontToFamilyNameMap,
+                                HashMap<String,ArrayList<String>>
+                                familyToFontListMap,
+                                Locale locale) {
+
+        populateFontFileNameMap0(fontToFileMap, fontToFamilyNameMap,
+                                 familyToFontListMap, locale);
+
+    }
+
+    private static native void
+        populateFontFileNameMap0(HashMap<String,String> fontToFileMap,
+                                 HashMap<String,String> fontToFamilyNameMap,
+                                 HashMap<String,ArrayList<String>>
+                                     familyToFontListMap,
+                                 Locale locale);
+
+    public synchronized native String getFontPath(boolean noType1Fonts);
+
+    public String[] getDefaultPlatformFont() {
+
+        if (defaultPlatformFont != null) {
+            return defaultPlatformFont;
+        }
+
+        String[] info = new String[2];
+        info[0] = "Arial";
+        info[1] = "c:\\windows\\fonts";
+        final String[] dirs = getPlatformFontDirs(true);
+        if (dirs.length > 1) {
+            String dir = (String)
+                AccessController.doPrivileged(new PrivilegedAction() {
+                        public Object run() {
+                            for (int i=0; i<dirs.length; i++) {
+                                String path =
+                                    dirs[i] + File.separator + "arial.ttf";
+                                File file = new File(path);
+                                if (file.exists()) {
+                                    return dirs[i];
+                                }
+                            }
+                            return null;
+                        }
+                    });
+            if (dir != null) {
+                info[1] = dir;
+            }
+        } else {
+            info[1] = dirs[0];
+        }
+        info[1] = info[1] + File.separator + "arial.ttf";
+        defaultPlatformFont = info;
+        return defaultPlatformFont;
+    }
+
+    /* register only TrueType/OpenType fonts
+     * Because these need to be registed just for use when printing,
+     * we defer the actual registration and the static initialiser
+     * for the printing class makes the call to registerJREFontsForPrinting()
+     */
+    static String fontsForPrinting = null;
+    protected void registerJREFontsWithPlatform(String pathName) {
+        fontsForPrinting = pathName;
+    }
+
+    public static void registerJREFontsForPrinting() {
+        final String pathName;
+        synchronized (Win32GraphicsEnvironment.class) {
+            GraphicsEnvironment.getLocalGraphicsEnvironment();
+            if (fontsForPrinting == null) {
+                return;
+            }
+            pathName = fontsForPrinting;
+            fontsForPrinting = null;
+        }
+        java.security.AccessController.doPrivileged(
+            new java.security.PrivilegedAction() {
+                public Object run() {
+                    File f1 = new File(pathName);
+                    String[] ls = f1.list(SunFontManager.getInstance().
+                            getTrueTypeFilter());
+                    if (ls == null) {
+                        return null;
+                    }
+                    for (int i=0; i <ls.length; i++ ) {
+                        File fontFile = new File(f1, ls[i]);
+                        registerFontWithPlatform(fontFile.getAbsolutePath());
+                    }
+                    return null;
+                }
+         });
+    }
+
+    protected static native void registerFontWithPlatform(String fontName);
+
+    protected static native void deRegisterFontWithPlatform(String fontName);
+
+}