I recently found out how to register a TTF font with the local GraphicsEnvironment, s.t., for my use case (SVG-to-PNG transcoding), Apache Batik may recognize the font:
import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.GraphicsEnvironment;
// [...]
GraphicsEnvironment lge = GraphicsEnvironment.getLocalGraphicsEnvironment();
try {
Font font = Font.createFont(Font.TRUETYPE_FONT, fontFile);
lge.registerFont(font);
} catch (FontFormatException e) {
logger.warn(e.getMessage(), e);
} catch (IOException e) {
logger.warn(e.getMessage(), e);
}
However, I was wondering if I could unregister any pre-existing fonts in order to guarantee that only the fonts I register will be used in transcoding.
There is no GraphicsEnvironment#unregisterFont(...), how could I achieve this instead?
PS: I don't want to subclass GraphicsEnvironment, as I cannot assume the presence any specific subclass, like sun.awt.Win32GraphicsEnvironment.
EDIT: Some more infos:
This cannot be done without reflection of private static variables and such... are you sure you need to do this?
Check out the source code to sun.font.FontManager.registerFont
, it may already have the safety you desire. (This is the method that does the actual work when you call GraphicsEnvironment.registerFont
)
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 (regFamilyKey) {
if (createdByFamilyName == null) {
createdByFamilyName = new Hashtable<String,FontFamily>();
createdByFullName = new Hashtable<String,Font2D>();
}
}
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<String> 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<String,FontFamily> familyTable;
Hashtable<String,Font2D> fullNameTable;
if (!maybeMultiAppContext()) {
familyTable = createdByFamilyName;
fullNameTable = createdByFullName;
fontsAreRegistered = true;
} else {
AppContext appContext = AppContext.getAppContext();
familyTable =
(Hashtable<String,FontFamily>)appContext.get(regFamilyKey);
fullNameTable =
(Hashtable<String,Font2D>)appContext.get(regFullNameKey);
if (familyTable == null) {
familyTable = new Hashtable<String,FontFamily>();
fullNameTable = new Hashtable<String,Font2D>();
appContext.put(regFamilyKey, familyTable);
appContext.put(regFullNameKey, fullNameTable);
}
fontsAreRegisteredPerAppContext = 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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With