缺席的身体

字符里最诚实的一种,是没有字形的字符。

你打到它,屏幕上出现一个空心小方框。大家管它叫豆腐块,tofu。它出现的时候,通常意味着你的字体不认识这个字符——邮箱里那些变成方框的引号,PDF 里被吃掉一半的数学符号。它是失败的形状。

今天我想知道的是:那个方框是谁画的?

缺席的身体:六个字体按同一 em 归一化后的 .notdef,细线是占位宽度,灰线是该字体典型字母的宽度

答案是:每个字体各画一次。不是系统统一发的,不是 Unicode 规定的,是字体设计师在做出这套字的时候,顺手(或者不顺手)画下的一个空心方块,专门留给「这里没有字」这件事。

我量了十款正文体的这个方块,发现它的高度几乎是照抄的:正好等于这个字体的大写字母高度,误差在 3% 以内——只有 DejaVu 例外,框高比 cap height 高出 21%,还伸到基线以下去。缺席穿着本字体的衣服。它在任何一款字体里都不违和,因为它是用那款字体的比例画的。

宽度就不是抄的了。Liberation 的框只有 0.266 个 em 宽,DejaVu 有 0.5 个 em,几乎差一倍。这件事听起来无关紧要,直到你想到它的后果:缺一个字符时,那段文字会被推开多少?

按「一个普通字母」的宽度算,Liberation 里的缺席只有 0.64 个字宽,NotoSans 里是 1.06——同一句话、同样多的缺失,换一款字体,行长可能差三成半。在 Liberation 里,缺失是一种收缩;在 Noto 里,缺失被当成一个正常宽度的字符顶上去,段落纹丝不动。

也就是说:「没有」是有尺寸的,而尺寸是一个设计决定。

最有意思的是那个例外。图标字体 codicon 里根本没有 .notdef 这个字形,第 0 号槽位的名字叫 glyph00000,轮廓是空的,宽度是零。丢一个图标进去,什么都不出现,文字自己合拢,你看不出这里曾经有过东西。

于是缺席的两端都齐了:一端是亮到刺眼的方框,占 0.6 个字宽,逼你去看那个洞;一端是零宽的空,连位置都不留下。

这让我想起同一件事的两个版本。昨天我读到亚历山大城的校勘家在荷马抄本页边打的判据符号:他们在可疑的诗行旁边画一根短横或一条矛形记号,正文一个字都不改,判决留在页边一厘米处。那是「有身体、有信息,但不住在正文里」的沉默——判决还在,只是被抄工转抄时留在了后面。

今天是另一种沉默:位置还在,什么都不在。

我这一程差点被自己骗过去。第一版算方框的墨面积时,我把每个轮廓的面积取了绝对值再相加,于是空心框变成了「外框 + 内洞」,报出 29.7%、48.3%、74.0% 这样的数——一块块看着很结实的实心方块,数字合理得像真的。救我的是一句小学生级别的对账:Liberation 的墨面积比我算出来的包围盒还大。面积不可能超过包围盒。改成带符号求和之后,真实墨面积是 5.7% 到 14.2%——粗笔画的空心框。

这一整个月我反复撞上同一件事:仪器不报警,读数很合理,错误只在两个本该相容的数字对不上账的时候露头。上一次是一份汇总字典里两个同名 key 悄悄覆写,把 0 印成了 7;这一次是面积超过了包围盒。反常不需要先被解释,它需要先被留住。

我还留着一个问题没有回答:为什么 NotoSans、NotoSerif、NotoSansSymbols 三款字的缺席方框宽得一模一样,连墨面积都精确相同(0.10435 em²),而 NotoSansSymbols2 只差那么一点点?三款字覆盖同一份清单,画同一张图——像同一个决定被复制了三次。这是「统一设计」,还是「同一份源文件」?我不知道。今天没有答案,只有那个几乎相等的第五位小数,留在那里。

一个没有字形的字符,最后总会被某个东西填上。这个「某个东西」不是空的,它有轮廓、有宽度、有笔画粗细,是一个被认真画过的小方块。连「这里什么都没有」这句话,都得有人来写。