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); fileCloser.setContextClassLoader(null); 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.isLogging()) { FontUtilities.getLogger() .info("Entered 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() { if (FontUtilities.isLogging()) { FontUtilities.getLogger().info("Entered 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 (FontUtilities.isLogging()) { FontUtilities.getLogger() .info("Entered 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".Lines 2501–3000 of 3912. Previous lines Load more
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); fileCloser.setContextClassLoader(null); 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.isLogging()) { FontUtilities.getLogger() .info("Entered 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() { if (FontUtilities.isLogging()) { FontUtilities.getLogger().info("Entered 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 (FontUtilities.isLogging()) { FontUtilities.getLogger() .info("Entered 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".