Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
META-INF.modules.java.desktop.classes.sun.font.SunFontManager Maven / Gradle / Ivy
/*
* Copyright (c) 2008, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.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.List;
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.FontConfiguration;
import sun.awt.SunToolkit;
import sun.awt.util.ThreadGroupUtils;
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) ||
name.startsWith(".otf", offset) ||
name.startsWith(".OTF", 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) ||
name.startsWith(".otf", offset) ||
name.startsWith(".OTF", 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_TTC = 2;
public static final int FONTFORMAT_COMPOSITE = 3;
public static final int FONTFORMAT_NATIVE = 4;
/* 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.
*/
// MACOSX begin -- need to access these in subclass
protected static final int CHANNELPOOLSIZE = 20;
protected FileFont[] fontFileCache = new FileFont[CHANNELPOOLSIZE];
// MACOSX end
private int lastPoolIndex = 0;
/* 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.
*/
// MACOSX begin -- need to access this in subclass
protected ConcurrentHashMap
fullNameToFont = new ConcurrentHashMap();
// MACOSX end
/* 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;
String[] jreOtherFontFiles;
boolean noOtherJREFontFiles = false; // initial assumption.
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;
}
/* After we reach MAXSOFTREFCNT, use weak refs for created fonts.
* This means that a small number of created fonts as used in a UI app
* will not be eagerly collected, but an app that create many will
* have them collected more frequently to reclaim storage.
*/
private static int maxSoftRefCnt = 10;
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";
maxSoftRefCnt =
Integer.getInteger("sun.java2d.font.maxSoftRefs", 10);
return null;
}
});
}
/**
* If the module image layout changes the location of JDK fonts,
* this will be updated to reflect that.
*/
public static final String getJDKFontDir() {
return jreFontDirName;
}
public TrueTypeFont getEUDCFont() {
// Overridden in Windows.
return null;
}
/* Initialise ptrs used by JNI methods */
private static native void initIDs();
@SuppressWarnings("unchecked")
protected SunFontManager() {
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 JDK 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();
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.");
}
}
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 =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
*/
// MACOSX begin -- need to access this in subclass
protected PhysicalFont addToFontList(PhysicalFont f, int rank) {
// MACOSX end
String fontName = f.fullName;
String familyName = f.familyName;
if (fontName == null || fontName.isEmpty()) {
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 {
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 {
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);
}
PhysicalFont findJREDeferredFont(String name, int style) {
/* 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 jreOtherFontFiles will be empty.
* noOtherJREFontFiles is used so we can skip this block as soon
* as its determined that it's not needed - almost always after the
* very first time through.
*/
if (noOtherJREFontFiles) {
return null;
}
synchronized (jreFontDirName) {
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)) {
continue;
}
otherFontFiles.add(deferredFile);
}
jreOtherFontFiles = otherFontFiles.toArray(STR_ARRAY);
if (jreOtherFontFiles.length == 0) {
noOtherJREFontFiles = true;
}
}
for (int i=0; i i = physicalFonts.values().iterator();
if (i.hasNext()) {
defaultPhysicalFont = i.next();
} else {
throw new Error("Probable fatal error:No fonts found.");
}
}
}
return defaultPhysicalFont;
}
public Font2D getDefaultLogicalFont(int style) {
return findFont2D("dialog", style, NO_FALLBACK);
}
/*
* return String representation of style prepended with "."
* This is useful for performance to avoid unnecessary string operations.
*/
private static String dotStyleStr(int num) {
switch(num){
case Font.BOLD:
return ".bold";
case Font.ITALIC:
return ".italic";
case Font.ITALIC | Font.BOLD:
return ".bolditalic";
default:
return ".plain";
}
}
/* This is implemented only on windows and is called from code that
* executes only on windows. This isn't pretty but its not a precedent
* in this file. This very probably should be cleaned up at some point.
*/
protected void
populateFontFileNameMap(HashMap 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 unmappedFontNames = null;
for (String font : fontToFamilyNameMap.keySet()) {
String file = fontToFileMap.get(font);
if (file == null) {
if (font.indexOf(" ") > 0) {
String newName = font.replaceFirst(" ", " ");
file = fontToFileMap.get(newName);
/* If this name exists and isn't for a valid name
* replace the mapping to the file with this font
*/
if (file != null &&
!fontToFamilyNameMap.containsKey(newName)) {
fontToFileMap.remove(newName);
fontToFileMap.put(font, file);
}
} else if (font.equals("marlett")) {
fontToFileMap.put(font, "marlett.ttf");
} else if (font.equals("david")) {
file = fontToFileMap.get("david regular");
if (file != null) {
fontToFileMap.remove("david regular");
fontToFileMap.put("david", file);
}
} else {
if (unmappedFontNames == null) {
unmappedFontNames = new ArrayList();
}
unmappedFontNames.add(font);
}
}
}
if (unmappedFontNames != null) {
HashSet unmappedFontFiles = new HashSet();
/* Every font key in fontToFileMap ought to correspond to a
* font key in fontToFamilyNameMap. Entries that don't seem
* to correspond are likely fonts that were named differently
* by GDI than in the registry. One known cause of this is when
* Windows has had its regional settings changed so that from
* GDI we get a localised (eg Chinese or Japanese) name for the
* font, but the registry retains the English version of the name
* that corresponded to the "install" locale for windows.
* Since we are in this code block because there are unmapped
* font names, we can look to find unused font->file mappings
* and then open the files to read the names. We don't generally
* want to open font files, as its a performance hit, but this
* occurs only for a small number of fonts on specific system
* configs - ie is believed that a "true" Japanese windows would
* have JA names in the registry too.
* Clone fontToFileMap and remove from the clone all keys which
* match a fontToFamilyNameMap key. What remains maps to the
* files we want to open to find the fonts GDI returned.
* A font in such a file is added to the fontToFileMap after
* checking its one of the unmappedFontNames we are looking for.
* The original name that didn't map is removed from fontToFileMap
* so essentially this "fixes up" fontToFileMap to use the same
* name as GDI.
* Also note that typically the fonts for which this occurs in
* CJK locales are TTC fonts and not all fonts in a TTC may have
* localised names. Eg MSGOTHIC.TTC contains 3 fonts and one of
* them "MS UI Gothic" has no JA name whereas the other two do.
* So not every font in these files is unmapped or new.
*/
@SuppressWarnings("unchecked")
HashMap ffmapCopy =
(HashMap)(fontToFileMap.clone());
for (String key : fontToFamilyNameMap.keySet()) {
ffmapCopy.remove(key);
}
for (String key : ffmapCopy.keySet()) {
unmappedFontFiles.add(ffmapCopy.get(key));
fontToFileMap.remove(key);
}
resolveFontFiles(unmappedFontFiles, unmappedFontNames);
/* If there are still unmapped font names, this means there's
* something that wasn't in the registry. We need to get all
* the font files directly and look at the ones that weren't
* found in the registry.
*/
if (unmappedFontNames.size() > 0) {
/* getFontFilesFromPath() returns all lower case names.
* To compare we also need lower case
* versions of the names from the registry.
*/
ArrayList registryFiles = new ArrayList();
for (String regFile : fontToFileMap.values()) {
registryFiles.add(regFile.toLowerCase());
}
/* We don't look for Type1 files here as windows will
* not enumerate these, so aren't useful in reconciling
* GDI's unmapped files. We do find these later when
* we enumerate all fonts.
*/
for (String pathFile : getFontFilesFromPath(true)) {
if (!registryFiles.contains(pathFile)) {
unmappedFontFiles.add(pathFile);
}
}
resolveFontFiles(unmappedFontFiles, unmappedFontNames);
}
/* remove from the set of names that will be returned to the
* user any fonts that can't be mapped to files.
*/
if (unmappedFontNames.size() > 0) {
int sz = unmappedFontNames.size();
for (int i=0; i family = familyToFontListMap.get(familyName);
if (family != null) {
if (family.size() <= 1) {
familyToFontListMap.remove(familyName);
}
}
}
fontToFamilyNameMap.remove(name);
if (FontUtilities.isLogging()) {
FontUtilities.getLogger()
.info("No file for font:" + name);
}
}
}
}
}
/**
* In some cases windows may have fonts in the fonts folder that
* don't show up in the registry or in the GDI calls to enumerate fonts.
* The only way to find these is to list the directory. We invoke this
* only in getAllFonts/Families, so most searches for a specific
* font that is satisfied by the GDI/registry calls don't take the
* additional hit of listing the directory. This hit is small enough
* that its not significant in these 'enumerate all the fonts' cases.
* The basic approach is to cross-reference the files windows found
* with the ones in the directory listing approach, and for each
* in the latter list that is missing from the former list, register it.
*/
private synchronized void checkForUnreferencedFontFiles() {
if (haveCheckedUnreferencedFontFiles) {
return;
}
haveCheckedUnreferencedFontFiles = true;
if (!FontUtilities.isWindows) {
return;
}
/* getFontFilesFromPath() returns all lower case names.
* To compare we also need lower case
* versions of the names from the registry.
*/
ArrayList registryFiles = new ArrayList();
for (String regFile : fontToFileMap.values()) {
registryFiles.add(regFile.toLowerCase());
}
/* To avoid any issues with concurrent modification, create
* copies of the existing maps, add the new fonts into these
* and then replace the references to the old ones with the
* new maps. ConcurrentHashmap is another option but its a lot
* more changes and with this exception, these maps are intended
* to be static.
*/
HashMap fontToFileMap2 = null;
HashMap fontToFamilyNameMap2 = null;
HashMap> familyToFontListMap2 = null;;
for (String pathFile : getFontFilesFromPath(false)) {
if (!registryFiles.contains(pathFile)) {
if (FontUtilities.isLogging()) {
FontUtilities.getLogger()
.info("Found non-registry file : " + pathFile);
}
PhysicalFont f = registerFontFile(getPathName(pathFile));
if (f == null) {
continue;
}
if (fontToFileMap2 == null) {
fontToFileMap2 = new HashMap(fontToFileMap);
fontToFamilyNameMap2 =
new HashMap(fontToFamilyNameMap);
familyToFontListMap2 = new
HashMap>(familyToFontListMap);
}
String fontName = f.getFontName(null);
String family = f.getFamilyName(null);
String familyLC = family.toLowerCase();
fontToFamilyNameMap2.put(fontName, family);
fontToFileMap2.put(fontName, pathFile);
ArrayList fonts = familyToFontListMap2.get(familyLC);
if (fonts == null) {
fonts = new ArrayList();
} else {
fonts = new ArrayList(fonts);
}
fonts.add(fontName);
familyToFontListMap2.put(familyLC, fonts);
}
}
if (fontToFileMap2 != null) {
fontToFileMap = fontToFileMap2;
familyToFontListMap = familyToFontListMap2;
fontToFamilyNameMap = fontToFamilyNameMap2;
}
}
private void resolveFontFiles(HashSet unmappedFiles,
ArrayList unmappedFonts) {
Locale l = SunToolkit.getStartupLocale();
for (String file : unmappedFiles) {
try {
int fn = 0;
TrueTypeFont ttf;
String fullPath = getPathName(file);
if (FontUtilities.isLogging()) {
FontUtilities.getLogger()
.info("Trying to resolve file " + fullPath);
}
do {
ttf = new TrueTypeFont(fullPath, null, fn++, false);
// prefer the font's locale name.
String fontName = ttf.getFontName(l).toLowerCase();
if (unmappedFonts.contains(fontName)) {
fontToFileMap.put(fontName, file);
unmappedFonts.remove(fontName);
if (FontUtilities.isLogging()) {
FontUtilities.getLogger()
.info("Resolved absent registry entry for " +
fontName + " located in " + fullPath);
}
}
}
while (fn < ttf.getFontCount());
} catch (Exception e) {
}
}
}
/* Hardwire the English names and expected file names of fonts
* commonly used at start up. Avoiding until later even the small
* cost of calling platform APIs to locate these can help.
* The code that registers these fonts needs to "bail" if any
* of the files do not exist, so it will verify the existence of
* all non-null file names first.
* They are added in to a map with nominally the first
* word in the name of the family as the key. In all the cases
* we are using the family name is a single word, and as is
* more or less required the family name is the initial sequence
* in a full name. So lookup first finds the matching description,
* then registers the whole family, returning the right font.
*/
public static class FamilyDescription {
public String familyName;
public String plainFullName;
public String boldFullName;
public String italicFullName;
public String boldItalicFullName;
public String plainFileName;
public String boldFileName;
public String italicFileName;
public String boldItalicFileName;
}
static HashMap platformFontMap;
/**
* default implementation does nothing.
*/
public HashMap populateHardcodedFileNameMap() {
return new HashMap(0);
}
Font2D findFontFromPlatformMap(String lcName, int style) {
if (platformFontMap == null) {
platformFontMap = populateHardcodedFileNameMap();
}
if (platformFontMap == null || platformFontMap.size() == 0) {
return null;
}
int spaceIndex = lcName.indexOf(' ');
String firstWord = lcName;
if (spaceIndex > 0) {
firstWord = lcName.substring(0, spaceIndex);
}
FamilyDescription fd = platformFontMap.get(firstWord);
if (fd == null) {
return null;
}
/* Once we've established that its at least the first word,
* we need to dig deeper to make sure its a match for either
* a full name, or the family name, to make sure its not
* a request for some other font that just happens to start
* with the same first word.
*/
int styleIndex = -1;
if (lcName.equalsIgnoreCase(fd.plainFullName)) {
styleIndex = 0;
} else if (lcName.equalsIgnoreCase(fd.boldFullName)) {
styleIndex = 1;
} else if (lcName.equalsIgnoreCase(fd.italicFullName)) {
styleIndex = 2;
} else if (lcName.equalsIgnoreCase(fd.boldItalicFullName)) {
styleIndex = 3;
}
if (styleIndex == -1 && !lcName.equalsIgnoreCase(fd.familyName)) {
return null;
}
String plainFile = null, boldFile = null,
italicFile = null, boldItalicFile = null;
boolean failure = false;
/* In a terminal server config, its possible that getPathName()
* will return null, if the file doesn't exist, hence the null
* checks on return. But in the normal client config we need to
* follow this up with a check to see if all the files really
* exist for the non-null paths.
*/
getPlatformFontDirs(noType1Font);
if (fd.plainFileName != null) {
plainFile = getPathName(fd.plainFileName);
if (plainFile == null) {
failure = true;
}
}
if (fd.boldFileName != null) {
boldFile = getPathName(fd.boldFileName);
if (boldFile == null) {
failure = true;
}
}
if (fd.italicFileName != null) {
italicFile = getPathName(fd.italicFileName);
if (italicFile == null) {
failure = true;
}
}
if (fd.boldItalicFileName != null) {
boldItalicFile = getPathName(fd.boldItalicFileName);
if (boldItalicFile == null) {
failure = true;
}
}
if (failure) {
if (FontUtilities.isLogging()) {
FontUtilities.getLogger().
info("Hardcoded file missing looking for " + lcName);
}
platformFontMap.remove(firstWord);
return null;
}
/* Some of these may be null,as not all styles have to exist */
final String[] files = {
plainFile, boldFile, italicFile, boldItalicFile } ;
failure = java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Boolean run() {
for (int i=0; i 0 && style != font.style) {
style |= font.style;
font = fontFamily.getFont(style);
if (font == null) {
font = fontFamily.getClosestStyle(style);
}
}
}
return font;
}
private synchronized HashMap getFullNameToFileMap() {
if (fontToFileMap == null) {
pathDirs = getPlatformFontDirs(noType1Font);
fontToFileMap = new HashMap(100);
fontToFamilyNameMap = new HashMap(100);
familyToFontListMap = new HashMap>(50);
populateFontFileNameMap(fontToFileMap,
fontToFamilyNameMap,
familyToFontListMap,
Locale.ENGLISH);
if (FontUtilities.isWindows) {
resolveWindowsFonts();
}
if (FontUtilities.isLogging()) {
logPlatformFontInfo();
}
}
return fontToFileMap;
}
private void logPlatformFontInfo() {
PlatformLogger logger = FontUtilities.getLogger();
for (int i=0; i< pathDirs.length;i++) {
logger.info("fontdir="+pathDirs[i]);
}
for (String keyName : fontToFileMap.keySet()) {
logger.info("font="+keyName+" file="+ fontToFileMap.get(keyName));
}
for (String keyName : fontToFamilyNameMap.keySet()) {
logger.info("font="+keyName+" family="+
fontToFamilyNameMap.get(keyName));
}
for (String keyName : familyToFontListMap.keySet()) {
logger.info("family="+keyName+ " fonts="+
familyToFontListMap.get(keyName));
}
}
/* Note this return list excludes logical fonts and JRE fonts */
protected String[] getFontNamesFromPlatform() {
if (getFullNameToFileMap().size() == 0) {
return null;
}
checkForUnreferencedFontFiles();
/* This odd code with TreeMap is used to preserve a historical
* behaviour wrt the sorting order .. */
ArrayList fontNames = new ArrayList();
for (ArrayList a : familyToFontListMap.values()) {
for (String s : a) {
fontNames.add(s);
}
}
return fontNames.toArray(STR_ARRAY);
}
public boolean gotFontsFromPlatform() {
return getFullNameToFileMap().size() != 0;
}
public String getFileNameForFontName(String fontName) {
String fontNameLC = fontName.toLowerCase(Locale.ENGLISH);
return fontToFileMap.get(fontNameLC);
}
private PhysicalFont registerFontFile(String file) {
if (new File(file).isAbsolute() &&
!registeredFonts.containsKey(file)) {
int fontFormat = FONTFORMAT_NONE;
int fontRank = Font2D.UNKNOWN_RANK;
if (ttFilter.accept(null, file)) {
fontFormat = FONTFORMAT_TRUETYPE;
fontRank = Font2D.TTF_RANK;
} else if
(t1Filter.accept(null, file)) {
fontFormat = FONTFORMAT_TYPE1;
fontRank = Font2D.TYPE1_RANK;
}
if (fontFormat == FONTFORMAT_NONE) {
return null;
}
return registerFontFile(file, null, fontFormat, false, fontRank);
}
return null;
}
/* Used to register any font files that are found by platform APIs
* that weren't previously found in the standard font locations.
* the isAbsolute() check is needed since that's whats stored in the
* set, and on windows, the fonts in the system font directory that
* are in the fontToFileMap are just basenames. We don't want to try
* to register those again, but we do want to register other registry
* installed fonts.
*/
protected void registerOtherFontFiles(HashSet registeredFontFiles) {
if (getFullNameToFileMap().size() == 0) {
return;
}
for (String file : fontToFileMap.values()) {
registerFontFile(file);
}
}
public boolean
getFamilyNamesFromPlatform(TreeMap familyNames,
Locale requestedLocale) {
if (getFullNameToFileMap().size() == 0) {
return false;
}
checkForUnreferencedFontFiles();
for (String name : fontToFamilyNameMap.values()) {
familyNames.put(name.toLowerCase(requestedLocale), name);
}
return true;
}
/* Path may be absolute or a base file name relative to one of
* the platform font directories
*/
private String getPathName(final String s) {
File f = new File(s);
if (f.isAbsolute()) {
return s;
} else if (pathDirs.length==1) {
return pathDirs[0] + File.separator + s;
} else {
String path = java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public String run() {
for (int p=0; p family = null;
String fontFile = null;
String familyName = fontToFamilyNameMap.get(lcName);
if (familyName != null) {
fontFile = fontToFileMap.get(lcName);
family = familyToFontListMap.get
(familyName.toLowerCase(Locale.ENGLISH));
} else {
family = familyToFontListMap.get(lcName); // is lcName is a family?
if (family != null && family.size() > 0) {
String lcFontName = family.get(0).toLowerCase(Locale.ENGLISH);
if (lcFontName != null) {
familyName = fontToFamilyNameMap.get(lcFontName);
}
}
}
if (family == null || familyName == null) {
return null;
}
String [] fontList = family.toArray(STR_ARRAY);
if (fontList.length == 0) {
return null;
}
/* first check that for every font in this family we can find
* a font file. The specific reason for doing this is that
* in at least one case on Windows a font has the face name "David"
* but the registry entry is "David Regular". That is the "unique"
* name of the font but in other cases the registry contains the
* "full" name. See the specifications of name ids 3 and 4 in the
* TrueType 'name' table.
* In general this could cause a problem that we fail to register
* if we all members of a family that we may end up mapping to
* the wrong font member: eg return Bold when Plain is needed.
*/
for (int f=0;f fontNameCache =
new ConcurrentHashMap();
/*
* The client supplies a name and a style.
* The name could be a family name, or a full name.
* A font may exist with the specified style, or it may
* exist only in some other style. For non-native fonts the scaler
* may be able to emulate the required style.
*/
public Font2D findFont2D(String name, int style, int fallback) {
String lowerCaseName = name.toLowerCase(Locale.ENGLISH);
String mapName = lowerCaseName + dotStyleStr(style);
/* If preferLocaleFonts() or preferProportionalFonts() has been
* called we may be using an alternate set of composite fonts in this
* app context. The presence of a pre-built name map indicates whether
* this is so, and gives access to the alternate composite for the
* name.
*/
Font2D font = fontNameCache.get(mapName);
if (font != null) {
return font;
}
if (FontUtilities.isLogging()) {
FontUtilities.getLogger().info("Search for font: " + name);
}
// The check below is just so that the bitmap fonts being set by
// AWT and Swing thru the desktop properties do not trigger the
// the load fonts case. The two bitmap fonts are now mapped to
// appropriate equivalents for serif and sansserif.
// Note that the cost of this comparison is only for the first
// call until the map is filled.
if (FontUtilities.isWindows) {
if (lowerCaseName.equals("ms sans serif")) {
name = "sansserif";
} else if (lowerCaseName.equals("ms serif")) {
name = "serif";
}
}
/* This isn't intended to support a client passing in the
* string default, but if a client passes in null for the name
* the java.awt.Font class internally substitutes this name.
* So we need to recognise it here to prevent a loadFonts
* on the unrecognised name. The only potential problem with
* this is it would hide any real font called "default"!
* But that seems like a potential problem we can ignore for now.
*/
if (lowerCaseName.equals("default")) {
name = "dialog";
}
/* First see if its a family name. */
FontFamily family = FontFamily.getFamily(name);
if (family != null) {
font = family.getFontWithExactStyleMatch(style);
if (font == null) {
font = findDeferredFont(name, style);
}
if (font == null) {
font = findFontFromPlatform(lowerCaseName, style);
}
if (font == null) {
font = family.getFont(style);
}
if (font == null) {
font = family.getClosestStyle(style);
}
if (font != null) {
fontNameCache.put(mapName, font);
return font;
}
}
/* If it wasn't a family name, it should be a full name of
* either a composite, or a physical font
*/
font = fullNameToFont.get(lowerCaseName);
if (font != null) {
/* Check that the requested style matches the matched font's style.
* But also match style automatically if the requested style is
* "plain". This because the existing behaviour is that the fonts
* listed via getAllFonts etc always list their style as PLAIN.
* This does lead to non-commutative behaviours where you might
* start with "Lucida Sans Regular" and ask for a BOLD version
* and get "Lucida Sans DemiBold" but if you ask for the PLAIN
* style of "Lucida Sans DemiBold" you get "Lucida Sans DemiBold".
* This consistent however with what happens if you have a bold
* version of a font and no plain version exists - alg. styling
* doesn't "unbolden" the font.
*/
if (font.style == style || style == Font.PLAIN) {
fontNameCache.put(mapName, font);
return font;
} else {
/* If it was a full name like "Lucida Sans Regular", but
* the style requested is "bold", then we want to see if
* there's the appropriate match against another font in
* that family before trying to load all fonts, or applying a
* algorithmic styling
*/
family = FontFamily.getFamily(font.getFamilyName(null));
if (family != null) {
Font2D familyFont = family.getFont(style|font.style);
/* We exactly matched the requested style, use it! */
if (familyFont != null) {
fontNameCache.put(mapName, familyFont);
return familyFont;
} else {
/* This next call is designed to support the case
* where bold italic is requested, and if we must
* style, then base it on either bold or italic -
* not on plain!
*/
familyFont = family.getClosestStyle(style|font.style);
if (familyFont != null) {
/* The next check is perhaps one
* that shouldn't be done. ie if we get this
* far we have probably as close a match as we
* are going to get. We could load all fonts to
* see if somehow some parts of the family are
* loaded but not all of it.
*/
if (familyFont.canDoStyle(style|font.style)) {
fontNameCache.put(mapName, familyFont);
return familyFont;
}
}
}
}
}
}
if (FontUtilities.isWindows) {
font = findFontFromPlatformMap(lowerCaseName, style);
if (FontUtilities.isLogging()) {
FontUtilities.getLogger()
.info("findFontFromPlatformMap returned " + font);
}
if (font != null) {
fontNameCache.put(mapName, font);
return font;
}
/* Don't want Windows to return a font from C:\Windows\Fonts
* if someone has installed a font with the same name
* in the JRE.
*/
if (deferredFontFiles.size() > 0) {
font = findJREDeferredFont(lowerCaseName, style);
if (font != null) {
fontNameCache.put(mapName, font);
return font;
}
}
font = findFontFromPlatform(lowerCaseName, style);
if (font != null) {
if (FontUtilities.isLogging()) {
FontUtilities.getLogger()
.info("Found font via platform API for request:\"" +
name + "\":, style="+style+
" found font: " + font);
}
fontNameCache.put(mapName, font);
return font;
}
}
/* If reach here and no match has been located, then if there are
* uninitialised deferred fonts, load as many of those as needed
* to find the deferred font. If none is found through that
* search continue on.
* There is possibly a minor issue when more than one
* deferred font implements the same font face. Since deferred
* fonts are only those in font configuration files, this is a
* controlled situation, the known case being Solaris euro_fonts
* versions of Arial, Times New Roman, Courier New. However
* the larger font will transparently replace the smaller one
* - see addToFontList() - when it is needed by the composite font.
*/
if (deferredFontFiles.size() > 0) {
font = findDeferredFont(name, style);
if (font != null) {
fontNameCache.put(mapName, font);
return font;
}
}
/* Some apps use deprecated 1.0 names such as helvetica and courier. On
* Solaris these are Type1 fonts in /usr/openwin/lib/X11/fonts/Type1.
* If running on Solaris will register all the fonts in this
* directory.
* May as well register the whole directory without actually testing
* the font name is one of the deprecated names as the next step would
* load all fonts which are in this directory anyway.
* In the event that this lookup is successful it potentially "hides"
* TrueType versions of such fonts that are elsewhere but since they
* do not exist on Solaris this is not a problem.
* Set a flag to indicate we've done this registration to avoid
* repetition and more seriously, to avoid recursion.
*/
if (FontUtilities.isSolaris &&!loaded1dot0Fonts) {
/* "timesroman" is a special case since that's not the
* name of any known font on Solaris or elsewhere.
*/
if (lowerCaseName.equals("timesroman")) {
font = findFont2D("serif", style, fallback);
fontNameCache.put(mapName, font);
}
register1dot0Fonts();
loaded1dot0Fonts = true;
Font2D ff = findFont2D(name, style, fallback);
return ff;
}
/* We check for application registered fonts before
* explicitly loading all fonts as if necessary the registration
* code will have done so anyway. And we don't want to needlessly
* load the actual files for all fonts.
* Just as for installed fonts we check for family before fullname.
* We do not add these fonts to fontNameCache for the
* app context case which eliminates the overhead of a per context
* cache for these.
*/
if (fontsAreRegistered) {
Hashtable familyTable = createdByFamilyName;
Hashtable nameTable = createdByFullName;
family = familyTable.get(lowerCaseName);
if (family != null) {
font = family.getFontWithExactStyleMatch(style);
if (font == null) {
font = family.getFont(style);
}
if (font == null) {
font = family.getClosestStyle(style);
}
if (font != null) {
if (fontsAreRegistered) {
fontNameCache.put(mapName, font);
}
return font;
}
}
font = nameTable.get(lowerCaseName);
if (font != null) {
if (fontsAreRegistered) {
fontNameCache.put(mapName, font);
}
return font;
}
}
/* If reach here and no match has been located, then if all fonts
* are not yet loaded, do so, and then recurse.
*/
if (!loadedAllFonts) {
if (FontUtilities.isLogging()) {
FontUtilities.getLogger()
.info("Load fonts looking for:" + name);
}
loadFonts();
loadedAllFonts = true;
return findFont2D(name, style, fallback);
}
if (!loadedAllFontFiles) {
if (FontUtilities.isLogging()) {
FontUtilities.getLogger()
.info("Load font files looking for:" + name);
}
loadFontFiles();
loadedAllFontFiles = true;
return findFont2D(name, style, fallback);
}
/* The primary name is the locale default - ie not US/English but
* whatever is the default in this locale. This is the way it always
* has been but may be surprising to some developers if "Arial Regular"
* were hard-coded in their app and yet "Arial Regular" was not the
* default name. Fortunately for them, as a consequence of the JDK
* supporting returning names and family names for arbitrary locales,
* we also need to support searching all localised names for a match.
* But because this case of the name used to reference a font is not
* the same as the default for this locale is rare, it makes sense to
* search a much shorter list of default locale names and only go to
* a longer list of names in the event that no match was found.
* So add here code which searches localised names too.
* As in 1.4.x this happens only after loading all fonts, which
* is probably the right order.
*/
if ((font = findFont2DAllLocales(name, style)) != null) {
fontNameCache.put(mapName, font);
return font;
}
/* Perhaps its a "compatibility" name - timesroman, helvetica,
* or courier, which 1.0 apps used for logical fonts.
* We look for these "late" after a loadFonts as we must not
* hide real fonts of these names.
* Map these appropriately:
* On windows this means according to the rules specified by the
* FontConfiguration : do it only for encoding==Cp1252
*
* REMIND: this is something we plan to remove.
*/
if (FontUtilities.isWindows) {
String compatName =
getFontConfiguration().getFallbackFamilyName(name, null);
if (compatName != null) {
font = findFont2D(compatName, style, fallback);
fontNameCache.put(mapName, font);
return font;
}
} else if (lowerCaseName.equals("timesroman")) {
font = findFont2D("serif", style, fallback);
fontNameCache.put(mapName, font);
return font;
} else if (lowerCaseName.equals("helvetica")) {
font = findFont2D("sansserif", style, fallback);
fontNameCache.put(mapName, font);
return font;
} else if (lowerCaseName.equals("courier")) {
font = findFont2D("monospaced", style, fallback);
fontNameCache.put(mapName, font);
return font;
}
if (FontUtilities.isLogging()) {
FontUtilities.getLogger().info("No font found for:" + name);
}
switch (fallback) {
case PHYSICAL_FALLBACK: return getDefaultPhysicalFont();
case LOGICAL_FALLBACK: return getDefaultLogicalFont(style);
default: return null;
}
}
/*
* Workaround for apps which are dependent on a font metrics bug
* in JDK 1.1. This is an unsupported win32 private setting.
* Left in for a customer - do not remove.
*/
public boolean usePlatformFontMetrics() {
return usePlatformFontMetrics;
}
public int getNumFonts() {
return physicalFonts.size()+maxCompFont;
}
private static boolean fontSupportsEncoding(Font font, String encoding) {
return FontUtilities.getFont2D(font).supportsEncoding(encoding);
}
protected abstract String getFontPath(boolean noType1Fonts);
Thread fileCloser = null;
Vector tmpFontFiles = null;
private int createdFontCount = 0;
public Font2D[] createFont2D(File fontFile, int fontFormat, boolean all,
boolean isCopy, CreatedFontTracker tracker)
throws FontFormatException {
List fList = new ArrayList();
int cnt = 1;
String fontFilePath = fontFile.getPath();
FileFont font2D = null;
final File fFile = fontFile;
final CreatedFontTracker _tracker = tracker;
boolean weakRefs = false;
int maxStrikes = 0;
synchronized (this) {
if (createdFontCount < maxSoftRefCnt) {
createdFontCount++;
} else {
weakRefs = true;
maxStrikes = 10;
}
}
try {
switch (fontFormat) {
case Font.TRUETYPE_FONT:
font2D = new TrueTypeFont(fontFilePath, null, 0, true);
font2D.setUseWeakRefs(weakRefs, maxStrikes);
fList.add(font2D);
if (!all) {
break;
}
cnt = ((TrueTypeFont)font2D).getFontCount();
int index = 1;
while (index < cnt) {
font2D = new TrueTypeFont(fontFilePath, null, index++, true);
font2D.setUseWeakRefs(weakRefs, maxStrikes);
fList.add(font2D);
}
break;
case Font.TYPE1_FONT:
font2D = new Type1Font(fontFilePath, null, isCopy);
font2D.setUseWeakRefs(weakRefs, maxStrikes);
fList.add(font2D);
break;
default:
throw new FontFormatException("Unrecognised Font Format");
}
} catch (FontFormatException e) {
if (isCopy) {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run() {
if (_tracker != null) {
_tracker.subBytes((int)fFile.length());
}
fFile.delete();
return null;
}
});
}
throw(e);
}
if (isCopy) {
FileFont.setFileToRemove(fList, fontFile, cnt, tracker);
synchronized (FontManager.class) {
if (tmpFontFiles == null) {
tmpFontFiles = new Vector();
}
tmpFontFiles.add(fontFile);
if (fileCloser == null) {
final Runnable fileCloserRunnable = new Runnable() {
public void run() {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run() {
for (int i=0;i) () -> {
ThreadGroup rootTG = ThreadGroupUtils.getRootThreadGroup();
fileCloser = new Thread(rootTG, fileCloserRunnable,
"FileCloser", 0, false);
fileCloser.setContextClassLoader(null);
Runtime.getRuntime().addShutdownHook(fileCloser);
return null;
});
}
}
}
return fList.toArray(new Font2D[0]);
}
/* 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[] mapEntries = 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 tmp = (Map.Entry)mapEntries[i];
tmp.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 Font2D.FONT_CONFIG_RANK) {
compFonts[i].replaceComponentFont(oldFont, newFont);
}
}
}
private synchronized void loadLocaleNames() {
if (localeFullNamesToFont != null) {
return;
}
localeFullNamesToFont = new HashMap();
Font2D[] fonts = getRegisteredFonts();
for (int i=0; i installedNames = null;
private static HashSet getInstalledNames() {
if (installedNames == null) {
Locale l = getSystemStartupLocale();
SunFontManager fontManager = SunFontManager.getInstance();
String[] installedFamilies =
fontManager.getInstalledFontFamilyNames(l);
Font[] installedFonts = fontManager.getAllInstalledFonts();
HashSet names = new HashSet();
for (int i=0; i createdByFamilyName;
private Hashtable createdByFullName;
private boolean fontsAreRegistered = false;
public boolean registerFont(Font font) {
/* This method should not be called with "null".
* It is the caller's responsibility to ensure that.
*/
if (font == null) {
return false;
}
/* Initialise these objects only once we start to use this API */
synchronized (regFamilyLock) {
if (createdByFamilyName == null) {
createdByFamilyName = new Hashtable();
createdByFullName = new Hashtable();
}
}
if (! FontAccess.getFontAccess().isCreatedFont(font)) {
return false;
}
/* We want to ensure that this font cannot override existing
* installed fonts. Check these conditions :
* - family name is not that of an installed font
* - full name is not that of an installed font
* - family name is not the same as the full name of an installed font
* - full name is not the same as the family name of an installed font
* The last two of these may initially look odd but the reason is
* that (unfortunately) Font constructors do not distinuguish these.
* An extreme example of such a problem would be a font which has
* family name "Dialog.Plain" and full name of "Dialog".
* The one arguably overly stringent restriction here is that if an
* application wants to supply a new member of an existing family
* It will get rejected. But since the JRE can perform synthetic
* styling in many cases its not necessary.
* We don't apply the same logic to registered fonts. If apps want
* to do this lets assume they have a reason. It won't cause problems
* except for themselves.
*/
HashSet names = getInstalledNames();
Locale l = getSystemStartupLocale();
String familyName = font.getFamily(l).toLowerCase();
String fullName = font.getFontName(l).toLowerCase();
if (names.contains(familyName) || names.contains(fullName)) {
return false;
}
/* Checks passed, now register the font */
Hashtable familyTable = createdByFamilyName;
Hashtable fullNameTable = createdByFullName;
fontsAreRegistered = true;
/* Create the FontFamily and add font to the tables */
Font2D font2D = FontUtilities.getFont2D(font);
int style = font2D.getStyle();
FontFamily family = familyTable.get(familyName);
if (family == null) {
family = new FontFamily(font.getFamily(l));
familyTable.put(familyName, family);
}
/* Remove name cache entries if not using app contexts.
* To accommodate a case where code may have registered first a plain
* family member and then used it and is now registering a bold family
* member, we need to remove all members of the family, so that the
* new style can get picked up rather than continuing to synthesise.
*/
if (fontsAreRegistered) {
removeFromCache(family.getFont(Font.PLAIN));
removeFromCache(family.getFont(Font.BOLD));
removeFromCache(family.getFont(Font.ITALIC));
removeFromCache(family.getFont(Font.BOLD|Font.ITALIC));
removeFromCache(fullNameTable.get(fullName));
}
family.setFont(font2D, style);
fullNameTable.put(fullName, font2D);
return true;
}
/* Remove from the name cache all references to the Font2D */
private void removeFromCache(Font2D font) {
if (font == null) {
return;
}
String[] keys = fontNameCache.keySet().toArray(STR_ARRAY);
for (int k=0; k getCreatedFontFamilyNames() {
Hashtable familyTable;
if (fontsAreRegistered) {
familyTable = createdByFamilyName;
} else {
return null;
}
Locale l = getSystemStartupLocale();
synchronized (familyTable) {
TreeMap map = new TreeMap();
for (FontFamily f : familyTable.values()) {
Font2D font2D = f.getFont(Font.PLAIN);
if (font2D == null) {
font2D = f.getClosestStyle(Font.PLAIN);
}
String name = font2D.getFamilyName(l);
map.put(name.toLowerCase(l), name);
}
return map;
}
}
public Font[] getCreatedFonts() {
Hashtable nameTable;
if (fontsAreRegistered) {
nameTable = createdByFullName;
} else {
return null;
}
Locale l = getSystemStartupLocale();
synchronized (nameTable) {
Font[] fonts = new Font[nameTable.size()];
int i=0;
for (Font2D font2D : nameTable.values()) {
fonts[i++] = new Font(font2D.getFontName(l), Font.PLAIN, 1);
}
return fonts;
}
}
protected String[] getPlatformFontDirs(boolean noType1Fonts) {
/* First check if we already initialised path dirs */
if (pathDirs != null) {
return pathDirs;
}
String path = getPlatformFontPath(noType1Fonts);
StringTokenizer parser =
new StringTokenizer(path, File.pathSeparator);
ArrayList pathList = new ArrayList();
try {
while (parser.hasMoreTokens()) {
pathList.add(parser.nextToken());
}
} catch (NoSuchElementException e) {
}
pathDirs = pathList.toArray(new String[0]);
return pathDirs;
}
/**
* Returns an array of two strings. The first element is the
* name of the font. The second element is the file name.
*/
protected abstract String[] getDefaultPlatformFont();
// Begin: Refactored from SunGraphicsEnviroment.
/*
* helper function for registerFonts
*/
private void addDirFonts(String dirName, File dirFile,
FilenameFilter filter,
int fontFormat, boolean useJavaRasterizer,
int fontRank,
boolean defer, boolean resolveSymLinks) {
String[] ls = dirFile.list(filter);
if (ls == null || ls.length == 0) {
return;
}
String[] fontNames = new String[ls.length];
String[][] nativeNames = new String[ls.length][];
int fontCount = 0;
for (int i=0; i < ls.length; i++ ) {
File theFile = new File(dirFile, ls[i]);
String fullName = null;
if (resolveSymLinks) {
try {
fullName = theFile.getCanonicalPath();
} catch (IOException e) {
}
}
if (fullName == null) {
fullName = dirName + File.separator + ls[i];
}
// REMIND: case compare depends on platform
if (registeredFontFiles.contains(fullName)) {
continue;
}
if (badFonts != null && badFonts.contains(fullName)) {
if (FontUtilities.debugFonts()) {
FontUtilities.getLogger()
.warning("skip bad font " + fullName);
}
continue; // skip this font file.
}
registeredFontFiles.add(fullName);
if (FontUtilities.debugFonts()
&& FontUtilities.getLogger().isLoggable(PlatformLogger.Level.INFO)) {
String message = "Registering font " + fullName;
String[] natNames = getNativeNames(fullName, null);
if (natNames == null) {
message += " with no native name";
} else {
message += " with native name(s) " + natNames[0];
for (int nn = 1; nn < natNames.length; nn++) {
message += ", " + natNames[nn];
}
}
FontUtilities.getLogger().info(message);
}
fontNames[fontCount] = fullName;
nativeNames[fontCount++] = getNativeNames(fullName, null);
}
registerFonts(fontNames, nativeNames, fontCount, fontFormat,
useJavaRasterizer, fontRank, defer);
return;
}
protected String[] getNativeNames(String fontFileName,
String platformName) {
return null;
}
/**
* Returns a file name for the physical font represented by this platform
* font name. The default implementation tries to obtain the file name
* from the font configuration.
* Subclasses may override to provide information from other sources.
*/
protected String getFileNameFromPlatformName(String platformFontName) {
return fontConfig.getFileNameFromPlatformName(platformFontName);
}
/**
* Return the default font configuration.
*/
public FontConfiguration getFontConfiguration() {
return fontConfig;
}
/* A call to this method should be followed by a call to
* registerFontDirs(..)
*/
public String getPlatformFontPath(boolean noType1Font) {
if (fontPath == null) {
fontPath = getFontPath(noType1Font);
}
return fontPath;
}
protected void loadFonts() {
if (discoveredAllFonts) {
return;
}
/* Use lock specific to the font system */
synchronized (this) {
if (FontUtilities.debugFonts()) {
Thread.dumpStack();
FontUtilities.getLogger()
.info("SunGraphicsEnvironment.loadFonts() called");
}
initialiseDeferredFonts();
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run() {
if (fontPath == null) {
fontPath = getPlatformFontPath(noType1Font);
registerFontDirs(fontPath);
}
if (fontPath != null) {
// this will find all fonts including those already
// registered. But we have checks in place to prevent
// double registration.
if (! gotFontsFromPlatform()) {
registerFontsOnPath(fontPath, false,
Font2D.UNKNOWN_RANK,
false, true);
loadedAllFontFiles = true;
}
}
registerOtherFontFiles(registeredFontFiles);
discoveredAllFonts = true;
return null;
}
});
}
}
protected void registerFontDirs(String pathName) {
return;
}
private void registerFontsOnPath(String pathName,
boolean useJavaRasterizer, int fontRank,
boolean defer, boolean resolveSymLinks) {
StringTokenizer parser = new StringTokenizer(pathName,
File.pathSeparator);
try {
while (parser.hasMoreTokens()) {
registerFontsInDir(parser.nextToken(),
useJavaRasterizer, fontRank,
defer, resolveSymLinks);
}
} catch (NoSuchElementException e) {
}
}
/* Called to register fall back fonts */
public void registerFontsInDir(String dirName) {
registerFontsInDir(dirName, true, Font2D.JRE_RANK, true, false);
}
// MACOSX begin -- need to access this in subclass
protected void registerFontsInDir(String dirName, boolean useJavaRasterizer,
// MACOSX end
int fontRank,
boolean defer, boolean resolveSymLinks) {
File pathFile = new File(dirName);
addDirFonts(dirName, pathFile, ttFilter,
FONTFORMAT_TRUETYPE, useJavaRasterizer,
fontRank==Font2D.UNKNOWN_RANK ?
Font2D.TTF_RANK : fontRank,
defer, resolveSymLinks);
addDirFonts(dirName, pathFile, t1Filter,
FONTFORMAT_TYPE1, useJavaRasterizer,
fontRank==Font2D.UNKNOWN_RANK ?
Font2D.TYPE1_RANK : fontRank,
defer, resolveSymLinks);
}
protected void registerFontDir(String path) {
}
/**
* Returns file name for default font, either absolute
* or relative as needed by registerFontFile.
*/
public synchronized String getDefaultFontFile() {
return defaultFontFileName;
}
/**
* Whether registerFontFile expects absolute or relative
* font file names.
*/
protected boolean useAbsoluteFontFileNames() {
return true;
}
/**
* Creates this environment's FontConfiguration.
*/
protected abstract FontConfiguration createFontConfiguration();
public abstract FontConfiguration
createFontConfiguration(boolean preferLocaleFonts,
boolean preferPropFonts);
/**
* Returns face name for default font, or null if
* no face names are used for CompositeFontDescriptors
* for this platform.
*/
public synchronized String getDefaultFontFaceName() {
return defaultFontName;
}
public void loadFontFiles() {
loadFonts();
if (loadedAllFontFiles) {
return;
}
/* Use lock specific to the font system */
synchronized (this) {
if (FontUtilities.debugFonts()) {
Thread.dumpStack();
FontUtilities.getLogger().info("loadAllFontFiles() called");
}
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run() {
if (fontPath == null) {
fontPath = getPlatformFontPath(noType1Font);
}
if (fontPath != null) {
// this will find all fonts including those already
// registered. But we have checks in place to prevent
// double registration.
registerFontsOnPath(fontPath, false,
Font2D.UNKNOWN_RANK,
false, true);
}
loadedAllFontFiles = true;
return null;
}
});
}
}
/*
* This method asks the font configuration API for all platform names
* used as components of composite/logical fonts and iterates over these
* looking up their corresponding file name and registers these fonts.
* It also ensures that the fonts are accessible via platform APIs.
* The composites themselves are then registered.
*/
private void
initCompositeFonts(FontConfiguration fontConfig,
ConcurrentHashMap altNameCache) {
if (FontUtilities.isLogging()) {
FontUtilities.getLogger()
.info("Initialising composite fonts");
}
int numCoreFonts = fontConfig.getNumberCoreFonts();
String[] fcFonts = fontConfig.getPlatformFontNames();
for (int f=0; ffilename
* mappings needed to speed start-up on Solaris.
* Augment this with the appendedpathname and the mappings
* for native (F3) fonts
*/
//String platName = platformFontName.replaceAll(" ", "_");
//System.out.println("filename."+platName+"="+fontFileName);
registerFontFile(fontFileName, nativeNames,
Font2D.FONT_CONFIG_RANK, true);
}
/* This registers accumulated paths from the calls to
* addFontToPlatformFontPath(..) and any specified by
* the font configuration. Rather than registering
* the fonts it puts them in a place and form suitable for
* the Toolkit to pick up and use if a toolkit is initialised,
* and if it uses X11 fonts.
*/
registerPlatformFontsUsedByFontConfiguration();
CompositeFontDescriptor[] compositeFontInfo
= fontConfig.get2DCompositeFontInfo();
for (int i = 0; i < compositeFontInfo.length; i++) {
CompositeFontDescriptor descriptor = compositeFontInfo[i];
String[] componentFileNames = descriptor.getComponentFileNames();
String[] componentFaceNames = descriptor.getComponentFaceNames();
/* It would be better eventually to handle this in the
* FontConfiguration code which should also remove duplicate slots
*/
if (missingFontFiles != null) {
for (int ii=0; ii();
}
missingFontFiles.add(fileName);
}
/*
* This is for use only within getAllFonts().
* Fonts listed in the fontconfig files for windows were all
* on the "deferred" initialisation list. They were registered
* either in the course of the application, or in the call to
* loadFonts() within getAllFonts(). The fontconfig file specifies
* the names of the fonts using the English names. If there's a
* different name in the execution locale, then the platform will
* report that, and we will construct the font with both names, and
* thereby enumerate it twice. This happens for Japanese fonts listed
* in the windows fontconfig, when run in the JA locale. The solution
* is to rely (in this case) on the platform's font->file mapping to
* determine that this name corresponds to a file we already registered.
* This works because
* - we know when we get here all deferred fonts are already initialised
* - when we register a font file, we register all fonts in it.
* - we know the fontconfig fonts are all in the windows registry
*/
private boolean isNameForRegisteredFile(String fontName) {
String fileName = getFileNameForFontName(fontName);
if (fileName == null) {
return false;
}
return registeredFontFiles.contains(fileName);
}
/*
* This invocation is not in a privileged block because
* all privileged operations (reading files and properties)
* was conducted on the creation of the GE
*/
public void
createCompositeFonts(ConcurrentHashMap altNameCache,
boolean preferLocale,
boolean preferProportional) {
FontConfiguration fontConfig =
createFontConfiguration(preferLocale, preferProportional);
initCompositeFonts(fontConfig, altNameCache);
}
/**
* Returns all fonts installed in this environment.
*/
public Font[] getAllInstalledFonts() {
if (allFonts == null) {
loadFonts();
TreeMap fontMapNames = new TreeMap<>();
/* warning: the number of composite fonts could change dynamically
* if applications are allowed to create them. "allfonts" could
* then be stale.
*/
Font2D[] allfonts = getRegisteredFonts();
for (int i=0; i < allfonts.length; i++) {
if (!(allfonts[i] instanceof NativeFont)) {
fontMapNames.put(allfonts[i].getFontName(null),
allfonts[i]);
}
}
String[] platformNames = getFontNamesFromPlatform();
if (platformNames != null) {
for (int i=0; i 0) {
fontNames = new String[fontMapNames.size()];
Object [] keyNames = fontMapNames.keySet().toArray();
for (int i=0; i < keyNames.length; i++) {
fontNames[i] = (String)keyNames[i];
}
}
Font[] fonts = new Font[fontNames.length];
for (int i=0; i < fontNames.length; i++) {
fonts[i] = new Font(fontNames[i], Font.PLAIN, 1);
Font2D f2d = fontMapNames.get(fontNames[i]);
if (f2d != null) {
FontAccess.getFontAccess().setFont2D(fonts[i], f2d.handle);
}
}
allFonts = fonts;
}
Font []copyFonts = new Font[allFonts.length];
System.arraycopy(allFonts, 0, copyFonts, 0, allFonts.length);
return copyFonts;
}
/**
* Get a list of installed fonts in the requested {@link Locale}.
* The list contains the fonts Family Names.
* If Locale is null, the default locale is used.
*
* @param requestedLocale, if null the default locale is used.
* @return list of installed fonts in the system.
*/
public String[] getInstalledFontFamilyNames(Locale requestedLocale) {
if (requestedLocale == null) {
requestedLocale = Locale.getDefault();
}
if (allFamilies != null && lastDefaultLocale != null &&
requestedLocale.equals(lastDefaultLocale)) {
String[] copyFamilies = new String[allFamilies.length];
System.arraycopy(allFamilies, 0, copyFamilies,
0, allFamilies.length);
return copyFamilies;
}
TreeMap familyNames = new TreeMap();
// these names are always there and aren't localised
String str;
str = Font.SERIF; familyNames.put(str.toLowerCase(), str);
str = Font.SANS_SERIF; familyNames.put(str.toLowerCase(), str);
str = Font.MONOSPACED; familyNames.put(str.toLowerCase(), str);
str = Font.DIALOG; familyNames.put(str.toLowerCase(), str);
str = Font.DIALOG_INPUT; familyNames.put(str.toLowerCase(), str);
/* Platform APIs may be used to get the set of available family
* names for the current default locale so long as it is the same
* as the start-up system locale, rather than loading all fonts.
*/
if (requestedLocale.equals(getSystemStartupLocale()) &&
getFamilyNamesFromPlatform(familyNames, requestedLocale)) {
/* Augment platform names with JRE font family names */
getJREFontFamilyNames(familyNames, requestedLocale);
} else {
loadFontFiles();
Font2D[] physicalfonts = getPhysicalFonts();
for (int i=0; i < physicalfonts.length; i++) {
if (!(physicalfonts[i] instanceof NativeFont)) {
String name =
physicalfonts[i].getFamilyName(requestedLocale);
familyNames.put(name.toLowerCase(requestedLocale), name);
}
}
}
// Add any native font family names here
addNativeFontFamilyNames(familyNames, requestedLocale);
String[] retval = new String[familyNames.size()];
Object [] keyNames = familyNames.keySet().toArray();
for (int i=0; i < keyNames.length; i++) {
retval[i] = familyNames.get(keyNames[i]);
}
if (requestedLocale.equals(Locale.getDefault())) {
lastDefaultLocale = requestedLocale;
allFamilies = new String[retval.length];
System.arraycopy(retval, 0, allFamilies, 0, allFamilies.length);
}
return retval;
}
// Provides an aperture to add native font family names to the map
protected void addNativeFontFamilyNames(TreeMap familyNames, Locale requestedLocale) { }
public void register1dot0Fonts() {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run() {
String type1Dir = "/usr/openwin/lib/X11/fonts/Type1";
registerFontsInDir(type1Dir, true, Font2D.TYPE1_RANK,
false, false);
return null;
}
});
}
/* Really we need only the JRE fonts family names, but there's little
* overhead in doing this the easy way by adding all the currently
* known fonts.
*/
protected void getJREFontFamilyNames(TreeMap familyNames,
Locale requestedLocale) {
registerDeferredJREFonts(jreFontDirName);
Font2D[] physicalfonts = getPhysicalFonts();
for (int i=0; i < physicalfonts.length; i++) {
if (!(physicalfonts[i] instanceof NativeFont)) {
String name =
physicalfonts[i].getFamilyName(requestedLocale);
familyNames.put(name.toLowerCase(requestedLocale), name);
}
}
}
/**
* Default locale can be changed but we need to know the initial locale
* as that is what is used by native code. Changing Java default locale
* doesn't affect that.
* Returns the locale in use when using native code to communicate
* with platform APIs. On windows this is known as the "system" locale,
* and it is usually the same as the platform locale, but not always,
* so this method also checks an implementation property used only
* on windows and uses that if set.
*/
private static Locale systemLocale = null;
private static Locale getSystemStartupLocale() {
if (systemLocale == null) {
systemLocale = (Locale)
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run() {
/* On windows the system locale may be different than the
* user locale. This is an unsupported configuration, but
* in that case we want to return a dummy locale that will
* never cause a match in the usage of this API. This is
* important because Windows documents that the family
* names of fonts are enumerated using the language of
* the system locale. BY returning a dummy locale in that
* case we do not use the platform API which would not
* return us the names we want.
*/
String fileEncoding = System.getProperty("file.encoding", "");
String sysEncoding = System.getProperty("sun.jnu.encoding");
if (sysEncoding != null && !sysEncoding.equals(fileEncoding)) {
return Locale.ROOT;
}
String language = System.getProperty("user.language", "en");
String country = System.getProperty("user.country","");
String variant = System.getProperty("user.variant","");
return new Locale(language, country, variant);
}
});
}
return systemLocale;
}
void addToPool(FileFont font) {
FileFont fontFileToClose = null;
int freeSlot = -1;
synchronized (fontFileCache) {
/* Avoid duplicate entries in the pool, and don't close() it,
* since this method is called only from within open().
* Seeing a duplicate is most likely to happen if the thread
* was interrupted during a read, forcing perhaps repeated
* close and open calls and it eventually it ends up pointing
* at the same slot.
*/
for (int i=0;i= 0) {
fontFileCache[freeSlot] = font;
return;
} else {
/* replace with new font. */
fontFileToClose = fontFileCache[lastPoolIndex];
fontFileCache[lastPoolIndex] = font;
/* lastPoolIndex is updated so that the least recently opened
* file will be closed next.
*/
lastPoolIndex = (lastPoolIndex+1) % CHANNELPOOLSIZE;
}
}
/* Need to close the font file outside of the synchronized block,
* since its possible some other thread is in an open() call on
* this font file, and could be holding its lock and the pool lock.
* Releasing the pool lock allows that thread to continue, so it can
* then release the lock on this font, allowing the close() call
* below to proceed.
* Also, calling close() is safe because any other thread using
* the font we are closing() synchronizes all reading, so we
* will not close the file while its in use.
*/
if (fontFileToClose != null) {
fontFileToClose.close();
}
}
protected FontUIResource getFontConfigFUIR(String family, int style,
int size)
{
return new FontUIResource(family, style, size);
}
}