在Java中测试字体是否是等宽字体

15

我正在尝试列出用户机器上所有的等宽字体。我可以通过以下方式获取Swing中的所有字体系列:

String[] fonts = GraphicsEnvironment.getLocalGraphicsEnvironment()
                                    .getAvailableFontFamilyNames();
有没有办法弄清楚这些字体哪些是等宽字体?
谢谢。
5个回答

7

一种更简单的方法,不需要创建BufferedImage来获取Graphics对象等:

    Font fonts[] = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
    List<Font> monoFonts1 = new ArrayList<>();

    FontRenderContext frc = new FontRenderContext(null, RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT, RenderingHints.VALUE_FRACTIONALMETRICS_DEFAULT);
    for (Font font : fonts) {
        Rectangle2D iBounds = font.getStringBounds("i", frc);
        Rectangle2D mBounds = font.getStringBounds("m", frc);
        if (iBounds.getWidth() == mBounds.getWidth()) {
            monoFonts1.add(font);
        }
    }

1
谢谢;伟大的实用解决方案!我添加了一行“Font testFont = new Font(font.getName(), font.getStyle(), 12)”并在此字体上进行测试(即大小为12),因为从“getAllFonts()”中出来的字体都是大小为1。这使得有所不同:从354个字体(大小为1)到303个字体(大小为12)。顺便说一句,我添加了一个带有“ ”(空格)的确认测试-结果没有从303个字体移动。 - stolsvik

6
你可以使用FontMetrics类的getWidths()方法。根据JavaDoc:
获取字体中前256个字符的进阶宽度。进阶是从字符基线的最左点到最右点的距离。请注意,字符串的进阶不一定是其字符进阶之和。
你可以使用FontMetrics类的charWidth(char)方法。例如:
Set<String> monospaceFontFamilyNames = new HashSet<String>();

GraphicsEnvironment graphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment();
String[] fontFamilyNames = graphicsEnvironment.getAvailableFontFamilyNames();

BufferedImage bufferedImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
Graphics graphics = bufferedImage.createGraphics();

for (String fontFamilyName : fontFamilyNames) {
    boolean isMonospaced = true;

    int fontStyle = Font.PLAIN;
    int fontSize = 12;
    Font font = new Font(fontFamilyName, fontStyle, fontSize);
    FontMetrics fontMetrics = graphics.getFontMetrics(font);

    int firstCharacterWidth = 0;
    boolean hasFirstCharacterWidth = false;
    for (int codePoint = 0; codePoint < 128; codePoint++) { 
        if (Character.isValidCodePoint(codePoint) && (Character.isLetter(codePoint) || Character.isDigit(codePoint))) {
            char character = (char) codePoint;
            int characterWidth = fontMetrics.charWidth(character);
            if (hasFirstCharacterWidth) {
                if (characterWidth != firstCharacterWidth) {
                    isMonospaced = false;
                    break;
                }
            } else {
                firstCharacterWidth = characterWidth;
                hasFirstCharacterWidth = true;
            }
        }
    }

    if (isMonospaced) {
        monospaceFontFamilyNames.add(fontFamilyName);
    }
}

graphics.dispose();

感谢您扩展示例代码 - 得到给定字体的FontMetrics并不明显。但是,由于某种原因,在我的机器上(OS X)找不到任何等宽字体... - Jonik
我使用已知为等宽字体的一种字体进行了测试(java.awt.Font[family=Andale Mono,name=Andale Mono,style=plain,size=12])。对于这种字体,getWidths() 返回类似于以下数字:7 0 4 4 4 4 4 4 0 7 7 4 7 7 4 ... :-/ - Jonik
嗯,有趣;这段代码在系统中找到了一个等宽字体:“Lucida Sans Typewriter”。但是像“Andale Mono”和“Courier New”这样的字体仍然未通过测试(这两个都在代码点1处)。当打印时,失败的字符看起来很像空格,所以我想知道空格检查是否正确... - Jonik
谢谢Adam。在我的Windows机器上有效。我会回家后在我的Mac上测试它。 - Ian
Jonik:也许你可以把codePoint的检查改为类似Character.isLetter(...) || Character.isDigit(...)这样的东西? - Adam Paynter
Adam,谢谢 - 在那个更改之后,它在 Mac 上找到了 8 种等宽字体,这可能是全部。另外,为了记录,在我工作的 Linux 机器上,使用 !Character.isWhitespace() 的版本找到了 23 种等宽字体,而使用 (Character.isLetter() || Character.isDigit()) 找到了 29 种字体。也许您可以更改示例代码,因为 isWhitespace() 似乎并不总是可靠的...但无论如何 +1! - Jonik

3

比较几个字符(m、i、1、.)的绘制长度(应该是一个好的集合)。

对于等宽字体,它们的长度将都相等;对于变宽字体,它们的长度将不同。


1

也许对于你的情况不适用,但如果你只想将字体设置为等宽字体,请使用逻辑字体名称:

Font mono = new Font("Monospaced", Font.PLAIN, 12);

这将是您系统上的保证等宽字体。


1
根据此响应,Java对底层字体细节了解不多,因此您需要比较字体的尺寸。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接