Use Tree Navigation
public abstract class

SunGraphicsEnvironment

extends GraphicsEnvironment
implements DisplayChangedListener FontSupport
/*
 * Copyright (c) 1997, 2010, Oracle and/or its affiliates. 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.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */


package sun.java2d;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.font.TextAttribute;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.InputStreamReader;
import java.io.IOException;
import java.text.AttributedCharacterIterator;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
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 sun.awt.AppContext;
import sun.awt.DisplayChangedListener;
import sun.awt.FontConfiguration;
import sun.awt.SunDisplayChanger;
import sun.font.CompositeFontDescriptor;
import sun.font.Font2D;
import sun.font.FontManager;
import sun.font.NativeFont;

/**
 * This is an implementation of a GraphicsEnvironment object for the
 * default local GraphicsEnvironment.
 *
 * @see GraphicsDevice
 * @see GraphicsConfiguration
 */


public abstract class SunGraphicsEnvironment extends GraphicsEnvironment
   
implements FontSupport, DisplayChangedListener {

   
public static boolean isLinux;
   
public static boolean isSolaris;
   
public static boolean isWindows;
   
public static boolean noType1Font;
   
private static Font defaultFont;
   
private static String defaultFontFileName;
   
private static String defaultFontName;
   
public static final String lucidaFontName = "Lucida Sans Regular";
   
public static final String lucidaFileName = "LucidaSansRegular.ttf";
   
public static boolean debugFonts = false;
   
protected static Logger logger = null;
   
private static ArrayList badFonts;
   
public static String jreLibDirName;
   
public static String jreFontDirName;
   
private static HashSet<String> missingFontFiles = null;

   
private FontConfiguration fontConfig;

   
/* 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;

   
/* 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;

   
/* loadedAllFontFiles is set to true when all fonts on the font path are
     * actually opened, validated and registered. This always implies
     * discoveredAllFonts is true.
     */

   
private boolean loadedAllFontFiles = false;

   
protected HashSet registeredFontFiles = new HashSet();
   
public static String eudcFontFileName; /* Initialised only on windows */

   
private static boolean isOpenJDK;
   
/**
     * A few things in Java 2D code are different in OpenJDK,
     * so need a way to tell which implementation this is.
     * The absence of Lucida Sans Regular is the simplest way for now.
     */

   
public static boolean isOpenJDK() {
       
return isOpenJDK;
   
}

   
static {
        java
.security.AccessController.doPrivileged(
                                   
new java.security.PrivilegedAction() {
           
public Object run() {

                jreLibDirName
= System.getProperty("java.home","") +
                   
File.separator + "lib";
                jreFontDirName
= jreLibDirName + File.separator + "fonts";
               
File lucidaFile =
                   
new File(jreFontDirName + File.separator + lucidaFileName);
                isOpenJDK
= !lucidaFile.exists();

               
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);
                   
}
               
}
               
return null;
           
}
       
});
   
};

   
public SunGraphicsEnvironment() {
        java
.security.AccessController.doPrivileged(
                                   
new java.security.PrivilegedAction() {
           
public Object run() {
               
String osName = System.getProperty("os.name");
               
if ("Linux".equals(osName)) {
                    isLinux
= true;
               
} else if ("SunOS".equals(osName)) {
                    isSolaris
= true;
               
} else if ("Windows".equals(osName)) {
                    isWindows
= true;
               
}

                noType1Font
= "true".
                    equals
(System.getProperty("sun.java2d.noType1Font"));

               
if (isOpenJDK()) {
                   
String[] fontInfo = FontManager.getDefaultPlatformFont();
                    defaultFontName
= fontInfo[0];
                    defaultFontFileName
= fontInfo[1];
               
} else {
                    defaultFontName
= lucidaFontName;
                   
if (useAbsoluteFontFileNames()) {
                        defaultFontFileName
=
                            jreFontDirName
+ File.separator + lucidaFileName;
                   
} else {
                        defaultFontFileName
= lucidaFileName;
                   
}
               
}

               
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 (debugFonts) {
                                    logger
.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 (isLinux) {
                   
/* Linux font configuration uses these fonts */
                    registerFontDir
(jreFontDirName);
               
}
                registerFontsInDir
(jreFontDirName, true, Font2D.JRE_RANK,
                                   
true, false);

               
/* 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);

               
/* Create the font configuration and get any font path
                 * that might be specified.
                 */

                fontConfig
= createFontConfiguration();
                getPlatformFontPathFromFontConfig
();

               
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 (debugFonts) {
                    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 (isSolaris && Locale.JAPAN.equals(Locale.getDefault())) {
                    registerFontDir
("/usr/openwin/lib/locale/ja/X11/fonts/TT");
               
}

                initCompositeFonts
(fontConfig, null);

               
/* Establish the default font to be used by SG2D etc */
                defaultFont
= new Font(Font.DIALOG, Font.PLAIN, 12);

               
return null;
           
}
       
});
   
}

   
protected GraphicsDevice[] screens;

   
/**
     * Returns an array of all of the screen devices.
     */

   
public synchronized GraphicsDevice[] getScreenDevices() {
       
GraphicsDevice[] ret = screens;
       
if (ret == null) {
           
int num = getNumScreens();
            ret
= new GraphicsDevice[num];
           
for (int i = 0; i < num; i++) {
                ret
[i] = makeScreenDevice(i);
           
}
            screens
= ret;
       
}
       
return ret;
   
}

   
protected abstract int getNumScreens();
   
protected abstract GraphicsDevice makeScreenDevice(int screennum);

   
/**
     * Returns the default screen graphics device.
     */

   
public GraphicsDevice getDefaultScreenDevice() {
       
return getScreenDevices()[0];
   
}

   
/**
     * Returns a Graphics2D object for rendering into the
     * given BufferedImage.
     * @throws NullPointerException if BufferedImage argument is null
     */

   
public Graphics2D createGraphics(BufferedImage img) {
       
if (img == null) {
           
throw new NullPointerException("BufferedImage cannot be null");
       
}
       
SurfaceData sd = SurfaceData.getPrimarySurfaceData(img);
       
return new SunGraphics2D(sd, Color.white, Color.black, defaultFont);
   
}

   
/* A call to this method should be followed by a call to
     * registerFontDirs(..)
     */

   
protected String getPlatformFontPath(boolean noType1Font) {
       
if (fontPath == null) {
            fontPath
= FontManager.getFontPath(noType1Font);
       
}
       
return fontPath;
   
}

   
private String[] platformFontDirs;
   
/**
     * Get all directories which contain installed fonts.
     */

   
public String[] getPlatformFontDirs() {
       
if (platformFontDirs == null) {
           
String path = getPlatformFontPath(noType1Font);
           
StringTokenizer parser =
               
new StringTokenizer(path, File.pathSeparator);;
           
ArrayList<String> pathList = new ArrayList<String>();
           
try {
               
while (parser.hasMoreTokens()) {
                    pathList
.add(parser.nextToken());
               
}
           
} catch (NoSuchElementException e) {
           
}
            platformFontDirs
= pathList.toArray(new String[0]);
       
}
       
return platformFontDirs;
   
}

   
/**
     * Whether registerFontFile expects absolute or relative
     * font file names.
     */

   
protected boolean useAbsoluteFontFileNames() {
       
return true;
   
}

   
/**
     * Returns file name for default font, either absolute
     * or relative as needed by registerFontFile.
     */

   
public String getDefaultFontFile() {
       
return defaultFontFileName;
   
}

   
/**
     * Returns face name for default font, or null if
     * no face names are used for CompositeFontDescriptors
     * for this platform.
     */

   
public String getDefaultFontFaceName() {
       
return defaultFontName;
   
}

   
public void loadFonts() {
       
if (discoveredAllFonts) {
           
return;
       
}
       
/* Use lock specific to the font system */
       
synchronized (FontManager.class) {
           
if (debugFonts) {
               
Thread.dumpStack();
                logger
.info("SunGraphicsEnvironment.loadFonts() called");
           
}
           
FontManager.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 (!FontManager.gotFontsFromPlatform()) {
                            registerFontsOnPath
(fontPath, false,
                                               
Font2D.UNKNOWN_RANK,
                                               
false, true);
                            loadedAllFontFiles
= true;
                       
}
                   
}
                   
FontManager.registerOtherFontFiles(registeredFontFiles);
                    discoveredAllFonts
= true;
                   
return null;
               
}
           
});
       
}
   
}


   
public void loadFontFiles() {
        loadFonts
();
       
if (loadedAllFontFiles) {
           
return;
       
}
       
/* Use lock specific to the font system */
       
synchronized (FontManager.class) {
           
if (debugFonts) {
               
Thread.dumpStack();
                logger
.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 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 = FontManager.getFileNameForFontName(fontName);
       
if (fileName == null) {
           
return false;
       
}
       
return registeredFontFiles.contains(fileName);
   
}

   
private Font[] allFonts;

   
/**
     * 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 = FontManager.getRegisteredFonts();
           
for (int i=0; i < allfonts.length; i++) {
               
if (!(allfonts[i] instanceof NativeFont)) {
                    fontMapNames
.put(allfonts[i].getFontName(null),
                                     allfonts
[i]);
               
}
           
}

           
String[] platformNames =  FontManager.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) {
                   
FontManager.setFont2D(fonts[i], f2d.handle);
               
}
           
}
            allFonts
= fonts;
       
}

       
Font []copyFonts = new Font[allFonts.length];
       
System.arraycopy(allFonts, 0, copyFonts, 0, allFonts.length);
       
return copyFonts;
   
}

     
/**
     * Returns all fonts available in this environment.
     */

   
public Font[] getAllFonts() {
       
Font[] installedFonts = getAllInstalledFonts();
       
Font[] created = FontManager.getCreatedFonts();
       
if (created == null || created.length == 0) {
           
return installedFonts;
       
} else {
           
int newlen = installedFonts.length + created.length;
           
Font [] fonts = java.util.Arrays.copyOf(installedFonts, newlen);
           
System.arraycopy(created, 0, fonts,
                             installedFonts
.length, created.length);
           
return fonts;
       
}
   
}

   
/**
     * 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;
   
public 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;
   
}

   
/* 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) {
       
FontManager.registerDeferredJREFonts(jreFontDirName);
       
Font2D[] physicalfonts = FontManager.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);
           
}
       
}
   
}

   
private String[] allFamilies; // cache for default locale only
   
private Locale lastDefaultLocale;

   
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()) &&
           
FontManager.getFamilyNamesFromPlatform(familyNames,
                                                    requestedLocale
)) {
           
/* Augment platform names with JRE font family names */
            getJREFontFamilyNames
(familyNames, requestedLocale);
       
} else {
            loadFontFiles
();
           
Font2D[] physicalfonts = FontManager.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 String[] getAvailableFontFamilyNames(Locale requestedLocale) {
       
String[] installed = getInstalledFontFamilyNames(requestedLocale);
       
/* Use a new TreeMap as used in getInstalledFontFamilyNames
         * and insert all the keys in lower case, so that the sort order
         * is the same as the installed families. This preserves historical
         * behaviour and inserts new families in the right place.
         * It would have been marginally more efficient to directly obtain
         * the tree map and just insert new entries, but not so much as
         * to justify the extra internal interface.
         */

       
TreeMap<String, String> map = FontManager.getCreatedFontFamilyNames();
       
if (map == null || map.size() == 0) {
           
return installed;
       
} else {
           
for (int i=0; i<installed.length; i++) {
                map
.put(installed[i].toLowerCase(requestedLocale),
                        installed
[i]);
           
}
           
String[] retval =  new String[map.size()];
           
Object [] keyNames = map.keySet().toArray();
           
for (int i=0; i < keyNames.length; i++) {
                retval
[i] = (String)map.get(keyNames[i]);
           
}
           
return retval;
       
}
   
}

   
public String[] getAvailableFontFamilyNames() {
       
return getAvailableFontFamilyNames(Locale.getDefault());
   
}

   
/**
     * 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);
   
}

   
public 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) ||
                       name
.startsWith(".otf", offset) ||
                       name
.startsWith(".OTF", offset));
           
}
       
}
   
}

   
public 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));
           
}
       
}
   
}

   
public static class TTorT1Filter implements FilenameFilter {
         
public boolean accept(File dir, String name) {
             
return SunGraphicsEnvironment.ttFilter.accept(dir, name) ||
                 
SunGraphicsEnvironment.t1Filter.accept(dir, name);
         
}
   
}

   
/* No need to keep consing up new instances - reuse a singleton.
     * The trade-off is that these objects don't get GC'd.
     */

   
public static final TTFilter ttFilter = new TTFilter();
   
public static final T1Filter t1Filter = new T1Filter();

   
/* The majority of the register functions in this class are
     * registering platform fonts in the JRE's font maps.
     * The next one is opposite in function as it registers the JRE
     * fonts as platform fonts. If subsequent to calling this
     * your implementation enumerates platform fonts in a way that
     * would return these fonts too you may get duplicates.
     * This function is primarily used to install the JRE fonts
     * so that the native platform can access them.
     * It is intended to be overridden by platform subclasses
     * Currently minimal use is made of this as generally
     * Java 2D doesn't need the platform to be able to
     * use its fonts and platforms which already have matching
     * fonts registered (possibly even from other different JRE
     * versions) generally can't be guaranteed to use the
     * one registered by this JRE version in response to
     * requests from this JRE.
     */

   
protected void registerJREFontsWithPlatform(String pathName) {
       
return;
   
}

   
/* Called from FontManager - has Solaris specific implementation */
   
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;
           
}
       
});
   
}

   
protected void registerFontDirs(String pathName) {
       
return;
   
}

   
/* 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,
                   
FontManager.FONTFORMAT_TRUETYPE, useJavaRasterizer,
                    fontRank
==Font2D.UNKNOWN_RANK ?
                   
Font2D.TTF_RANK : fontRank,
                    defer
, resolveSymLinks);
        addDirFonts
(dirName, pathFile, t1Filter,
                   
FontManager.FONTFORMAT_TYPE1, useJavaRasterizer,
                    fontRank
==Font2D.UNKNOWN_RANK ?
                   
Font2D.TYPE1_RANK : fontRank,
                    defer
, resolveSymLinks);
   
}

   
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) {
       
}
   
}

   
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
= FontManager.FONTFORMAT_TRUETYPE;
       
} else if (t1Filter.accept(null, fontFileName)) {
            fontFormat
= FontManager.FONTFORMAT_TYPE1;
       
} else {
            fontFormat
= FontManager.FONTFORMAT_NATIVE;
       
}
        registeredFontFiles
.add(fontFileName);
       
if (defer) {
           
FontManager.registerDeferredFont(fontFileName,
                                             fontFileName
, nativeNames,
                                             fontFormat
, false, fontRank);
       
} else {
           
FontManager.registerFontFile(fontFileName, nativeNames,
                                         fontFormat
, false, fontRank);
       
}
   
}

   
protected void registerFontDir(String path) {
   
}

   
protected String[] getNativeNames(String fontFileName,
                                     
String platformName) {
       
return null;
   
}

   
/*
     * 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 (debugFonts) {
                    logger
.warning("skip bad font " + fullName);
               
}
               
continue; // skip this font file.
           
}

            registeredFontFiles
.add(fullName);

           
if (debugFonts && logger.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];
                   
}
               
}
                logger
.info(message);
           
}
            fontNames
[fontCount] = fullName;
            nativeNames
[fontCount++] = getNativeNames(fullName, null);
       
}
       
FontManager.registerFonts(fontNames, nativeNames, fontCount,
                                  fontFormat
, useJavaRasterizer, fontRank,
                                  defer
);
       
return;
   
}

   
/*
     * 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);
   
}

   
/**
     * Creates this environment's FontConfiguration.
     */

   
