Webfont(网页字体)

教程
作者:Rainer Erich Scheichelbauer
en fr zh

16 二月 2019

Whenever you want to use custom fonts on the web, you need webfonts. And whenever you make webfonts, file size will be your main concern. Learn how to properly create WOFF and WOFF2 fonts, plus some neat tips as a bonus.

当你想要在网页上使用自定义字体时,你便需要 webfont。制作 webfont 时,首先要关心的是字体文件的大小,文件越小越好。一个好的单一文种字集 webfont 大小建议在 20K 以下。

格式概览

上文提到文件大小是首要因素,其次则是浏览器支持。不幸的是,并非每款浏览器都支持全部 webfont 格式。使用 Glyphs 可以制作四种 webfont 格式:EOT、 WOFF、 WOFF2,以及普通 OpenType 字体(TTF/OTF)。以下快速概览一下几种格式:

  • EOT:嵌入的 OpenType(Embedded OpenType)。 仅受 Internet Explorer 支持;其中 IE6-8(现在的市场占有率加起来不到 0.19%)仅支持 EOT 格式,IE9 之后的版本则支持了 WOFF。查看更多关于 EOT 支持的信息。
  • WOFF:Web 开放字体格式(Web Open Font Format)。 目前浏览器支持最广泛的格式;受 Chrome 5、Firefox 3.6、IE9、Edge、Safari 5.1、Opera 11.1、Android WebKit 4.4 及更新的版本支持。查看更多关于 WOFF 支持的信息。
  • WOFF2:Web 开放字体格式 2。 拥有最好的文件压缩比,但浏览器支持尚且有限;受 Chrome 36、Firefox 39、Safari 10(仅 macOS 10.13 High Sierra 和 iOS 11 及更新版本)、Edge 14、Android Webkit 67 以及 Opera 23 支持;不被支持于 IE、Opera Mini、Android UC 以及 BlackBerry。查看更多关于 WOFF2 支持的信息。
  • 普通 OpenType:OTF 和 TTF。 用于尚不支持 WOFF 的旧版浏览器的回退格式,包括 Chrome 4(市占率 0%)、Safari 3.2–5.0(macOS 及 iOS,市占率约 0.02%)、Opera 10.1–11.0(市占率 0%)、Android WebKit 2.2–4.3(市占率 0.28%)。查看更多关于 TTF 支持的信息。

你完全可以忽略 EOT 和普通 OpenType 这两种格式。是的,尽管它们没有什么害处,但是并没有什么必要再去使用它们。在本文写作时,只有大约 0.5% 的网页浏览可能发生在仅支持这些格式的浏览器上,当你读到这篇文章的时候,这个数字可能会更少。

换句话说,请专注于 WOFF 和 WOFF2 上。现在,继续读下去吧。

导出 Webfont

当然了,要将你在“文件 > 字体信息 > 字体子样”中设置的字体导出,只需选择“文件 > 导出”(Cmd-E)并选择顶栏的“Webfont”选项即可。此时你会看到这样的对话框:

你基本上需要做三个决定:

  1. OpenType/CFF 还是 TrueType:这里的单选按钮决定了 WOFF 和 WOFF2 字体将以何种轮廓格式被导出;而 EOT 字体通常是 TrueType 格式。基于 TrueType 的字体使用部件来保持文件尺寸较小,基于 PostScript 的字体则使用一种称为子程序化(subroutinisation)的技术来控制。复杂轮廓无法被子程序化。Windows 在处理复杂的 PostScript 轮廓时存在性能问题,因此有时你会在屏幕渲染中看到差异,特别是在使用渲染提示时(详见以下要点)。最好的办法是分别测试 WOFF 和 WOFF2 在两种格式下的文件大小和屏幕显示质量,然后再做决定。

  2. 自动渲染提示:如果勾选此选项,EOT 和基于 TrueType 的 WOFF/WOFF2 中将会应用 ttfAutohint,基于 CFF 的 WOFF/WOFF2 则会应用 Adobe 的自动渲染提示算法。轮廓复杂的字体无法添加渲染提示。

  3. WOFF、WOFF2、EOT:文件将以前述的何种文件格式导出。该对话框不提供普通 OpenType 的选项,你可以使用顶栏中的“OTF”选项,或者使用自定义参数(详见后文)来覆盖界面上的选项。或者直接毋需在意,因为普通 OpenType 对网页来说真的不是一种有用的格式。

啊,对了,实际上你还要做第四个决定:导出的目标文件夹。这和通常 OTF 对话框中的内容相同,你可以预先设置一个文件夹,这样就省去了后面额外的“保存”对话框。这可以显著加快你的的导出过程,特别是当你在 1 KB、1 KB 地不断缩减 WOFF 的文件大小,需要频繁地导出时。

自定义参数

