我最近发现了如何为我的用例(SVG到PNG转码)在本地GraphicsEnvironment s.t.注册TTF字体,Apache Batik可以识别该字体:

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);
}

但是,我想知道是否可以取消注册任何预先存在的字体,以确保仅将我注册的字体用于转码。

没有GraphicsEnvironment#unregisterFont(...),我该如何实现呢?

PS:我不想继承GraphicsEnvironment的子类,因为我无法假定存在任何特定的子类,例如sun.awt.Win32GraphicsEnvironment。

编辑:更多信息:
  • 由于sun.font.FontManager随着Java7的变化(从类到接口(interface),什么都没有),我宁愿不使用任何依赖它的解决方法。
  • 我的JVM是Oracle JVM。
  • 最佳答案

    如果没有私有(private)静态变量的反射(reflect)就无法做到这一点,例如……您确定需要这样做吗?

    将源代码 checkout 到 sun.font.FontManager.registerFont ,它可能已经具有您想要的安全性。 (这是当您调用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;
    }
    

    10-05 17:51