通常字体是在编辑器里用手画的:设计师摆点、拉曲线。Ipak 的特色字体——Bannai、Ganch、Metro 和 Kalam——做法不同。每个字母都由一个不大的 Python 程序按整个字体共有的规则构造。规则只有一个,所以所有字符的粗细、切口和节奏都一致——从 A 到 Ҳ,从 0 到 №。
这篇文章讲每款字体的规则是怎样的、画好的字母如何组装成字体文件,以及0.1版目前的边界在哪里。

先有母题,后有字母
每款特色字体都不是从字母开始,而是从一个一眼就能认出的母题开始:巴纳伊的砖、甘奇刻刀、塔什干地铁的金属字母、卡拉姆笔(这些技艺的历史见《巴纳伊、甘奇、卡拉姆》一文)。母题变成规则,规则变成程序,程序按它构造出任何字母。
为了在实际中比较各母题,我们把 TOSHKENT 一词放到塔什干机场的照片上,代替真正的招牌。

Bannai:错缝砌砖
字母先写成一张格子图——一行行的井号和点,就像画在方格本上。大写字母占10行。格子是正方形的,70单位。笔画占两格,字母内部的空隙不小于两格:这样字母在远处不会糊成一片。
然后程序砌砖。砖占两格,相邻行之间的砖缝错开半砖。这就是错缝砌筑:一行的砖缝与相邻行的不重合。砖与砖之间留9单位的灰缝。K、N、Z 的斜笔画按砌筑的台阶走,而不是平滑斜坡。Ö 的点、Ў 的短音符和符号 ʻ 放在大写高度以上的行里,Қ Ҳ 的尾巴和 Ş Ç 的软音符放在行线以下的行里。

Ganch:刻刀的斜切面与光
轮廓由矩形和八边形构成。竖笔画168单位,横笔画140:这样看起来一样粗。“圆”字母 O、C、G、Q 是八边形。
最后一遍是刻刀的斜切面。程序遍历轮廓,在每个直角或更尖的外角(100°以内)处切去尖端:沿两条边各退40单位,把两个点用直线连起。直角处相当于45°的斜切。内角保持尖锐:刀要从那里进去。八边形的钝角面不去动。
立体感单独由字母的图计算。对字母内部的每一点,求它到最近边缘的距离。得到一张“距离场”:边缘处为零,向笔画中间渐大。把这个场当作高度,就得到笔画中央的棱脊和通向两侧的两个坡面——V 形刻口。接着程序看每个坡面朝向哪里。朝向左上方来光的坡面变亮,背光的一面变暗。上方再加一道朝右下方的柔和阴影:字母从墙面上凸出来。对石墙,同一个场反过来用:字母变成凹槽。

Metro:细宽大写字母与楔形衬线
样本是1984年车站站台上的“КОСМОНАВТЛАР”铭文(建筑师 Серго Сутягин 和 Сергей Соколов)。笔画很细:主线48单位,横线40。字母宽,高700之下宽约600单位。
笔画末端是小小的楔形衬线。楔子的底边每侧比笔画宽出40单位,楔高34单位,顶端恰好收到笔画的边缘。词以大字距排出。Л 是“小房子”形,与车站上的一样。图形是我们自己的,按原则来;拉丁字母用同一套体系构建。

Kalam:斜置的宽笔
每个字母给定一个骨架——由点组成的中轴线。笔是长150单位、与行线成65°角的线段。程序把笔放到骨架的每个点上,把相邻位置连成四边形,再把所有四边形合并。粗细没有人设定:它由笔画方向自己得出。在65°角下,横画沉重,竖画轻细。
如果笔画几乎顺着笔尖走,它会细成一根线。所以斜笔画至少偏离笔尖角度9—10°:这样最细的线也不窄于25单位。所有字符——字母、点、数字、标点——都用同一支笔写。