你可以在“文件 > 字体信息 > 字体子样”中,或者“.glyphsproject”文件中使用以下参数。

  • Webfont Formats(Webfont 格式):这允许你为相关字体子样指定何种导出格式。如果你要将 webfont 的制作以“.glyphsproject”格式分包,那么必须使用这一参数。一般选择 WOFF 或 WOFF2,除非你知道自己在做什么。可能还会包含过时的 EOT 和普通 TTF 文件。
  • Webfont Only(仅用于 Webfont):将在某种程度上篡改字体内部的表,这样,流氓用户将很难从 webfont 逆向工程出 OTF 或 TTF 文件。但是,我们不建议这样做。只有当客户坚持使用此参数作为额外的安全预防措施时,才可以使用此参数。
  • Save as TrueType(保存为 TrueType):强制在导出对话框中应用“TrueType”选项。
  • Autohint(自动渲染提示):强制在导出对话框中应用“自动渲染提示”选项。

不过等下,还有更多自定义参数可供你的 webfont 使用。接着读吧。

子集化

“子集化”是指将字体中(无用)的字符形删除,从而让字体文件的体积更小。基本而言,有两种相反的方法可以在 Glyphs 中实现子集化:指定需要保留的字符形,或者指定需要移除的字符形。在这两种情况下,都需要在“文件 > 字体信息 > 字体子样”或 .glyphsproject 文件中使用自定义参数来处理子集:

  • Remove Glyphs(移除字符形):提供一个字符形名称的列表,用以指定哪些字符形不会出现在导出的 webfont 中。只需键入所有字符形的名称,通过换行来分隔。
  • Keep Glyphs(保留字符形):和上述操作相同,只是这次你需要说明希望导出的字符形。字体中所有未被提及的字符形将不会被导出。

这两种情况下,你都可以通过使用“字体”选项卡(Cmd-Opt-1)右键菜单中的“复制字符形名称 > 每行一个”命令来加快速度,然后在自定义参数属性中粘贴即可。唯二不能移除的字符形是 .notdefspace

你可以使用星号(*)作为通配符,既可以放在词首也可以放在词尾。比如,你不需要逐个拼出 ordfeminineordmasculine ,只要写上 ord* 即可;或者用 *.ss05 代表“风格组合 5”中的所有字符形,甚至是用 *.ss* 代表所有的风格组合。

更棒的是,还可以使用关键通配符。它们让你能够在需要移除(或保留)的字符形列表中添加完整的分类、子分类甚至文种。一个关键通配符包含区分大小写的字符形信息键(scriptunicodecategory 以及 subCategory),后接等号,再接相应的键值。比如你想要移除全部希腊字母、全部小写字母和全部数字,那么就在“Remove Glyphs”参数中添加这几行内容:

script=cyrillic
subCategory=Lowercase
category=Number

你还可以将关键通配符和星号结合使用。比如,要删掉 Unicode 值在 U+0300U+04FF 之间的字符形,你要在参数值中这样添加:

unicode=03*
unicode=04*

好,既然我们已经掌握了用于子集化的工具,那么我们要删去哪些东西呢?这里有一些主意:

  • 小型大写:*.sc
  • 数字变体:*.tf *.tosf *.osf *.lf
  • 风格组合:*.ss*
  • 字符变体:*.cv*
  • 字符装饰:*.ornm
  • 极少见、不应使用、已废弃或无法键入的字母:AEacute aeacute Aringacute aringacute IJ ij napostrophe Oslashacute oslashacute
  • 本地化及兼容字符形:Tcedilla tcedilla Ldot ldot
  • 罕用符号:lozenge paragraph currency florin logicalnot infinity integral product summation radical micro partialdiff perthousand registered trademark bar brokenbar dagger daggerdbl estimated apple
  • 所应用的网站上不包含的语言中的字母。比如,在专门介绍荷兰文学的页面上,你很可能不需要那些只在世界语中使用的额外字符形: Ccircumflex ccircumflex Hcircumflex hcircumflex Jcircumflex jcircumflex
  • 字体的应用中所不需要的文字中的字母。在做一个保加利亚的网页?你会需要西里尔字母,可能再有一点拉丁字母,但可能不需要希腊字母的部分。所以你这样写:script=greek

还有吗?当然了,但你的目标可能各有差异。所以每次导出之后,复查一下全部字符形,看看是不是还能再去掉一个。要实现这点,最好经常导出并测试一下。你可以在诸如 OTMasterFontTableViewer 这些应用程序,或者 Wakamai FondueFontDrop 这些网页中快速打开你的字体,继续阅读下面的内容了解这方面的更多信息。不必要的字符形还会通过检测吗?漏网之鱼,不存在的。

