字体如何告诉 OS "I AM a MONSPACED / FIXED-WIDTH FONT"?

how does a font tell the OS that "I AM a MONSPACED / FIXED-WIDTH FONT"?

一开始我只想知道,为什么在IDE PYCHARM on Windows 10 的时候,勾选了"show only monospaced fonts",很多字体都不会列出来在编辑器的字体选择对话框中 [settings/editor/colors 和 fonts/font],也在 mintty 中。

我不知道 pycharm 是怎么做的,但是 mintty 使用的是 win32 API "LOGFONT".

那么,windows OS 是如何知道字体是否等宽(固定宽度)的?

即: "source code pro" 列出,“源代码pro black / extralight / light ...都没有;

"fira code" 已列出,但 fira 代码 light/medium/retina 未列出;

并且,如果 "show only monospaced".

,其他一些等宽字体未在此处列出

似乎pycharm勾选后只能识别字体家族名称"show only monospced fonts"

in OSX(Mavericks) 有点复杂:如果选中 'show only monospaced fonts',pycharm 仍然可以获得姓氏,但无法知道是哪种字体-如果安装了 rugular weighted 字体版本,weight 是默认值。


然后,我试着修改了一些字体,顺便在font forge里看了一眼原来的字体'file-info',但我不知道哪个部分真正影响了WINDOWS或 PYCHARM(IDEA/INTELIJ/by JDK 事实上?)知道哪种字体是等宽的。

在"OS/2"部分,检查了fixed-width/monospace/monospaced提到的任何参数,但没有任何帮助,在windows pycharm仍然无法检测到它们。

所以,最后,我真的很想知道,TTF 文件或任何其他字体文件类型使用哪个参数来告诉 [windows/osx/mac os/linux] OS 'i am monospaced'?

TrueType 字体可以包含 post table。 post table 包含一个名为 isFixedPitch 的 32 位字段。如果它包含 0,则表示比例字体。等宽字体 应该 将此设置为 1 (但通常建议字体引擎应接受任何非零值作为等宽字体,因为这是早期版本需要 TT 规范)。

等宽字体还应将 hheahmtx table 中的 numberOfHMetrics 设置为 3

panose字体匹配数据中也有数据(在OS/2table中)指定字体是否等宽

如果非要我猜的话,我会说 Apple 更有可能使用 post table,而 Microsoft 更有可能使用 Panose 数据(但我很容易错关于其中一个或两个)。


参考文献:

  1. Apple documentation of 'post' table
  2. Microsoft OpenType Recommendations
  3. Panose Classification Metrics Guide

PyCharm 就此而言,所有 JetBrains IDEA 套件都不使用字体标志来识别等宽字体。相反,他们使用以下算法确定字体是否为等宽字体:

  • 常规变体的 'l''W'' ' 字符在 12 号时宽度相同吗?
  • 粗体的'l''W'' '字符是否宽度相同 作为 12 码的常规变体?
  • 斜体变体的'l''W'' '字符是否相同 宽度为 12 码的常规变体?
  • bold-italics 变体的 'l''W'' ' 字符是 与 12 码的常规款式宽度相同?

如果上面的任何宽度计算 return 答案不一致,字体将被列为 non-monospace。

要解决此问题,您应该安装您尝试使用的字体的粗体变体。如果粗体版本的字体不可用,IntelliJ 的字体渲染器将 auto-generate 影响字符宽度的字体,因此导致等宽检查失败。


来源The IntelliJ Community Edition Source Code(为简洁起见缩短了摘录,并根据 Apache 2.0 许可而不是 Whosebug 的 CC-BY-SA 许可获得许可。)

private static int getFontWidth(Font font, int mask) {
    int width = getCharWidth(font, ' ');
    return width == getCharWidth(font, 'l')
        && width == getCharWidth(font, 'W') ? width : 0;
}

FontInfo构造函数中。

int width = getFontWidth(font, Font.PLAIN);
if (!plainOnly) {
    if (width != 0 && width != getFontWidth(font, Font.BOLD)) width = 0;
    if (width != 0 && width != getFontWidth(font, Font.ITALIC)) width = 0;
    if (width != 0 && width != getFontWidth(font, Font.BOLD | Font.ITALIC)) width = 0;
}
boolean isMonospaced = width > 0;