浏览器中的字体回退(与操作系统不同)基于两个因素:
CSS规范在这方面相对简单,只是给出使用它们的系统名称的字体列表,但有几种可能的“万能”字体,这些字体在从计算机到计算机并没有任何保证(例如,没有理由假设serif
映射到Times
或Times New Roman
)。
文本引擎使用的回退算法完全取决于引擎,但通常在字形查找步骤中启动: 文本引擎看到一串代码点,并尝试使用字体来整形该字符串。对于序列中的每个点,它都检查字体是否具有匹配的字形(通过咨询CMAP表和子表),或者规则告诉引擎只有更多的代码点跟随时才可能使用字形,通过GSUB机制(例如,一个没有单独字母e
、t
和c
字形但有一个表示序列e
+t
+c
应该被替换为单个字形&
的GSUB规则),当它完成了这种“代码点单元”的累积,它将整形文本并将其交给请求它整形文本的任何东西。
如果在字形查找期间,发现字体不包含任何可以让引擎整形特定代码点的内容(即在运行CMAP数据和GSUB规则时仍然显示“没有字形”),那么文本引擎可以做两件事:
.notdef
轮廓,通常会给出带有可爱的空框(字体专业人士称之为“豆腐”)或问号的文本。在使用回退时,引擎可以按照备选字体列表逐个进行搜索,直到:(a) 找到一个字形或者 (b) 列表用尽,此时引擎必须放弃,并使用 .notdef
字形。无论引擎从原字体还是列表中的最后一个字体中获取 .notdef
字形,都完全取决于引擎(尽管通常为了易读性,它会选择第一个字体)
目前没有在任何地方定义这个“标准”算法;字体回退基本上是文本引擎作者提供的方便机制,就像浏览器带有书签管理器一样(方便,但不属于任何规范)。就 OpenType 而言,它并没有要求引擎应该仅提供 .notdef
当找不到字形时,或者它是否应该提供它可以处理的部分内容,然后在其他地方查找缺失的字形,以此来呈现文本。CSS 暗示你的文本引擎应该至少具备某种形式的字体回退功能,但它并没有指定它应该如何运作或何时启动。
Firefox有不同的算法来处理CJK字形和非CJK字形:
非CJK算法非常简单:尝试给定HTML语言的所有配置字体。这些包括config font.name.{generic}.{language}
和 config font.name-list.{generic}.{language}
。
CJK天生就是复杂的,由于其大量的字形、编码和语言变种。因此Firefox使用动态搜索算法来解决字形问题。
ja
) 字体。ko
) 字体。zh-CN
) 字体。zh-HK
) 字体。zh-TW
) 字体。该算法目前在GetLangPrefs()中实现。在CJK和非CJK情况下,搜索字体的数量有限制(32)。脚本搜索顺序是硬编码的,因此目前无法由用户进行配置。
Firefox回退算法的优点是由于其动态性质,可以搜索更多的字体,从而最小化用户遇到缺失字形的机会。此外,通过了解搜索顺序,用户可以操作配置以选择缺失字形所需的字体。
缺点是不一致性:由于搜索列表是硬编码的,某些语言的字体会优先考虑所有网页。例如,在缺少标签的韩文网页上可能会使用日文优化字体。此外,由于尝试了更多的字体,性能可能会变差。
与Firefox不同,Chromium选择了一种更静态的方法来搜索字体。Chromium不分CJK和非CJK情况,只为每个脚本硬编码几个“核心”字体,并假设这些字体应始终可用。映射脚本到字体可以在InitializeScriptFontMap()中找到。目前无法对此映射进行用户配置。
该算法的优点是简单、一致和高效,但代价是灵活性和可配置性。
实现方式可能会在未来发生变化。更多详情请参见https://gist.github.com/CrendKing/c162f5a16507d2163d58ee0cf542e695。