专业提示:如果你发现自己频繁地经历导出,那么就暂且导出为常规 OTF 吧,记得不去除重叠、不添加渲染提示,这样会更快一些。一旦你调整出了完美的子集,再回头导出为成熟的、适用于生产环境的 WOFF 吧。

特性子集化

如果你做完了子集化工作,然后导出,你可能会获得像这样的报错对话框:

对话框的报错内容为“在特性代码中的 MakeOTF 错误”。通常是一个“句法错误”,然后指出一个字符形名称(本例中是 brevecomb)和具体的位置:在字体临时文件夹(Temp)里 features.fea 文件中的哪一行(本例中是第 74 行),以及哪个特性中的哪一行(本例中是 ccmp 的第二行)。最可能导致这个错误的是一个字符形名称尚在代码中出现,但指向一个已在子集化过程中被删除的字符形。换句话说,“文件 > 字体信息 > 特性”中的特性代码并没有和子集子样的字符形构成相同步。

如果出问题的是一项自动生成的特性,那么首先就不应该发生这种情况。但是如果你有一些复杂的自定义参数,那么就有可能发生。在这种情况下,你可能需要强制自动更新,只需在子样中添加这条自定义参数,当然还要勾选它的复选框:

  • Update Features:如果激活,则强制刷新全部自动生成的 OpenType 特性代码(包括前缀和类型)。

不过,如果你有非经自动生成的手动代码,你可能要考虑以下几条参数之一:

  • Remove Prefixes:指定一组前缀名称,并在导出时删除它们。
  • Remove Classes:指定一组 OpenType 类型名称(不带前面的@符号),并在导出时删除它们。
  • Remove Features:指定一组 OpenType 特性的四字标签,并在导出时删除相应的特性。
  • Replace Prefixes:提供在左侧边栏中出现的前缀名称,后接分号,后接新代码。导出时新代码会代替原代码被插入。
  • Replace Classes:提供 OpenType 类型名称(不带前面的@符号),后接分号,后接新代码。导出时新代码会代替原代码被插入。
  • Replace Features:提供 OpenType 特性的四字标签,后接分号,后接新代码。导出时新代码会代替原代码被插入。

进一步减小文件体积

经验表明,子集化能够最有效地减小文件量,不过还有一些办法能够给你的 WOFF 文件再压缩一两个 KB。要找出你的 webfont 中是什么使体积增大最多,看看 WOFF 中的表有多大。要实现这点,最好的办法是下载并安装 fonttools,然后通过在“终端”中运行 ttx -l fontname.woff ,列出字体中全部的表。或者更好的方法是,在“终端”中键入 ttx -l 后接空格,然后把 WOFF 文件拖拽进“终端”窗口,这样便会插入文件路径,然后敲回车键。如果一切顺利,你会得到一个类似这样的表的清单:

    tag     checksum    length    offset
    ----  ----------  --------  --------
    CFF   0x9C30A665     29398      2992
    GDEF  0x3C093D1F       189     32392
    GPOS  0x15445ACD     13356     32584
    GSUB  0x42EA82BB      1429     45940
    OS/2  0x68B4820A        78      1372
    cmap  0x8F4E4BFE      1042      1928
    head  0x12AB135E        52       292
    hhea  0x066A05BF        32      1340
    hmtx  0x1BC61668       994       344
    maxp  0x02435000         6       284
    name  0x76A3CF96       475      1452
    post  0xFFB80032        19      2972

看一下“length”这一列:你会发现带有 CFF 标签(Compact Font Format,压缩字体格式,带有 PostScript 轮廓)占据了最多的空间,紧随其后的是GPOS(glyph positioning,字符形定位)。CFFcmaphmtx 直接受字体中字符形数量影响,而 GDEFGPOSGSUB 则取决于所有的 OpenType 特性(包括字偶间距信息)。CFF 也是 PostScript 渲染提示信息所存储的地方。

我们来看一个典型的基于 TrueType 的 webfont:

    tag     checksum    length    offset
    ----  ----------  --------  --------
    DSIG  0x00000001         8     14124
    GDEF  0x01C701B8        28       384
    GSUB  0x4C1A4D0D       492       412
    OS/2  0x697CB056        76       904
    cmap  0xEFD48A4D       628       980
    cvt   0x0CE0037F        40     12208
    fpgm  0x9E3611CA      1729     12248
    gasp  0x00000010         8     12200
    glyf  0xE8E7B58E      8718      1608
    head  0x112593E0        53     10328
    hhea  0x088C054F        32     10384
    hmtx  0xDBC80FBB       336     10416
    loca  0x668F76DC       216     10752
    maxp  0x033E0F25        32     10968
    name  0x6F5D0BBB       348     11000
    post  0x55CEA4FD       852     11348
    prep  0x6846C89C       143     13980

