/MRI-J/jdk/src/share/classes/sun/font/FontManager.java
Java | 3760 lines | 2342 code | 303 blank | 1115 comment | 617 complexity | 8c06dc7474fbbbebfb698d7074d32963 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, LGPL-3.0
Large files files are truncated, but you can click here to view the full file
- /*
- * Copyright 2003-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.GraphicsEnvironment;
- import java.awt.FontFormatException;
- import java.io.File;
- import java.io.FilenameFilter;
- import java.security.AccessController;
- import java.security.PrivilegedAction;
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.HashSet;
- import java.util.Hashtable;
- import java.util.Iterator;
- import java.util.Locale;
- import java.util.Map;
- import java.util.NoSuchElementException;
- import java.util.StringTokenizer;
- import java.util.TreeMap;
- import java.util.Vector;
- import java.util.concurrent.ConcurrentHashMap;
- import java.util.logging.Level;
- import java.util.logging.Logger;
- import javax.swing.plaf.FontUIResource;
- import sun.awt.AppContext;
- import sun.awt.FontConfiguration;
- import sun.awt.SunHints;
- import sun.awt.SunToolkit;
- import sun.java2d.HeadlessGraphicsEnvironment;
- import sun.java2d.SunGraphicsEnvironment;
- import java.awt.geom.GeneralPath;
- import java.awt.geom.Point2D;
- import java.awt.geom.Rectangle2D;
- import java.lang.reflect.Constructor;
- import sun.java2d.Disposer;
- /*
- * Interface between Java Fonts (java.awt.Font) and the underlying
- * font files/native font resources and the Java and native font scalers.
- */
- public final class FontManager {
- 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;
- public static final int NO_FALLBACK = 0;
- public static final int PHYSICAL_FALLBACK = 1;
- public static final int LOGICAL_FALLBACK = 2;
- public static final int QUADPATHTYPE = 1;
- public static final int CUBICPATHTYPE = 2;
- /* 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 static int lastPoolIndex = 0;
- private static int poolSize = 0;
- private static 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 static int maxCompFont = 0;
- private static CompositeFont [] compFonts = new CompositeFont[20];
- private static ConcurrentHashMap<String, CompositeFont>
- compositeFonts = new ConcurrentHashMap<String, CompositeFont>();
- private static ConcurrentHashMap<String, PhysicalFont>
- physicalFonts = new ConcurrentHashMap<String, PhysicalFont>();
- private static ConcurrentHashMap<String, PhysicalFont>
- registeredFontFiles = new ConcurrentHashMap<String, PhysicalFont>();
- /* given a full name find the Font. Remind: there's duplication
- * here in that this contains the content of compositeFonts +
- * physicalFonts.
- */
- private static ConcurrentHashMap<String, Font2D>
- fullNameToFont = new ConcurrentHashMap<String, Font2D>();
- /* TrueType fonts have localised names. Support searching all
- * of these before giving up on a name.
- */
- private static HashMap<String, TrueTypeFont> localeFullNamesToFont;
- private static PhysicalFont defaultPhysicalFont;
- /* deprecated, unsupported hack - actually invokes a bug! */
- private static boolean usePlatformFontMetrics = false;
- public static Logger logger = null;
- public static boolean logging;
- static boolean longAddresses;
- static String osName;
- static boolean useT2K;
- static boolean isWindows;
- static boolean isSolaris;
- public static boolean isSolaris8; // needed to check for JA wavedash fix.
- public static boolean isSolaris9; // needed to check for songti font usage.
- private static boolean loaded1dot0Fonts = false;
- static SunGraphicsEnvironment sgEnv;
- static boolean loadedAllFonts = false;
- static boolean loadedAllFontFiles = false;
- static TrueTypeFont eudcFont;
- static HashMap<String,String> jreFontMap;
- static HashSet<String> jreLucidaFontFiles;
- static String[] jreOtherFontFiles;
- static boolean noOtherJREFontFiles = false; // initial assumption.
- /* Used to indicate required return type from toArray(..); */
- private static String[] STR_ARRAY = new String[0];
- private static void initJREFontMap() {
- /* Key is familyname+style value as an int.
- * Value is filename containing the font.
- * If no mapping exists, it means there is no font file for the style
- * If the mapping exists but the file doesn't exist in the deferred
- * list then it means its not installed.
- * This looks like a lot of code and strings but if it saves even
- * a single file being opened at JRE start-up there's a big payoff.
- * Lucida Sans is probably the only important case as the others
- * are rarely used. Consider removing the other mappings if there's
- * no evidence they are useful in practice.
- */
- jreFontMap = new HashMap<String,String>();
- jreLucidaFontFiles = new HashSet<String>();
- if (SunGraphicsEnvironment.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 {
- if (SunGraphicsEnvironment.debugFonts) {
- logger = Logger.getLogger("sun.java2d", null);
- logging = logger.getLevel() != Level.OFF;
- }
- initJREFontMap();
- 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");
- }
- osName = System.getProperty("os.name", "unknownOS");
- isSolaris = osName.startsWith("SunOS");
- if (isSolaris) {
- String t2kStr= System.getProperty("sun.java2d.font.scaler");
- useT2K = "t2k".equals(t2kStr);
- String version = System.getProperty("os.version", "unk");
- isSolaris8 = version.equals("5.8");
- isSolaris9 = version.equals("5.9");
- } else {
- isWindows = osName.startsWith("Windows");
- if (isWindows) {
- String eudcFile =
- SunGraphicsEnvironment.eudcFontFileName;
- if (eudcFile != null) {
- try {
- eudcFont = new TrueTypeFont(eudcFile, null, 0,
- true);
- } catch (FontFormatException e) {
- }
- }
- String prop =
- System.getProperty("java2d.font.usePlatformFont");
- if (("true".equals(prop) || getPlatformFontVar())) {
- 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.");
- }
- }
- }
- return null;
- }
- });
- }
- /* Initialise ptrs used by JNI methods */
- private static native void initIDs();
- public static void addToPool(FileFont font) {
- boolean added = false;
- synchronized (fontFileCache) {
- /* use poolSize to quickly detect if there's any free slots.
- * This is a performance tweak based on the assumption that
- * if this is executed at all often, its because there are many
- * fonts being used and the pool will be full, and we will save
- * a fruitless iteration
- */
- if (poolSize < CHANNELPOOLSIZE) {
- for (int i=0; i<CHANNELPOOLSIZE; i++) {
- if (fontFileCache[i] == null) {
- fontFileCache[i] = font;
- poolSize++;
- added = true;
- break;
- }
- }
- assert added;
- } else {
- // is it possible for this to be the same font?
- assert fontFileCache[lastPoolIndex] != font;
- /* replace with new font, poolSize is unchanged. */
- fontFileCache[lastPoolIndex].close();
- fontFileCache[lastPoolIndex] = font;
- /* lastPoolIndex is updated so that the least recently opened
- * file will be closed next.
- */
- lastPoolIndex = (lastPoolIndex+1) % CHANNELPOOLSIZE;
- }
- }
- }
- /*
- * In the normal course of events, the pool of fonts can remain open
- * ready for quick access to their contents. The pool is sized so
- * that it is not an excessive consumer of system resources whilst
- * facilitating performance by providing ready access to the most
- * recently used set of font files.
- * The only reason to call removeFromPool(..) is for a Font that
- * you want to to have GC'd. Currently this would apply only to fonts
- * created with java.awt.Font.createFont(..).
- * In this case, the caller is expected to have arranged for the file
- * to be closed.
- * REMIND: consider how to know when a createFont created font should
- * be closed.
- */
- public static void removeFromPool(FileFont font) {
- synchronized (fontFileCache) {
- for (int i=0; i<CHANNELPOOLSIZE; i++) {
- if (fontFileCache[i] == font) {
- fontFileCache[i] = null;
- poolSize--;
- }
- }
- }
- }
- /**
- * 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);
- // }
- /* Revise the implementation to in fact mean "font is a composite font.
- * This ensures that Swing components will always benefit from the
- * fall back fonts
- */
- public static boolean fontSupportsDefaultEncoding(Font font) {
- return getFont2D(font) instanceof CompositeFont;
- }
- /**
- * This method is provided for internal and exclusive use by Swing.
- *
- * It may be used in conjunction with fontSupportsDefaultEncoding(Font)
- * In the event that a desktop properties font doesn't directly
- * support the default encoding, (ie because the host OS supports
- * adding support for the current locale automatically for native apps),
- * then Swing calls this method to get a font which uses the specified
- * font for the code points it covers, but also supports this locale
- * just as the standard composite fonts do.
- * Note: this will over-ride any setting where an application
- * specifies it prefers locale specific composite fonts.
- * The logic for this, is that this method is used only where the user or
- * application has specified that the native L&F be used, and that
- * we should honour that request to use the same font as native apps use.
- *
- * The behaviour of this method is to construct a new composite
- * Font object that uses the specified physical font as its first
- * component, and adds all the components of "dialog" as fall back
- * components.
- * The method currently assumes that only the size and style attributes
- * are set on the specified font. It doesn't copy the font transform or
- * other attributes because they aren't set on a font created from
- * the desktop. This will need to be fixed if use is broadened.
- *
- * Operations such as Font.deriveFont will work properly on the
- * font returned by this method for deriving a different point size.
- * Additionally it tries to support a different style by calling
- * getNewComposite() below. That also supports replacing slot zero
- * with a different physical font but that is expected to be "rare".
- * Deriving with a different style is needed because its been shown
- * that some applications try to do this for Swing FontUIResources.
- * Also operations such as new Font(font.getFontName(..), Font.PLAIN, 14);
- * will NOT yield the same result, as the new underlying CompositeFont
- * cannot be "looked up" in the font registry.
- * This returns a FontUIResource as that is the Font sub-class needed
- * by Swing.
- * Suggested usage is something like :
- * FontUIResource fuir;
- * Font desktopFont = getDesktopFont(..);
- * // NOTE even if fontSupportsDefaultEncoding returns true because
- * // you get Tahoma and are running in an English locale, you may
- * // still want to just call getCompositeFontUIResource() anyway
- * // as only then will you get fallback fonts - eg for CJK.
- * if (FontManager.fontSupportsDefaultEncoding(desktopFont)) {
- * fuir = new FontUIResource(..);
- * } else {
- * fuir = FontManager.getCompositeFontUIResource(desktopFont);
- * }
- * return fuir;
- */
- public static FontUIResource getCompositeFontUIResource(Font font) {
- FontUIResource fuir =
- new FontUIResource(font.getName(),font.getStyle(),font.getSize());
- Font2D font2D = getFont2D(font);
- if (!(font2D instanceof PhysicalFont)) {
- /* Swing should only be calling this when a font is obtained
- * from desktop properties, so should generally be a physical font,
- * an exception might be for names like "MS Serif" which are
- * automatically mapped to "Serif", so there's no need to do
- * anything special in that case. But note that suggested usage
- * is first to call fontSupportsDefaultEncoding(Font) and this
- * method should not be called if that were to return true.
- */
- return fuir;
- }
- CompositeFont dialog2D =
- (CompositeFont)findFont2D("dialog", font.getStyle(), NO_FALLBACK);
- if (dialog2D == null) { /* shouldn't happen */
- return fuir;
- }
- PhysicalFont physicalFont = (PhysicalFont)font2D;
- CompositeFont compFont = new CompositeFont(physicalFont, dialog2D);
- setFont2D(fuir, compFont.handle);
- /* marking this as a created font is needed as only created fonts
- * copy their creator's handles.
- */
- setCreatedFont(fuir);
- return fuir;
- }
- public static 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;
- }
- public static native void setFont2D(Font font, Font2DHandle font2DHandle);
- private static native boolean isCreatedFont(Font font);
- private static native void setCreatedFont(Font font);
- public static 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);
- 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.
- */
- public static void registerCompositeFont(String compositeName,
- String[] componentFileNames,
- String[] componentNames,
- int numMetricsSlots,
- int[] exclusionRanges,
- int[] exclusionMaxIndex,
- boolean defer,
- ConcurrentHashMap<String, Font2D>
- altNameCache) {
- CompositeFont cf = new CompositeFont(compositeName,
- componentFileNames,
- componentNames,
- numMetricsSlots,
- exclusionRanges,
- exclusionMaxIndex, defer);
- /* 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 static void addCompositeToFontList(CompositeFont f, int rank) {
- if (logging) {
- logger.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 static 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 (logging) {
- logger.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(
- SunGraphicsEnvironment.jreFontDirName)) {
- if (logging) {
- logger.warning("Unexpected attempt to replace a JRE " +
- " font " + fontName + " from " +
- oldFont.platName +
- " with " + newFont.platName);
- }
- return oldFont;
- }
- if (logging) {
- logger.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 static 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;
- }
- public static 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 static final ConcurrentHashMap<String, FontRegistrationInfo>
- deferredFontFiles =
- new ConcurrentHashMap<String, FontRegistrationInfo>();
- private static final ConcurrentHashMap<String, Font2DHandle>
- initialisedFonts = new ConcurrentHashMap<String, Font2DHandle>();
- /* Remind: possibly enhance initialiseDeferredFonts() to be
- * optionally given a name and a style and it could stop when it
- * finds that font - but this would be a problem if two of the
- * fonts reference the same font face name (cf the Solaris
- * euro fonts).
- */
- public static synchronized void initialiseDeferredFonts() {
- for (String fileName : deferredFontFiles.keySet()) {
- initialiseDeferredFont(fileName);
- }
- }
- public static synchronized void registerDeferredJREFonts(String jreDir) {
- for (FontRegistrationInfo info : deferredFontFiles.values()) {
- if (info.fontFilePath != null &&
- info.fontFilePath.startsWith(jreDir)) {
- initialiseDeferredFont(info.fontFilePath);
- }
- }
- }
- /* 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.
- */
- private static PhysicalFont findJREDeferredFont(String name, int style) {
- PhysicalFont physicalFont;
- String nameAndStyle = name.toLowerCase(Locale.ENGLISH) + style;
- String fileName = jreFontMap.get(nameAndStyle);
- if (fileName != null) {
- initSGEnv(); /* ensure jreFontDirName is initialised */
- fileName = SunGraphicsEnvironment.jreFontDirName +
- File.separator + fileName;
- if (deferredFontFiles.get(fileName) != null) {
- physicalFont = initialiseDeferredFont(fileName);
- if (physicalFont != null &&
- (physicalFont.getFontName(null).equalsIgnoreCase(name) ||
- physicalFont.getFamilyName(null).equalsIgnoreCase(name))
- && physicalFont.style == style) {
- return physicalFont;
- }
- }
- }
- /* Iterate over the deferred font files looking for any in the
- * jre directory that we didn't recognise, open each of these.
- * In almost all installations this will quickly fall through
- * because only the Lucidas will be present and jreOtherFontFiles
- * will be empty.
- * noOtherJREFontFiles is used so we can skip this block as soon
- * as its determined that its not needed - almost always after the
- * very first time through.
- */
- if (noOtherJREFontFiles) {
- return null;
- }
- synchronized (jreLucidaFontFiles) {
- if (jreOtherFontFiles == null) {
- HashSet<String> otherFontFiles = new HashSet<String>();
- for (String deferredFile : deferredFontFiles.keySet()) {
- File file = new File(deferredFile);
- String dir = file.getParent();
- String fname = file.getName();
- /* skip names which aren't absolute, aren't in the JRE
- * directory, or are known Lucida fonts.
- */
- if (dir == null ||
- !dir.equals(SunGraphicsEnvironment.jreFontDirName) ||
- jreLucidaFontFiles.contains(fname)) {
- continue;
- }
- otherFontFiles.add(deferredFile);
- }
- jreOtherFontFiles = otherFontFiles.toArray(STR_ARRAY);
- if (jreOtherFontFiles.length == 0) {
- noOtherJREFontFiles = true;
- }
- }
- for (int i=0; i<jreOtherFontFiles.length;i++) {
- fileName = jreOtherFontFiles[i];
- if (fileName == null) {
- continue;
- }
- jreOtherFontFiles[i] = null;
- physicalFont = initialiseDeferredFont(fileName);
- if (physicalFont != null &&
- (physicalFont.getFontName(null).equalsIgnoreCase(name) ||
- physicalFont.getFamilyName(null).equalsIgnoreCase(name))
- && physicalFont.style == style) {
- return physicalFont;
- }
- }
- }
- return null;
- }
- /* This skips JRE installed fonts. */
- private static PhysicalFont findOtherDeferredFont(String name, int style) {
- for (String fileName : deferredFontFiles.keySet()) {
- File file = new File(fileName);
- String dir = file.getParent();
- String fname = file.getName();
- if (dir != null &&
- dir.equals(SunGraphicsEnvironment.jreFontDirName) &&
- jreLucidaFontFiles.contains(fname)) {
- continue;
- }
- PhysicalFont physicalFont = initialiseDeferredFont(fileName);
- if (physicalFont != null &&
- (physicalFont.getFontName(null).equalsIgnoreCase(name) ||
- physicalFont.getFamilyName(null).equalsIgnoreCase(name)) &&
- physicalFont.style == style) {
- return physicalFont;
- }
- }
- return null;
- }
- private static PhysicalFont findDeferredFont(String name, int style) {
- PhysicalFont physicalFont = findJREDeferredFont(name, style);
- if (physicalFont != null) {
- return physicalFont;
- } else {
- return findOtherDeferredFont(name, style);
- }
- }
- public static void registerDeferredFont(String fileNameKey,
- String fullPathName,
- String[] nativeNames,
- int fontFormat,
- boolean useJavaRasterizer,
- int fontRank) {
- FontRegistrationInfo regInfo =
- new FontRegistrationInfo(fullPathName, nativeNames, fontFormat,
- useJavaRasterizer, fontRank);
- deferredFontFiles.put(fileNameKey, regInfo);
- }
- public static synchronized
- PhysicalFont initialiseDeferredFont(String fileNameKey) {
- if (fileNameKey == null) {
- return null;
- }
- if (logging) {
- logger.info("Opening deferred font file " + fileNameKey);
- }
- PhysicalFont physicalFont;
- FontRegistrationInfo regInfo = deferredFontFiles.get(fileNameKey);
- if (regInfo != null) {
- deferredFontFiles.remove(fileNameKey);
- physicalFont = registerFontFile(regInfo.fontFilePath,
- regInfo.nativeNames,
- regInfo.fontFormat,
- regInfo.javaRasterizer,
- regInfo.fontRank);
- if (physicalFont != null) {
- /* Store the handle, so that if a font is bad, we
- * retrieve the substituted font.
- */
- initialisedFonts.put(fileNameKey, physicalFont.handle);
- } else {
- initialisedFonts.put(fileNameKey,
- getDefaultPhysicalFont().handle);
- }
- } else {
- Font2DHandle handle = initialisedFonts.get(fileNameKey);
- if (handle == null) {
- /* Probably shouldn't happen, but just in case */
- physicalFont = getDefaultPhysicalFont();
- } else {
- physicalFont = (PhysicalFont)(handle.font2D);
- }
- }
- return physicalFont;
- }
- /* Note that the return value from this method is not always
- * derived from this file, and may be null. See addToFontList for
- * some explanation of this.
- */
- public static PhysicalFont registerFontFile(String fileName,
- String[] nativeNames,
- int fontFormat,
- boolean useJavaRasterizer,
- int fontRank) {
- PhysicalFont regFont = registeredFontFiles.get(fileName);
- if (regFont != null) {
- return regFont;
- }
- PhysicalFont physicalFont = null;
- try {
- String name;
- switch (fontFormat) {
- case FontManager.FONTFORMAT_TRUETYPE:
- int fn = 0;
- TrueTypeFont ttf;
- do {
- ttf = new TrueTypeFont(fileName, nativeNames, fn++,
- useJavaRasterizer);
- PhysicalFont pf = addToFontList(ttf, fontRank);
- if (physicalFont == null) {
- physicalFont = pf;
- }
- }
- while (fn < ttf.getFontCount());
- break;
- case FontManager.FONTFORMAT_TYPE1:
- Type1Font t1f = new Type1Font(fileName, nativeNames);
- physicalFont = addToFontList(t1f, fontRank);
- break;
- case FontManager.FONTFORMAT_NATIVE:
- NativeFont nf = new NativeFont(fileName, false);
- physicalFont = addToFontList(nf, fontRank);
- default:
- }
- if (logging) {
- logger.info("Registered file " + fileName + " as font " +
- physicalFont + " rank=" + fontRank);
- }
- } catch (FontFormatException ffe) {
- if (logging) {
- logger.warning("Unusable font: " +
- fileName + " " + ffe.toString());
- }
- }
- if (physicalFont != null &&
- fontFormat != FontManager.FONTFORMAT_NATIVE) {
- registeredFontFiles.put(fileName, physicalFont);
- }
- return physicalFont;
- }
- public static void registerFonts(String[] fileNames,
- String[][] nativeNames,
- int fontCount,
- int fontFormat,
- boolean useJavaRasterizer,
- int fontRank, boolean defer) {
- for (int i=0; i < fontCount; i++) {
- if (defer) {
- registerDeferredFont(fileNames[i],fileNames[i], nativeNames[i],
- fontFormat, useJavaRasterizer, fontRank);
- } else {
- registerFontFile(fileNames[i], nativeNames[i],
- fontFormat, useJavaRasterizer, fontRank);
- }
- }
- }
- /*
- * This is the Physical font used when some other font on the system
- * can't be located. There has to be at least one font or the font
- * system is not useful and the graphics environment cannot sustain
- * the Java platform.
- */
- public static PhysicalFont getDefaultPhysicalFont() {
- if (defaultPhysicalFont == null) {
- /* findFont2D will load all fonts before giving up the search.
- * If the JRE Lucida isn't found (eg because the JRE fonts
- * directory is missing), it could find another version of Lucida
- * from the host system. This is OK because at that point we are
- * trying to gracefully handle/recover from a system
- * misconfiguration and this is probably a reasonable substitution.
- */
- defaultPhysicalFont = (PhysicalFont)
- findFont2D("Lucida Sans Regular", Font.PLAIN, NO_FALLBACK);
- if (defaultPhysicalFont == null) {
- defaultPhysicalFont = (PhysicalFont)
- findFont2D("Arial", Font.PLAIN, NO_FALLBACK);
- }
- if (defaultPhysicalFont == null) {
- /* Because of the findFont2D call above, if we reach here, we
- * know all fonts have already been loaded, just accept any
- * match at this point. If this fails we are in real trouble
- * and I don't know how to recover from there being absolutely
- * no fonts anywhere on the system.
- */
- Iterator i = physicalFonts.values().iterator();
- if (i.hasNext()) {
- defaultPhysicalFont = (PhysicalFont)i.next();
- } else {
- throw new Error("Probable fatal error:No fonts found.");
- }
- }
- }
- return defaultPhysicalFont;
- }
- public static CompositeFont getDefaultLogicalFont(int style) {
- return (CompositeFont)findFont2D("dialog", style, NO_FALLBACK);
- }
- /*
- * return String representation of style prepended with "."
- * This is useful for performance to avoid unnecessary string operations.
- */
- private static String dotStyleStr(int num) {
- switch(num){
- case Font.BOLD:
- return ".bold";
- case Font.ITALIC:
- return ".italic";
- case Font.ITALIC | Font.BOLD:
- return ".bolditalic";
- default:
- return ".plain";
- }
- }
- static void initSGEnv() {
- if (sgEnv == null) {
- GraphicsEnvironment ge =
- GraphicsEnvironment.getLocalGraphicsEnvironment();
- if (ge instanceof HeadlessGraphicsEnvironment) {
- HeadlessGraphicsEnvironment hgEnv =
- (HeadlessGraphicsEnvironment)ge;
- sgEnv = (SunGraphicsEnvironment)
- hgEnv.getSunGraphicsEnvironment();
- } else {
- sgEnv = (SunGraphicsEnvironment)ge;
- }
- }
- }
- /* This is implemented only on windows and is called from code that
- * executes only on windows. This is…
Large files files are truncated, but you can click here to view the full file