feitian109 的博客

不存在完美的 1:2 编程字体

什么是、以及为什么要 1:2

我们习惯使用等宽字体(Monospace)进行编程,无论是在代码编辑器还是在终端中。等宽字体保证了字符上下对齐,带来了可预测的列结构,消除了光标移动的不确定性,也保证了终端 Ascii 图形甚至是复杂 TUI 界面的成立。但是如果我们在编程中引入中文呢?原本完美的布局立马被打破了(使用的字体为 Monaspace Neon 和 Sarasa Gothic SC(更纱黑体)):

引入中文后的问题

为什么会出现这样的问题?原因在于 Monaspace 和更纱黑体的进宽(advance width)不一样,可以把它简单理解为字符的宽度。Monaspace 的进宽是 1240,Em Size(字体设计的单位大小)是 2000,那么它的字符就相当于 1240/2000=0.62 em1240/2000 = 0.62\,\text{em} 宽。更纱黑体的进宽和 Em Size 都是 1000,计算下来就是 1000/1000=1 em1000/1000 = 1\,\text{em} 宽。拉丁字符是 0.62em 宽,而中文字符是 1em 宽,难怪在混排的情况下无论怎么增删空格,制表符都不可能对齐。

想要让拉丁字符和中文字符完美对齐,只有让它们的宽度比达到完美的 1:2 才能实现。但是代价是什么呢?

现存的 1:2 方案及其局限性

使用 0.5em 的拉丁字符

既然中文字符是方块字,宽度锁死在了 1em,我们就只能让拉丁字符变得更窄了。这类方案的代表字体是 Sarasa Mono SC(等距更纱黑体)。

等距更纱黑体的拉丁字符使用的是 Iosevka,特点是 x-height(字符 x 的高度)高,ascender(字体向上伸出的长度)长,视觉上是一种细长的感觉。选择这种字体其实是为了让拉丁字符和中文字符能够视觉大小匹配,观察下面的示例,可以发现更纱黑体拥有很好的混排效果:

等距更纱黑体

缺点是 Iosevka 0.5em 的字符宽度加上细长的设计,让这款字体在纯拉丁文本的场景下看起来格外累,这也是为什么大部分编程字体都采用 0.6em 甚至 0.62em 的宽度:

Iosevka

即使这样,我认为更纱黑体仍然是 1:2 字体中不错的一个解决方案。

调整中文的字间距

0.5em 宽度的拉丁字符不好看,那么我从 0.6em 宽度的拉丁字符出发,将中文字符的进宽调整到 1.2em 不就可以了吗?还真可以,代表字体是 Maple Mono CN:

Maple Mono

但是中文想要拉到 1.2em 的进宽,还要保证是方块字,那就只能增加字间距了,这导致了下图中的结果:

Maple Mono is bad

拉丁文本看起来很完美,但是在纯中文情况下,所有字符之间像是加了空格一样。而且空间利用率也下降了,假设原本屏幕一行可以显示 120 个字符,现在就只能显示 100 个了。就个人来说,我不太喜欢这种增加字间距的做法。

如果中文是宽体

使用 Fira Mono(0.6em)组合 Glow Sans SC(未来荧黑,1.2em,思源黑体的宽体修改),会得到如下结果:

Glow Sans with Fira Mono

显然,宽体没有解决空间利用率的问题,而且我相信大部分人不喜欢宽体的中文字体。

没有完美的方案

回看上面的讨论,我们实际上面对的是一个不可能三角:

  • 严格的 1:2 对齐
  • 拉丁字符的最佳可读性(0.6em 左右的宽度)
  • 中文的标准形态(1em 宽度和零字间距)

三者只能取其二,你可以根据自身的需求来灵活选择。如果你本身使用中文较多,可以选择压缩拉丁字符;如果你使用拉丁字符较多,可以选择拉宽中文;如果你根本用不上 1:2 带来的好处,使用拉丁字符和中文各自可读性最佳的字体也是可以的。