在 TrueType 字体中,轮廓信息存储在 glyf 表中,通常是字体中最大的表。TrueType 渲染提示信息分散在 prepgaspcvtfpgm 中。

所以,取决于你要在何处缩减最多的字节数,你可以就下一步要采取的缩减文件体积的操作做出明智的决定。下面是一些建议。

缩减渲染提示:

你在导出基于 TrueType 的 webfont,并且勾选了“自动渲染提示”选项?在你的子样中添加一个“TTFAutohint options”自定义参数,然后在可能的地方细致调整:

  • 限制渲染提示:TTF 自动渲染提示(ttfAutohint)可以被限制在一个特定的 PPM 尺寸(以像素为单位的字号)之内。尝试把它设置得尽可能低,仅应用在真正有影响的尺寸下。因此,保持“Hinting Limit”尽量低,比如小于 50,并应用一个较小的“Hint set range”范围,只包含最重要的 PPM 尺寸,比如 12 到 32。
  • 避免诸如“Hint composites”、“Adjust subglyphs”、“Detailed info”和“ttfa table”之类的额外内容。它们通常不需设置,并且会占用相当大的空间。
  • 勾选“No Autohint Info”。这部分内容并不算大,不过能省则省吧。

如果你在导出基于 CFF 的 webfont,可能只需在最需要的字符形上应用渲染提示。考虑添加一个名为“Disable autohinting for glyphs”的自定义参数,并指定一个不需进行渲染提示的字符形的列表。

如果你的字体仅用于或主要用于 Apple 硬件:考虑完全删除渲染提示,或者——当你的 web 管理员知道怎样做时——为 Apple 设备提供专属的无渲染提示。

缩减字偶间距:

删除一些字符形后,与之相关的大量字偶间距信息便会消失。但是,剩下的字偶间距对于文件大小而言,可能仍然是个负担。

删除最小的字偶间距对。任何小于某个阈值的字偶间距都可以删掉,例如根据设计,可以选择 5 或 10 个单位。在 mekkablue 脚本中,你可以看到一个“Metrics > Delete Small Kerning pair”(删除微小字偶间距对)脚本,来帮你完成这个工作。

当然了,你应当仅在你的字体副本中这样做。

记号附加:

考虑添加一个“Remove Glyphs”参数来删除所有结合标符——当然,只在记得字体所支持的文字不需要它们时,例如拉丁字母、希腊字母或西里尔字母。没有了结合标符,Glyphs 就不会创建 mark(记号附加)和 mkmk(记号上附加记号)特性,从而减小 GPOS 表的大小。你可以通过删除所有 *comb 字符形来实现这一点。

不过当心,这可能会适得其反:基于 TrueType 的文件可能会变得更大,因为它无法在变音符号中调用结合标符作为部件了。所以,一定要测试、比较、验证。如果文件大小增加了,那么就不要删除结合标符,而考虑添加一个“Remove Features”特性,其值为:

mark
mkmk

基于 CFF 的字体在你删除结合标符时总会变得更小,因为它们一开始就不使用部件。

更多的缩减:

对于基于 CFF 的 WOFF 和 WOFF2 字体,还可以尝试禁用子程序化。WOFF 和 WOFF2 的压缩有时确实做得更好。这值得一试:在子样中添加“Disable Subroutines”参数,勾选其复选框,然后导出并查看文件大小。

测试

正如 Adobe 的 Miguel Sousa 所说,没有经过测试的字体就是破损的字体,他说得没错。但是如何测试 webfont 呢?当然是在浏览器中:Mac 上的 Safari、Chrome 和 Firefox,Windows 上的 Chrome、Edge 和 Firefox。然后怎样“在浏览器中测试”呢?你有几个选择。

首先,在导出之后,你可以在 mekkablue 脚本中运行“Test > Webfont Test HTML”。它会为当前文件(.glyphs 或 .glyphsproject)创建一个 HTML 文件,存储在你最近导出 webfont 的文件夹中,并直接打开该文件夹。在你所选择的浏览器中打开 HTML 文件,激活 OpenType 特性,键入测试文本,然后在不同字号下查看你的字体。使用这种方法还可以同时查看 CSS 示例代码。

或者,你也可以将完成的 WOFF 文件拖拽到 Viktor 和 Clemens Nübel 的奇妙页面 FontDrop 中,这是他们为 Monotype 开发的。将 WOFF 拖进来,就可以看到完整的字体。非常适合测试字体信息、渲染提示(以瀑布流展示)和字符形集合。嗬,它还能够显示边距值!

你也可能想使用与之类似的、Roel Nieskens 天才般的测试页面 Wakamai Fondue。它非常适合测试 OpenType 特性,甚至还提供了 HTML 和 CSS 示例代码,以便你轻松地复制粘贴。


Chinese translation by Willie Liu (刘育黎) from 3type (三言).