【问题标题】:Testing whether a Font is monospaced in Java在 Java 中测试字体是否为等宽字体
【发布时间】:2010-10-29 15:41:13
【问题描述】:

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

String[] fonts = GraphicsEnvironment.getLocalGraphicsEnvironment()
                                    .getAvailableFontFamilyNames();

有没有办法确定其中哪些是等宽的?

提前致谢。

【问题讨论】:

    标签: java swing fonts


    【解决方案1】:

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

    对于等宽字体,它们都是相等的,对于可变宽度字体,它们不会。

    【讨论】:

      【解决方案2】:

      根据this response,Java 对底层字体细节了解不多,因此您必须对字体的尺寸进行一些比较。

      【讨论】:

        【解决方案3】:

        您可以使用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)上找不到任何等宽字体......
        • 我使用一种已知为等宽字体 (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 ... :-/
        • 嗯,有趣;此代码在此系统上找到 一个 等宽字体:“Lucida Sans Typewriter”。但是像“Andale Mono”和“Courier New”这样的字体仍然无法通过测试(这两种字体都在代码点 1 处)。打印时,失败的字符看起来很像空格,所以我想知道空格检查是否正常工作......
        • 感谢亚当。在我的 Windows 机器上工作。我会在家里的 Mac 上检查一下
        • Jonik:也许您可以将 codePoint 检查更改为 Character.isLetter(...) || Character.isDigit(...)?
        【解决方案4】:

        可能不适用于您的情况,但如果您只是想将字体设置为等宽字体,请使用逻辑字体名称:

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

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

        【讨论】:

          【解决方案5】:

          一种更简单的方法,不需要制作 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);
                  }
              }
          

          【讨论】:

          • 谢谢;非常实用的解决方案!我添加了一行 'Font testFont = new Font(font.getName(), font.getStyle(), 12)' 并在此字体上进行测试(即大小 12),因为来自“getAllFonts()”的字体是全部大小为 1。这有所不同:从 354 个正面(大小为 1)到 303 个字体(大小为 12)。顺便说一句,我添加了一个带有“”(空格)的确认测试 - 结果没有从 303 字体中让步。
          猜你喜欢
          • 2020-12-17
          • 1970-01-01
          • 2013-01-09
          • 2021-07-04
          • 1970-01-01
          • 2014-03-24
          • 1970-01-01
          • 2014-06-09
          • 1970-01-01
          相关资源
          最近更新 更多