字体缺字会触发回退并改变版式
别名: 缺字 · tofu · glyph coverage
概念解释
选定的字体里没有某一个字符——生僻名「喆」、注音、欧元、emoji、少数民族文字——渲染会去下一面孔里找字形。这一找叫回退(fallback)。换来的字形来自另一套度量:宽度不同、字面高低不同、字重对不上,于是这一两个字把整行的节奏、有时把整段的换行都改掉。缺的若哪一张面孔都没有,就留下一块豆腐块(tofu)或空白。
网络字体还没加载完、加载失败后的回退,是另一组问题。这里说的是:你选的这张脸,cmap 里根本没有这个字。选字体等于选覆盖范围,不是只选风格。
机制
一行的排版是字形宽度的累加。回退字形若比原字体更宽,后面的字被往右推,可能多出一次折行,卡片高度跟着长一截;若更窄,行尾会出现一个莫名其妙的洞。垂直度量不一致时,这一个字还会在行里上下跳,像打了一个补丁。读者未必说得出「缺字」,他们看到的是「这一行忽然有个字胖了 / 瘦了 / 像换了字体」。
回退链的顺序由系统决定。同一串字符在 iOS 和 Android 上可能落到完全不同的配套面孔,品牌语气在那一两处断裂。中文尤其容易在姓名、地名、古字、括号变体上踩雷:UI 字体为了体积砍掉了扩展区,正文里一出现就跳。拉丁产品进中文市场时,常把英文面孔硬套到汉字上,汉字几乎全部走回退,等于正文从未真正「选过」中文字体,只是看起来暂时能显示。
覆盖不是「有中文」这么粗。要问的是:产品会碰到的字符集——基本汉字、扩展 A、注音、拼音音调、货币、单位、特殊连字——哪一块不在包里。缺一块,那一块的版式就不属于你选的那张脸。
边界
用户生成内容的字符集无法预封顶,回退在那里是常态,目标改成「回退链也是挑过的配套面孔」,而不是幻想零回退。代码点、科学符号、音乐记号本来就常走专用字体,主动回退是设计。品牌标题、导航、按钮这些短、可控的字符串,缺字不可接受,因为那一两处的度量跳会直接改组件宽度。字体加载过程中的闪烁和跳动是加载策略,缺字回退即使字体已经加载完也会发生,两件事不要当成一个 bug 来修。
怎么落地
- 用产品真实文案(含姓名、地名、货币、单位、中英混排)在目标字体上做覆盖检查,列出落到回退链的码位。
- 为可控 UI 字符串选覆盖足够的面孔;为用户输入准备一条度量相近的回退链,而不是听任系统乱接。
- 拉丁面孔不要假装能排中文正文。中文必须有自己选中的家族,回退只处理缺口,不处理整段。
- 把回退命中的字符在屏幕上圈出来。若圈里的字宽度、字重、基线和左右明显不是同一张脸,这一处的版式已经不属于所选字体。用户生成内容允许圈存在,标题和按钮上不允许。