创建带有复杂轮廓的字体

教程
作者:Rainer Erich Scheichelbauer
en fr zh

20 十二月 2016

Grunge fonts, handwriting fonts, dirty letterpress fonts: if your font is complex, this tutorial is for you.

现代字体技术主要面向 “正常” 的字体。这意味着其中使用的技术会假定你的字体:

  • 具有简单的轮廓和尽可能少的节点
  • 极值点处有节点(了解如何绘制高质量的路径
  • 多个字符形共用相似的形状(例如,小写 n、h 和 m 的字肩接近相同)
  • 字符形数量合理(大部分是 3 到 4 位数)

那么如果你的字体打破了以上某种假定,我们要讨论的就是所谓的 “复杂” 轮廓了。如果你的字体中出现了这类轮廓,那么你就可能必须在子程序化、渲染提示甚至是路径调整方面做出决定。否则,你的字体可能无法导出。或者更糟的是,它能够导出,你将其发送给最终用户后,字体的表现十分糟糕,这样你就会收到大量的客户求助邮件。这不好。

为了让你对 “复杂轮廓是什么样子” 有一个认识:


字体:Adinah by Andy Lethbridge、Fairwater by Laura Worthington、Letterpress by Marcus Sterz、Weingut by Georg Herold-Wildfellner

子程序化

子程序化(Subroutinization)是 CFF 字体缩减文件体积的机制(CFF 字体即包含 PostScript 轮廓的字体,后缀名 .otf),会试图找出你轮廓中的循环结构,并将其存储在所谓的 “子程序” 中,正如其名。它和部件类似,但除了对整个字符形外,对路径和曲线也有效。子程序化会在导出时自动完成,所以你通常不需要担心它。

当你的字体中带有很多相似形状,且字体体积适中(比如包含几百个或小几千个字符形)时,子程序化的效果最好。如果形状过于多样,比如在污垢效果或扫描字体中,就会超出子程序化的极限。或者你的轮廓过于复杂精细,或每个字符中节点过多。再或者,在很多 CJK 字体中,你的字体中有很多很多字符形,比如 20 000 个或更多。 (想要了解更多内容?请阅读小林剑关于 CJK 字体子程序化的博文。)在以上任何一种情况下,你会发现导出所需时间异常的长。这是子程序化算法试图在数以百万计的轮廓中找到相似的形状。可能发生的情况是,子程序会使字体变大而非变小,因为形状的数量很大,但重复使用的却很少。甚至,也有可能字体根本无法导出。

“Disable Subroutines” 自定义参数会关闭子程序化。在 “文件 > 字体信息 > 子样” 中遍览各个子样,在每个子样的 “自定义参数” 表中,使用加号按钮添加新的参数,将 “属性” 改为 Disable Subroutines,并勾选 “值” 下方的复选框:

在以下情况下考虑停用子程序化:

  • 字体中有非常多的字符形,几千个以上;
  • 字体中形状不相似,比如污迹字体、手写字、油印效果、勒索信字体,其中的字符形需要看上去尽可能的不同;
  • 字体中轮廓高度精细,比如用于首字下沉的装饰字母、符号、图标、插画、杂锦字符;
  • 每个字符形中节点数量特别多,三位数起步。

渲染提示

渲染提示是为轮廓部件添加 “提示” 的步骤。“提示” 可以帮助栅格化渲染器确定字符形中哪一部分是基本字干,需要在栅格化拟合。“栅格化拟合” 这一过程通过扭曲轮廓使其更好地适应像素网格。

是的,你没看错。渲染提示并不会保持你的形状不变,相反,它会使轮廓变形。渲染提示牺牲形状保真度,以更好地适应小像素尺寸。结果是屏幕上的文本更干净、更清晰、更统一、更易识。不过,渲染提示对你的轮廓有很多要求:它们必须尽可能的干净,且 “正常”。

在以下情况下考虑不添加渲染提示:

  • 字体故意设计得不一致、多样化、形状之间不相似
  • 连写的手写字体中,一个字母的出笔必须和下一个字母的入笔精确相连;
  • 字体中轮廓高度精细
  • 字体中每个字符包含节点过多
  • 字体的 “文件 > 字体信息 > 母版” 中未设置对齐区域
  • 字体的 “文件 > 字体信息 > 母版” 中未定义标准字干
  • 字体带有非常规的轮廓比如:
    • 极值点处没有节点
    • 字干粗细多样化,各不相同
    • 没有全部进入对齐区域
    • 字体通过装饰效果创建,例如 Glyphs 滤镜 “粗糙化” 或 “填充轮廓”,也包括内外勾线、虚线、投影、3D 效果等。

要避免在字体中进行渲染提示,你需要做两件事:停用自动渲染提示,并去除手动放置的提示。要停用自动渲染提示只需在 “文件 > 导出” 对话框中取消勾选 “自动渲染提示” 选项。或者,在 “文件 > 字体信息 > 子样” 中使用 Autohint 自定义参数,当然了,“值” 为关闭:

要删除字体中已经手动放置的渲染提示,你可以单击 “字体” 视图中左下角的齿轮按钮,选择 “添加智能过滤器”。在随后出现的对话框中,为你的智能过滤器一个合理的名字,然后设置为 “包含渲染提示:是”。按下 “好” 确认对话框,在左侧边栏中选择智能过滤器。然后,遍历其中的字符形,并手动删除其中的渲染提示。

或者,更简单的办法是,使用删除手动提示的脚本。在我的 GitHub mekkablue 脚本库中,你会找到 “Hinting > Delete all Hints in Font”。在这个库里的 readme 文档中,阅读安装说明。

要确保最终的 OpenType 字体中没有出现渲染提示,你需要测试字体。在 Glyphs 中打开导出的 OTF,并应用我们在前几段讨论过的 “智能过滤器”。或者,在 DTL OTMaster 这样的应用程序中,逐个分析你的字符形。

路径

我们已经讨论过 “复杂路径”,即具有大量节点的路径。假设你的设计中出现了复杂轮廓,再假设你是个遵纪守法的好公民,那么你应该已经根据本地字体权威给出的建议,停用了程序化。尽管如此,仍然有可能存在问题,因为在基于 CFF 的 OTF 字体中,高数量的细小曲线段可能导致问题发生。

首先,虽然这样的字体可以在屏幕上正常工作,但是打印机很容易产生 “子路径过多” 的错误。在栅格化时,每个小曲线段会被分割成许多小线段,即所谓的 “子路径”。它们非常小,远低于栅格化分辨率的阈值。在激光打印机上,这个分辨率可能相对较高,这意味着子路径必须非常短,当然也必须非常多。因为它们必须加在一起才能构成要替换的完整曲线段。为了让你有一个概念,这是一个曲线段分割为大量子路径的放大视图:

其次,在 Microsoft Windows 中使用字体可能会遇到性能问题。比如,你输入一个由六个字母组成的单词,然后,你看着这些字母一个接一个地出现,要花去大约 10 秒钟。Windows 在处理 CFF 轮廓方面本身就有麻烦,但是据我们在雷德蒙德(Microsoft 总部所在地)的朋友所说,复杂的 CFF 对于这个操作系统而言,可能是一个过于难解的问题。

有两种方法可以解决这个问题:

首先,如果你的设计允许,把所有的曲线段换成直线段。此解决方案背后的基本原理是,直线段比曲线段更容易渲染得多。

有一个插件可以帮你实现这一点:Retractor。通过 “窗口 > 插件管理器” 找到并安装它。你可以在导出时使用 Filter 自定义参数自动触发它(更多信息详见滤镜的 readme 文档)。对于带有极小曲线段的字体,比如污渍字体、斑污字体、油印字体,通常推荐这种方法。

如果你的字体轮廓中包含较大一些的曲线段,你可以考虑使用 “粗糙化” 滤镜(“滤镜 > 粗糙化”),它会将一切路径都变成小的线段。考虑将位移值设置得较小(甚至可能是零)而段长度稍大。同样,你可以通过自定义参数触发此滤镜,只需通过滤镜窗口左下角的齿轮菜单将其拷贝到剪贴板中,然后将其粘贴到 “文件 > 字体信息 > 子样” 中的 “自定义参数” 表中。

其次,如果你的设计需要让曲线段清楚地留在原位,考虑将字体导出为 TrueType 轮廓。这里的思路是,TrueType 实际上比 CFF/PostScript 允许更高的复杂性。要将字体导出为 TrueType,你可以在 “文件 > 导出” 中勾选 “保存为 TTF” 选项,或者在 “文件 > 字体信息 > 子样” 中使用 Save as TrueType 自定义参数。这很简单。


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