无用的知识:代码字体选择


字体可以有效地影响整个界面的氛围, 给人以“设计感”。 如果你经常阅读 / 修改代码, 不妨为代码挑选一款合适的字体。

考虑到现代的系统与软件往往会提供足够好的默认字体设置, 不用用户再折腾。 再加上既有的习惯并不容易改掉, 所以本文的知识没什么用1。 而且这绝不是在自嘲。

TL;DR - 我自己在用的字体是 Go Mono

等宽字体 vs. 比例字体

按照绝大多数软件的默认设置, 代码要用等宽字体来展示。 等宽字体可以做到在垂直方向上按照列数对齐。 因为,呃,每个字符宽度相等。 让空白符有语义的代码(例如 Python,YAML)可能必须要有这样的对齐, 才会比较易读。

再者, 有些代码风格会希望让跨越多行的同类语法结构在垂直方向上对齐。 如果你需要看图演示, vim-easy-align 这个 Vim 插件的 README 里面用 gif 图片演示了多个例子。 我自己倒是对这种风格没什么兴趣。

垂直对齐的另一个好处是可以在字符界面绘图。 比如现代的编程工具(包括编译器,语法检查,测试框架等) 需要绘制这样的输出,来指出源码可能出错的位置:

error: expected type, found `}`
 --> hello.rs:2:21
  |
2 |   println!("Yaye"): }
  |                     ^

当然,等宽字体还可以用来画 ASCII art。

另有一个原因是,标点符号往往不是文字(特别是正文)的重点内容。 比例字体里的标点符号的尺寸往往偏小。 而在编程语言里,标点符号都在语法结构中起着重要作用, 现代的等宽字体就会为此设计易辨识(legible,而非 readable2)的符号。

不过话说回来,如果有某种专门为编程设计的比例字体, 能解决标点符号的易读性问题, 再放弃 ASCII art 等依赖垂直对齐的需求, 倒也不是不可以用来展示代码。 而且比例字体也有自己的优势,如:

在 Reddit 上的这个讨论里面有很多信息。 可以肯定的是,这个世界上有人故意不用等宽字体写代码。

衬线体 vs. 非衬线体

当我因为开始学习 Web 前端开发而接触字体知识时, 首先了解到的知识就是衬线非衬线字体的区别。 衬线是否能改善字体的易辨识性, 传统的意见认为衬线体更易辨识, 只是这件事似乎并没有很可靠的理论基础 / 实验验证。

但是,在为代码设计的字体里, 很多字体没有严格遵守衬线 / 非衬线的限制, 而是为了把 ijlI1 这些字符分开, 故意把这些字符加上衬线, 而其余字符没有衬线(很可能是为了节省成本)。 这种 inconsistent 的设计出现在如下的流行字体:

上面提到的这些缺乏个性的字体我都不喜欢。除了 Monaco。

如果要求所有字符都加上衬线, 这倒是容易保持风格一致。 而且衬线等宽字体又非常地少见, 个性十足,极少撞……撞字?

字号

常见的字体格式 TTF 和 OTF 都是矢量字体, 可以按照用户指定的尺寸自由缩放。 不用太担心字体不支持显示器的分辨率的问题。

由于低分辨率下的反锯齿渲染往往会造成笔划边缘模糊, 有些字体为这种用例内嵌了位图(bitmap)版本。 甚至还有没有矢量的纯位图字体。 但位图字体的问题是:要为每种尺寸手动绘制, 制作成本太高。 通常只有在很小的字号上使用。

考虑到现在的显示器分辨率越发地高, 再加上视网膜屏这个产品概念的诞生, 让高端显示器的 DPI 更加地高, 上述的问题已经很少见, 位图字体也就很少纳入考量了。

字形比例

太胖或者太瘦的字体都不好看。 现代字体在设计长宽比时, 会考虑与经典字体的比例相接近 / 一致, 这样用新字体替换旧字体时, 更容易维持原有的排版设计。

Anonymous Pro 这个字体没能被我选中, 就是因为它和我以往惯用的等宽字体相比, 实在是偏胖。

一个特殊情况是, 如果用户愿意牺牲易辨识性来换取每行能显示更多的字符, 那么有可能会选择用细瘦的字体。 但是对于阅读代码这一用途而言, 牺牲易读性恐怕很难接受。

为编程设计的连字(Font Ligature)

这个玩意不知道是什么时候突然流行起来的。 简单来说,可以把 != 渲染成 。 但这种连字符往往不是字体设计者会考虑到的, 而是由第三方 patch 进去的。 与原有字体的视觉风格不一定契合。

而且,如果你要修改代码, 例如说,把 != 改成 ==。 如果有 ! 这个字符作为 anchor, 可以用搜索的方式快速把光标定位过去, 然后在 Vim 里按 r= 即可。 如果用了连字, 等于是把一个操作符原有的字符信息在视觉上隐藏了。 给编辑代码带来困难。

所以我在选择字体时, 不会考虑连字符这回事。 甚至于说,有它还不如没它。

结语

字体设计还有许多要素, 诸如,笔划粗细,支持的字符集等等。 这里不再多谈。

最终我在这里:http://programmingfonts.org/tagged/serif 找到了 Go Mono 字体: 所有字符都统一地有 Slab Serif, 宽度相对较窄,高宽比也合适。 虽然我还没有动力去学习 Go 语言, 但这个副产品让我很是满意。

延伸阅读

  1. 除非你是为程序员设计字体的专业人士。 不过那样的话,你懂得的肯定比本文要多得多,所以也就不用读下去了。 

  2. legible 主要指字体里的字符是否容易被人眼辨识。 而 readable 是指文字的意思能否被人理解。 如果你在该用 legible 的地方用了 readable,那么这种 Chinglish 就不够 readable。