从代码到字体 · Ipak

> Ipak 的特色字体由程序构建：错缝砌砖、刻刀的斜切面与光、楔形衬线、宽笔的笔痕。每条规则如何变成字母，字母又如何组装成字体文件。

[Oʻzbekcha](https://ipak.xronocode.com/jarayon/from-code-to-font/index.md) · [English](https://ipak.xronocode.com/en/jarayon/from-code-to-font/index.md) · [Русский](https://ipak.xronocode.com/ru/jarayon/from-code-to-font/index.md) · [Español](https://ipak.xronocode.com/es/jarayon/from-code-to-font/index.md) · [Português](https://ipak.xronocode.com/pt/jarayon/from-code-to-font/index.md) · [中文](https://ipak.xronocode.com/zh/jarayon/from-code-to-font/index.md)

[制作过程]

# 从代码到字体

Ipak 的特色字体由程序构建：错缝砌砖、刻刀的斜切面与光、楔形衬线、宽笔的笔痕。每条规则如何变成字母，字母又如何组装成字体文件。

通常字体是在编辑器里用手画的：设计师摆点、拉曲线。Ipak 的特色字体——Bannai、Ganch、Metro 和 Kalam——做法不同。每个字母都由一个不大的 Python 程序按整个字体共有的规则构造。规则只有一个，所以所有字符的粗细、切口和节奏都一致——从 A 到 Ҳ，从 0 到 №。

这篇文章讲每款字体的规则是怎样的、画好的字母如何组装成字体文件，以及0.1版目前的边界在哪里。

![Ipak 的四款特色字体](https://ipak.xronocode.com/images/p/from-code-to-font/four-fonts.png)

*Ipak 的四款特色字体——Bannai、Ganch、Metro、Kalam。文本用普通撇号输入；ʻ 符号是字体的替换功能自己放上的 Ipak；由 Ipak v0.1 字体经 HarfBuzz（hb-view）渲染；字体——SIL OFL 1.1 · CC BY 4.0 · Ipak*

## 先有母题，后有字母

每款特色字体都不是从字母开始，而是从一个一眼就能认出的母题开始：巴纳伊的砖、甘奇刻刀、塔什干地铁的金属字母、卡拉姆笔（这些技艺的历史见《巴纳伊、甘奇、卡拉姆》一文）。母题变成规则，规则变成程序，程序按它构造出任何字母。

为了在实际中比较各母题，我们把 TOSHKENT 一词放到塔什干机场的照片上，代替真正的招牌。

![塔什干机场照片上的四个母题](https://ipak.xronocode.com/images/p/from-code-to-font/concepts-sheet.jpg)

*塔什干机场照片的效果图上，特色字体的四个母题：巴纳伊、甘奇、地铁、卡拉姆 照片：Alpasli, Wikimedia Commons；效果图——Ipak · CC BY-SA 4.0（照片：Alpasli；效果图——Ipak）*

**说明.** 轮廓是圈住字母的闭合曲线。字体正是以轮廓的形式存储字母的。字体单位是字号（字体大小）的千分之一。Ipak 特色字体的大写高度是700个这样的单位。轮廓之间可以做加、减和相交的布尔运算，就像图形编辑器里的“合并”“减去”“保留公共部分”。为此使用开放库 skia-pathops。

## Bannai：错缝砌砖

字母先写成一张格子图——一行行的井号和点，就像画在方格本上。大写字母占10行。格子是正方形的，70单位。笔画占两格，字母内部的空隙不小于两格：这样字母在远处不会糊成一片。

然后程序砌砖。砖占两格，相邻行之间的砖缝错开半砖。这就是错缝砌筑：一行的砖缝与相邻行的不重合。砖与砖之间留9单位的灰缝。K、N、Z 的斜笔画按砌筑的台阶走，而不是平滑斜坡。Ö 的点、Ў 的短音符和符号 ʻ 放在大写高度以上的行里，Қ Ҳ 的尾巴和 Ş Ç 的软音符放在行线以下的行里。

![Ipak Bannai：10行网格上的字母，砖块错缝砌筑](https://ipak.xronocode.com/images/p/from-code-to-font/spec-bannai.png)

*Ipak Bannai：10行网格上的字母，砖块错缝砌筑 Ipak · CC BY 4.0 · Ipak*

## Ganch：刻刀的斜切面与光

轮廓由矩形和八边形构成。竖笔画168单位，横笔画140：这样看起来一样粗。“圆”字母 O、C、G、Q 是八边形。

最后一遍是刻刀的斜切面。程序遍历轮廓，在每个直角或更尖的外角（100°以内）处切去尖端：沿两条边各退40单位，把两个点用直线连起。直角处相当于45°的斜切。内角保持尖锐：刀要从那里进去。八边形的钝角面不去动。

立体感单独由字母的图计算。对字母内部的每一点，求它到最近边缘的距离。得到一张“距离场”：边缘处为零，向笔画中间渐大。把这个场当作高度，就得到笔画中央的棱脊和通向两侧的两个坡面——V 形刻口。接着程序看每个坡面朝向哪里。朝向左上方来光的坡面变亮，背光的一面变暗。上方再加一道朝右下方的柔和阴影：字母从墙面上凸出来。对石墙，同一个场反过来用：字母变成凹槽。

![Ganch：由距离场算出的浮雕](https://ipak.xronocode.com/images/p/from-code-to-font/ganch.png)

*Ipak Ganch：V 形刻口的浮雕由轮廓经距离场算出，光从左上方来 Ipak · CC BY 4.0 · Ipak*

**说明.** 距离场是一张图，上面每个点都记着它离图形边缘有多远。计算机图形学里会用到它，比如在游戏里渲染清晰的文字。在这里它一次给出所有字符的浮雕：棱面不需要为每个字母手绘，由一个函数统一算出。

## Metro：细宽大写字母与楔形衬线

样本是1984年车站站台上的“КОСМОНАВТЛАР”铭文（建筑师 Серго Сутягин 和 Сергей Соколов）。笔画很细：主线48单位，横线40。字母宽，高700之下宽约600单位。

笔画末端是小小的楔形衬线。楔子的底边每侧比笔画宽出40单位，楔高34单位，顶端恰好收到笔画的边缘。词以大字距排出。Л 是“小房子”形，与车站上的一样。图形是我们自己的，按原则来；拉丁字母用同一套体系构建。

![Ipak Metro：КОСМОНАВТЛАР](https://ipak.xronocode.com/images/p/from-code-to-font/scene-metro.png)

*Ipak Metro：带楔形衬线、大字距的细宽大写字母，拉丁与西里尔字母同一体系 Ipak · CC BY 4.0 · Ipak*

## Kalam：斜置的宽笔

每个字母给定一个骨架——由点组成的中轴线。笔是长150单位、与行线成65°角的线段。程序把笔放到骨架的每个点上，把相邻位置连成四边形，再把所有四边形合并。粗细没有人设定：它由笔画方向自己得出。在65°角下，横画沉重，竖画轻细。

如果笔画几乎顺着笔尖走，它会细成一根线。所以斜笔画至少偏离笔尖角度9—10°：这样最细的线也不窄于25单位。所有字符——字母、点、数字、标点——都用同一支笔写。

![Ipak Kalam：平头笔的笔痕](https://ipak.xronocode.com/images/p/from-code-to-font/kalam-65.png)

*Ipak Kalam：宽150单位的平头笔在65°角下的笔痕 Ipak · CC BY 4.0 · Ipak*

## 每种风格里的符号 ʻ 和 ʼ

符号不是从引号里现成搬来的：每一个都按自己字体的规则画出。共同点只有一个：ʻ 的质量集中在下部，ʼ 的集中在上部，因此剪影朝向不同方向，不会混淆。

| 字体 | ʻ | ʼ |
| --- | --- | --- |
| Bannai | 砌筑出来的“6”字形：底部是2×2格的头部，尾巴伸向右上 | 同一个形，旋转180° |
| Ganch | 质量在下的楔形，倾角10° | 质量在上的楔形，倾角23° |
| Metro | 楔子底边朝下，尖端朝右上 | 楔子底边朝上，尖端朝左下 |
| Kalam | 运笔动作：上部是发丝线，向下向左，底部转为按笔 | 同一个动作，旋转180° |

把符号向 O 和 G 靠近的是按测量做的紧排（见下文）：ʻ 应读作字母的一部分，而不是一个独立的引号。

## 从图片到字体

图片还不是字体。字体必须以正确的间距排放字母、懂得乌兹别克语规则，并能在任何程序里打开。

**说明.** FontBuilder 是开放库 fontTools 的一部分。把字母轮廓、宽度、“字符 → 字母”对照表和名称交给它，它就组装出 .ttf 文件。

**组装。**每个风格的程序交出一个字典：字符、轮廓、宽度、左右边距。组装环节把曲线转成 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ʻ》一文。

![关闭替换与开启替换的 QOʻNGʻIROQ](https://ipak.xronocode.com/images/p/from-code-to-font/calt-qongiroq.png)

*用键盘撇号输入的 QOʻNGʻIROQ 一词在 Ipak Metro 中：上面关闭替换，下面开启替换 Ipak；由 IpakMetro-Regular.ttf v0.1 经 HarfBuzz（hb-view）渲染；字体——SIL OFL 1.1 · CC BY 4.0 · Ipak*

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

![Ipak Bannai 试印](https://ipak.xronocode.com/images/p/from-code-to-font/bannai-proof.png)

*Ipak Bannai 试印：全部字符集和三种字母的控制词 Ipak；由 HarfBuzz（hb-view）渲染；字体——SIL OFL 1.1 · CC BY 4.0 · Ipak*

## 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](https://fonttools.readthedocs.io/en/latest/fontBuilder/fontBuilder.html)

skia-pathops——轮廓的布尔运算。 [https://github.com/fonttools/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](https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.distance_transform_edt.html)

HarfBuzz——文字排版引擎。 [https://harfbuzz.github.io/](https://harfbuzz.github.io/)

OpenType：calt 特性（Contextual Alternates）。 [https://learn.microsoft.com/en-us/typography/opentype/spec/features_ae#tag-calt](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](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)](https://en.wikipedia.org/wiki/Kosmonavtlar_(Tashkent_Metro))

---
页面: https://ipak.xronocode.com/zh/jarayon/from-code-to-font/ · SIL Open Font License 1.1 · avenex@xronocode.com
