/* * 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 javax.swing.plaf.FontUIResource; import sun.awt.AppContext; import sun.awt.FontConfiguration; import sun.awt.SunToolkit; import sun.java2d.FontSupport; import sun.util.logging.PlatformLogger; /** * 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 compositeFonts = new ConcurrentHashMap(); private ConcurrentHashMap physicalFonts = new ConcurrentHashMap(); private ConcurrentHashMap registeredFonts = new ConcurrentHashMap(); /* given a full name find the Font. Remind: there's duplication * here in that this contains the content of compositeFonts + * physicalFonts. */ private ConcurrentHashMap fullNameToFont = new ConcurrentHashMap(); /* TrueType fonts have localised names. Support searching all * of these before giving up on a name. */ private HashMap localeFullNamesToFont; private PhysicalFont defaultPhysicalFont; static boolean longAddresses; private boolean loaded1dot0Fonts = false; boolean loadedAllFonts = false; boolean loadedAllFontFiles = false; HashMap jreFontMap; HashSet 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 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(); jreLucidaFontFiles = new HashSet(); 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()) { PlatformLogger 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() { 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 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 deferredFontFiles = new ConcurrentHashMap(); private final ConcurrentHashMap initialisedFonts = new ConcurrentHashMap(); /* 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 otherFontFiles = new HashSet(); 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 fontToFileMap, HashMap fontToFamilyNameMap, HashMap> 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 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 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> 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 fileList = new ArrayList(); 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