protected abstract FontConfiguration createFontConfiguration();

   
public abstract FontConfiguration
        createFontConfiguration
(boolean preferLocaleFonts,
                               
boolean preferPropFonts);

   
/*
     * 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) {
               
/* 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) {
               
FontManager.registerCompositeFont(
                    descriptor
.getFaceName(),
                    componentFileNames
, componentFaceNames,
                    descriptor
.getCoreComponentCount(),
                    descriptor
.getExclusionRanges(),
                    descriptor
.getExclusionRangeLimits(),
                   
true,
                    altNameCache
);
           
} else {
               
FontManager.registerCompositeFont(
                    descriptor
.getFaceName(),
                    componentFileNames
, componentFaceNames,
                    descriptor
.getCoreComponentCount(),
                    descriptor
.getExclusionRanges(),
                    descriptor
.getExclusionRangeLimits(),
                   
true);
           
}
           
if (debugFonts) {
                logger
.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 registerPlatformFontsUsedByFontConfiguration() {
   
}

   
/**
     * Determines whether the given font is a logical font.
     */

   
public static boolean isLogicalFont(Font f) {
       
return FontConfiguration.isLogicalFontFamilyName(f.getFamily());
   
}

   
/**
     * Return the default font configuration.
     */

   
public FontConfiguration getFontConfiguration() {
       
return fontConfig;
   
}

   
/**
     * Return the bounds of a GraphicsDevice, less its screen insets.
     * See also java.awt.GraphicsEnvironment.getUsableBounds();
     */

   