每种风格里的符号 ʻ 和 ʼ
符号不是从引号里现成搬来的:每一个都按自己字体的规则画出。共同点只有一个:ʻ 的质量集中在下部,ʼ 的集中在上部,因此剪影朝向不同方向,不会混淆。
| 字体 | ʻ | ʼ |
|---|---|---|
| Bannai | 砌筑出来的“6”字形:底部是2×2格的头部,尾巴伸向右上 | 同一个形,旋转180° |
| Ganch | 质量在下的楔形,倾角10° | 质量在上的楔形,倾角23° |
| Metro | 楔子底边朝下,尖端朝右上 | 楔子底边朝上,尖端朝左下 |
| Kalam | 运笔动作:上部是发丝线,向下向左,底部转为按笔 | 同一个动作,旋转180° |
把符号向 O 和 G 靠近的是按测量做的紧排(见下文):ʻ 应读作字母的一部分,而不是一个独立的引号。
从图片到字体
图片还不是字体。字体必须以正确的间距排放字母、懂得乌兹别克语规则,并能在任何程序里打开。
**组装。**每个风格的程序交出一个字典:字符、轮廓、宽度、左右边距。组装环节把曲线转成 TrueType 格式、统一轮廓方向、经 FontBuilder 写出文件。小写字母映射到相应的大写:这些是特色字体,用于大写铭文。键盘输入的 ' ‘ ’ ` 映射到符号 ʻ 和 ʼ。
**按测量定间距。**程序取一对字母,一行一行地量它们之间的空白距离,量遍整个大写高度;很深的凹口会被裁掉,因为眼睛不会看进那里。基准是同一字体 HH、HO、OH、OO 四对的平均值。每对都向基准靠拢,但只靠一半:否则 LT 这类开放字对会粘在一起。这种针对单独字母对的修正称为紧排。拉丁字母与拉丁字母做紧排,西里尔字母与西里尔字母做。数字不做紧排,好让数字列保持整齐。
**符号 ʻ 和 ʼ 单独处理。**符号小、位置高,所以只在符号条带内——大写高度最上面的38%——测量(详见《符号条带》一文)。ʻ 紧贴 O 和 G:为 HH 空白的0.45。ʻ 之后是正常的 HH 空白。ʼ 恰好站在中间:两侧各为0.8的空白。
**禁止触碰。**所有修正之后,程序检查每对在全部高度上(包括行线以下的尾巴)的最窄处。任何一对都不能靠近到小于字号的5%。修正也有限制:向任何方向不超过字号的15%。小于20单位的修正不写入。
**乌兹别克语替换。**人们用普通撇号输入 Oʻzbekiston。字体按写在文件内部的规则自己把它换成正确的符号;在 OpenType 格式里这样的规则叫 calt。O 之后、字母之前——是 ʻ。G 之后是 ʻ,词尾也一样(BOGʻ);但词尾的 ng 组合除外,词尾的 s 之前也除外(英语所有格 's)。其余情况下两个字母之间是 ʼ,如 MAʼNO。逻辑与工作字体 Ipak Davlat 相同;详见《o' → oʻ》一文。

最后,组装环节打印试印:全部字符集和控制词——OʻZBEKISTON RESPUBLIKASI、ҚЎҚОН · ҒАЛЛАОРОЛ · ҲАЁТ、ÖZBEKISTON · TOŞKENT。

0.1版的边界
- **只有大写和一种字款。**小写用同样的大写排出。暂无多种字重。
- **Bannai 用于大型铭文。**方形砌筑接近像素字体,不适用于标牌和小字号。
- **Kalam 的笔尖角度是我们的调配。**65°是在同一个词上比较选出的,不是引自来源。
- **间距是自动设置的。**用于招牌时,值得对着具体的词用眼睛再核一遍。
- **替换不区分语言。**在特色字体里,规则对任何文本都生效:英语 DON'T 里的撇号也会变成 ʼ。对非乌兹别克语文本,宜在排版程序中关闭替换。
结语
四款特色字体每款94个字符:带 Ö Ğ Ş Ç 的拉丁字母、带 Ў Қ Ғ Ҳ 的西里尔字母、符号 ʻ 和 ʼ、数字与标点。它们都由每字体一条规则构建,并和整个字体库一样按开放许可 SIL OFL 1.1 发布。
来源
本文依据 Ipak 特色字体的构建代码和工具文档写成:fontTools、skia-pathops、SciPy、HarfBuzz、OpenType 规范;关于 Metro 的样本——Wikipedia “Kosmonavtlar (Tashkent Metro)”。
链接:
- fontTools: FontBuilder. https://fonttools.readthedocs.io/en/latest/fontBuilder/fontBuilder.html
- skia-pathops——轮廓的布尔运算。 https://github.com/fonttools/skia-pathops
- SciPy:distance_transform_edt——距离场。 https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.distance_transform_edt.html
- HarfBuzz——文字排版引擎。 https://harfbuzz.github.io/
- OpenType:calt 特性(Contextual Alternates)。 https://learn.microsoft.com/en-us/typography/opentype/spec/features_ae#tag-calt
- OpenType:kern 特性(Kerning)。 https://learn.microsoft.com/en-us/typography/opentype/spec/features_ko#tag-kern
- Wikipedia: Kosmonavtlar (Tashkent Metro). https://en.wikipedia.org/wiki/Kosmonavtlar_(Tashkent_Metro)