public static Rectangle getUsableBounds(GraphicsDevice gd) {
       
GraphicsConfiguration gc = gd.getDefaultConfiguration();
       
Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(gc);
       
Rectangle usableBounds = gc.getBounds();

        usableBounds
.x += insets.left;
        usableBounds
.y += insets.top;
        usableBounds
.width -= (insets.left + insets.right);
        usableBounds
.height -= (insets.top + insets.bottom);

       
return usableBounds;
   
}

   
/**
     * This method is provided for internal and exclusive use by Swing.
     * This method should no longer be called, instead directly call
     * FontManager.fontSupportsDefaultEncoding(Font).
     * This method will be removed once Swing is updated to no longer
     * call it.
     */

   
public static boolean fontSupportsDefaultEncoding(Font font) {
       
return FontManager.fontSupportsDefaultEncoding(font);
   
}

   
public static void useAlternateFontforJALocales() {
       
FontManager.useAlternateFontforJALocales();
   
}

   
/*
     * 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);
   
}

   
/* If (as we do on X11) need to set a platform font path,
     * then the needed path may be specified by the platform
     * specific FontConfiguration class & data file. Such platforms
     * (ie X11) need to override this method to retrieve this information
     * into suitable data structures.
     */

   
protected void getPlatformFontPathFromFontConfig() {
   
}

   
/**
     * From the DisplayChangedListener interface; called
     * when the display mode has been changed.
     */

   
public void displayChanged() {
       
// notify screens in device array to do display update stuff
       
for (GraphicsDevice gd : getScreenDevices()) {
           
if (gd instanceof DisplayChangedListener) {
               
((DisplayChangedListener) gd).displayChanged();
           
}
       
}

       
// notify SunDisplayChanger list (e.g. VolatileSurfaceManagers and
       
// SurfaceDataProxies) about the display change event
        displayChanger
.notifyListeners();
   
}

   
/**
     * Part of the DisplayChangedListener interface:
     * propagate this event to listeners
     */

   
public void paletteChanged() {
        displayChanger
.notifyPaletteChanged();
   
}

   
/*
     * ----DISPLAY CHANGE SUPPORT----
     */


   
protected SunDisplayChanger displayChanger = new SunDisplayChanger();

   
/**
     * Add a DisplayChangeListener to be notified when the display settings
     * are changed.
     */

   
public void addDisplayChangedListener(DisplayChangedListener client) {
        displayChanger
.add(client);
   
}

   
/**
     * Remove a DisplayChangeListener from Win32GraphicsEnvironment
     */

   
public void removeDisplayChangedListener(DisplayChangedListener client) {
        displayChanger
.remove(client);
   
}

   
/*
     * ----END DISPLAY CHANGE SUPPORT----
     */

}