在“汉字字形的编码方法”一文中,我们了解了字符编码的原理和汉字字形的复杂性:异体字(相同字义的多变字形)和古文字(非标准字形),以及基于汉字部件的动态组字方法。但还留下了一个问题——如何将抽象字符的不同字形在数字媒介中显示?即字形的数字化表示和渲染方法。本文重点关注字形的数字化表示方法,以及在文字的排版使用中,如何实现手写连笔的特效?
这种手写风格字体,可以根据上下文字符动态搭配可变字形,从而实现连笔手写的特殊效果,广泛用于邀请函、婚礼请柬、名片、书籍封面、广告、海报和品牌标识等设计场景,以传达一种手工艺感和优雅的氛围。这种可变字形的技术能否应用于汉字行草书风格的字体设计中呢?
如上图所示,在行草书的书法作品中,存在字与字之间的笔画连接,也被称为自然书写的“连笔字”。目前,可以从方正字库、汉仪字库等字体公司下载到赵孟頫行书风格的字库,在文本编辑/排版软件中使用这个字体,可以达到与上图(左)相似的效果。
- 是否可以实现上图(右)这样的连笔特效呢?
答案是肯定的,需要字体设计师设计字形的多种变体(Variant),并定义好触发变体的上下文规则(Contextual Alternates)。 - 为什么在中文字库中很少见这样的连笔字效果?
主要因为汉字数量繁多,如果要设计一整套的字间连笔规范,其数量是成指数递增的,单纯靠人力很难完成,需要更高级更智能的造字软件及技术的支撑。
本文的内容有关字体设计和排版特效背后的相关技术,主要包括:
- 字符、字符集、字形和字体的关系;
- 字形的数字化表示方法:点阵字形和矢量字形;
- 字体标准与字体特效:TrueType、OpenType和WOFF/WOFF2。
什么是字符、字形、字体?
- 字符(Character):字符是抽象的,代表一个特定的字音或意义。字符可以是字母、数字、标点符号、汉字或任何其他书写系统中的书写单位,甚至Emoji表情等。
- 字符集(Character Set):字符集是一组预定义的字符的集合。它是对字符进行分类和组织的方式,以便在计算机系统中使用。字符集中的每一个字符有一个唯一的 ID,叫做 Code Point(码点、内码)。常见字符集:ASCII、GB2312、GBK、Unicode 等。
- 字形(Glyph):字形是字符的具体表现形式,即字母、数字、汉字或符号的实际形状或视觉设计效果。同一个字符在不同的字体中可以有不同的字形,如英文字母"A"是一个字符,而Times New Roman中的"A"、Arial中的"A"等是该字符的不同字形,具有独特的设计。对于每一个汉字字符,宋体、黑体、仿宋、颜真卿楷书、邓石如隶书、赵孟頫行书、王羲之草书等等,都属于不同的字形。
- 字体(Font):字体是指一组字形的集合,这些字形按照特定的风格和设计规则排列,用于显示文本。一个字体中包括了所有字符的字形(如大写字母、小写字母、数字、标点符号、简/繁体汉字等),以及可能包含的额外视觉元素,如连字、特殊符号等。举例:Times New Roman、Arial、宋体、黑体、赵孟頫行书等都是字体的名称,每个字体都有其独特且统一的字形设计风格。每个字体所支持的字符集可能不同:有些中文字体仅包含GB2312简体中文字符集(6763个字符),有些包含GBK简/繁体中文字符集(约2.1w个字符),有些则是包含87,887个汉字的GB 18030-2022字符集。
总结来说,字符是语言的抽象单位,字形是字符在视觉设计上的具体表现,而字体则是包含了一系列字形和排版信息的集合。用户安装了相应的字体,即可在各种数字媒介上显示或打印出不同风格的字形。字体文件中包含了字形数据、排版特效和指令,用户安装了该字体,即可在各种数字媒介上渲染出(显示或打印)相应风格的字形。
那么,问题来了:
汉字字形如何用数据表示?
如何将字体中的字形数据渲染成图像?
如何实现特殊的排版效果?如连笔字。
字形如何用数据表示?
点阵字形(bitmap)
比较直观的数字化表示方法是点阵字形。
点阵字形通过在固定尺寸的网格上排列像素点来定义每个字符的字形,网格的尺寸表示原始字号的大小。由于点阵字体是基于像素的,只能在特定分辨率下提供清晰的显示效果,在放大时可能出现锯齿或失真。点阵字体通常用于对图像质量要求不高或显示设备分辨率较低的应用场景,如LED屏和售票机的单据打印。
数一数👆点阵字体用到的像素行数和像素列数。与英文不同,汉字点阵字库设计要难得多:汉字字形复杂,在小尺寸下设计空间小,这是原因之一;汉字字数庞大,开发所需的成本较高,这是原因之二。下面以三言(3type)的点阵字体为例,具体分析:
1)汉字字形的复杂程度变化大。简单如“丁”只有两笔,复杂如“矗”仅横就有12行。16×16的方格可以将大部分汉字清晰而完整地显示出来;过于复杂的字,则可以进行简化。我们在阅读汉字时,主要是通过抓住字的整体造型来识别的。
2)为什么偏偏是16像素的方格?这个尺寸的由来有几方面的原因:电子显示要以二进制为基础,实际常用的是2的n次方或8的倍数。如果要为方块汉字设定一个格子,那么它的边长可以是4、8、16、24或32。使用的像素点数决定了整个字的显示大小。在常见的LED显示屏中,相邻两个像素的间距大约为7mm~10mm,这样16像素的字就有10-20厘米大,是一个在一定距离上可以一目了然的尺寸。
3)在排版时,字格之间是不留空隙、紧密排列的,为了让汉字不至于连在一起,要留出一排像素作为间距。这样一来,实际每个字符可供使用的像素为15列×16行。以上图为例,绿色为二进制(0/1表示是否显示),白色为对应的十六进制,最右一列数字全零。每行由2个字节表示,每个字符由32字节表示。可以计算一下,字库如果要支持GB2312字符集的6763个字符,需要至少多少存储空间?
即使是在这样有限的条件下“螺蛳壳里做道场”,设计师仍然有很大的发挥空间——通过对整体风格的把握和对细节的处理方式,可以得到显示风格迥然相异的字体。以3type研发的「丁卯点阵体」为例,「丁卯点阵体」是一套像素风格的中文字库,设计初衷是探索小尺寸像素汉字的可能性。经过实验,确认汉字在最低 7×7 的像素网格中仍然可读——这一尺寸,是上述最常见的国标点阵字(16×16)的四分之一。
扩展阅读:
- 在上个世纪的DOS操作系统时代,如何显示中文?
回顾初代中文信息系统,可从这几个名字(respect)按图索骥:严援朝(首个汉字操作系统CCDOS,中国第一代最著名的程序员,1986年获国家科学进步二等奖)、吴晓军(2.13)、鲍岳桥(UCDOS)、朱崇君(CCED)、求伯君(WPS)、王永民(五笔字型输入法)。
矢量字形(vector)
点阵字体在使用中存在的最大的问题是很难进行缩放,特定尺寸的点阵字体只能清晰地显示在相应的字号下,否则字形只能被强行放大,产生成马赛克式的锯齿边缘,造成字形失真。
在上世纪80年代后期,Adobe 推出了矢量字形标准 Type1,使用矢量的图形来表示字符。矢量字形在放大缩小的时候不会遇到像点阵字体那样失真的问题。同时,Adobe 还推出了用于印刷的语言 Postscript。
进入到图形化操作系统时代,在使用文本排版软件时,可选的字号大小组合有无数种,很难为每种字号都定义一个点阵字库,且这样占用的存储空间也会很大。于是微软和苹果都想在自己的操作系统中引入矢量字体,但是因为Adobe的授权太贵了,因此两家公司联手开发了自己的矢量字体(TrueType)和打印技术(TrueImage)。
矢量字形把每个字符的笔划分解成各种直线和曲线,然后记下这些直线和曲线的参数,在显示的时候,再根据具体的尺寸大小,渲染出这些线条,来还原出字形。
控制点和轮廓
在矢量字形中,使用直线和曲线的组合以构建复杂的字形。实际上,在最底层,TrueType 字体中的每一个字形都是由网格上的点来描述的。
通过两个点可以得到一条直线。通过三个点可以得到一个二次贝塞尔曲线 (Bezier quadratic curves)。在这种情况下,有两个点作为曲线的起点和终点,另一个点为在曲线外的控制点。这三个点可以确定一条唯一的曲线。
二次贝塞尔曲线的完整定义如下:
给定三个点 p0、p1、p2。控制点 p1 位于点 p0 和 p2 处的切线与曲线的交点处。 因此,p0p1 与点 p0 处的曲线相切。 类似地,p2p1 与点 p2 处的曲线相切。 由这三个点指定的曲线由参数方程定义。
对于从 0 到 1 的 t,p(t)的位置可以用这个方程式表示:
p(t) = (1-t)2p0 + 2t(1-t)p1 + t2p2由此可以得出这条曲线。
更复杂的曲线则可以使用多段二次曲线连接得到。两段曲线的切线相互连接,可以产生二次样条(quadratic spline)。 如果每个曲线上的点都位于连接其两侧的两个侧翼控制点的线上,则连接的二次曲线具有一阶连续性和切线连续性。
例如上图,由 p0、p1、p2 三个点定义了一条曲线,再添加一个曲线上的终点 p4,并在 p1p2 延长线上添加控制点 p3,这样就得到了由 p2、p3、p4 定义的一条新的曲线。这条曲线与第一条在 p2 处相连,并与第一条具有一阶连续性。
在这五个点中,由于 p2 是可以由 p1,p3 和其他数据推算出来的,因此可以去掉点 p2 来简化这条曲线的表述。由此我们可以得到一条这样的曲线:
最后,通过线段之间的相互连接,可以得到一个封闭的字形轮廓,如下图字符 C。构成字形的轮廓线不一定只有一条,如下图字符 B 就是包含 3 条轮廓线。在创建一个字形时,还需要每个字形中轮廓的方向,浅显的讲就是控制点要按顺序排列。轮廓的方向同时也定义了一个字形中哪些地方是实心哪些地方是空心的。
总结:字形的矢量图形由若干轮廓组成,每个轮廓由若干条线段或贝塞尔曲线闭合而成。根据字体格式不同,使用的贝塞尔曲线的阶数也有区别。TrueType(TTF)字体采用二次贝塞尔曲线(3点控制),而 OpenType(OTF)字体采用三次贝塞尔曲线(4点控制)。OpenType 兼容两种轮廓格式:TrueType 轮廓和 Type1 轮廓。Type1 轮廓在某些高级打印和显示设备上可能提供更精细的渲染效果。
矢量字形的优点:
- 可以随意放大缩小而不失真
- 所需存储量和字号大小无关
控制点的坐标体系
即便字体设计时是矢量的,但输出设备(显示器或打印机)终究还是点阵式的网格。将矢量字形轮廓转换为点阵位图的一种简单方法是“栅格化”,即将字形轮廓叠加到光栅网格上。中心位于轮廓内或轮廓上的所有像素都将成为所生成的位图图像的一部分,其他像素则不是。
上图显示了使用此规则对圆形进行栅格化的结果。该图揭示了这种简单栅格化技术的一些缺点。具体来说,结果会随着轮廓在光栅网格中的精确位置而变化,并且生成的形状既不左右对称,也不上下对称。而圆形是需要对称的。
如何在不同分辨率的输出设备上保障字形轮廓的重要属性(如对称性)?这类问题不仅具有理论价值,还会影响文字的可读性。不受控制的栅格化所带来的常见问题包括字干粗细不均匀、字迹丢失、字符特征丢失和衬线不对称等。
如上图所示,带来的问题是小尺寸的字母难以辨认,大尺寸的字母则不美观。虽然随着尺寸和分辨率的增加,可读性会提高。但即使每 em 像素数相对较高,优雅度和对原始设计的保真度仍然存在问题。
以上图中的字符 e 为例。这里栅格化生成的位图(左)清晰但不优雅,其中最明显的问题是 e 顶部的寡像素和下部曲线的锯齿状等。TrueType 字体语言通过在栅格化之前变形字形轮廓来修复栅格化意外造成的问题,这种技术被称为“网格拟合(Grid-fitting)”(中)。图中,深色轮廓为浅色轮廓经过“网格拟合”后的轮廓。再将其栅格化,得到令人满意的位图(右)。
因此,在创建字形轮廓时,字体创建者会使用一个虚构的正方形区域,作为控制点的参照系。这个区域被称为”em square”。设计好一个字形后,就要把这个字形放到网格中,确定其在网格中的相对位置。以 TrueType 字体文件为例,主网格是一个二维坐标系,横纵向都分别至多有 32768 个单位(正负各 16384 个单位),这个单位被叫做 FUnit(Font Unit)。所有的点位置坐标都是以 FUnit 为单位来定义的。FUnit 是字体设计中的一个基本单位,是 em 方块中最小的度量单位,而 em 方块是一个用于衡量字形大小以及对齐字形的虚拟正方形参考区域。
Em 方块中高和宽的单位数量(也就是横纵坐标上单位的数量)反映了网格的密度。每个正方形区域中网格的密度越高,表现出的字体形状就越精细。但是字体设计者也必须考虑到输出设备的最大分辨率,以达到理想的效果。
例如:你给一款手机设计了一个外壳,设计精度是0.01厘米,意味着只要有 0.01 厘米的偏差都能让你感觉到不自在。然而实际拿去生产的时候却发现机器的加工精度只能做到 0.1 厘米,因此生产出来的手机壳怎么摸都会让你感觉不自在。只有当你的设计精度和实际生产时能达到的精度达到平衡时,才能拥有最佳的效果。例如上图中的字母 A,设计出的轮廓是左上方的样子,但如果放到一个较低密度的网格中,就不能很好的表现字体设计的细节,而放在较高密度的网格中,则可以更准确地还原字体的轮廓。
在将文字的轮廓置入到网格中后,还需要对文字缩放、网格拟合轮廓、设备优化等进行调整和声明。这些内容与字体渲染方式有关,具体将在另一篇文章中介绍。
字体度量
字体度量在排版中非常重要,它决定了字符之间的间距和对齐方式,确保文本在屏幕或打印上的合理布局。字体度量是指字体中字符的尺寸和位置信息。它包括了字符的宽度(width)、高度(height)、上沿线(ascender)、下沿线(descender)、基线(baseline)等数据。
扩展阅读:
- 如何将点阵汉字矢量化?(https://gist.github.com/bendmorris/7170209)
字体标准与字体特性
字体标准中定义了字形轮廓在字体文件中的表示格式。字体特性(font feature)或变体(variant)指的是在同一个字体文件中包含的不同字形或字符样式。
字体特性就像字体中的密室。打开它们,你将发现,你可以让字体或微妙或显著地展现出它的另一面。这些特性对于出色的排版至关重要。
——Tim Brown(Adobe 排版设计负责人)
在Web开发中,CSS提供了font-feature-settings
、font-variation-settings
、font-variant-position
属性,允许开发者调用字体特性,从而在网页上显示各种字形样式。
TrueType
TrueType 是由 Apple 和 Microsoft 联合提出的一种矢量字体标准。它用二次贝塞尔曲线描述字形轮廓,含有字形构造、颜色填充、数字描述函数、流程条件控制、字体微调(Hinting)等指令。
- Hinting:字体微调是指使用网格拟合(Grid-fitting)指令,指导字体渲染引擎调整字形轮廓的显示,使其与栅格化网格对齐,尽可能恢复轮廓像素的美观,保证可读性。在低屏幕分辨率下,微调对于生成清晰易读的文本至关重要。抗锯齿和液晶显示器的子像素渲染技术可让已微调的字体进行进一步清晰化。
OpenType
Adobe和Microsoft从1995年开始着手联合开发 OpenType 项目,该项目除了开发一种新型的跨平台字体文件格式外,还旨在为用户提供具有更丰富排版特性的字体格式。该字体格式已经成为一种业内标准,获得所有主流操作系统和软件的支持,越来越多的字体厂商也将自己的字库升级到 OpenType 字体格式。
- 可变字体(Variable Fonts)
2016年9月,在1.8 版 的OpenType 规范中加入了这种支持无级变换的功能——可变字体。除了上图中的字重无极变换之外,还支持更多丰富的字形变化(如宽度、倾斜度、光学尺寸、字内空间、颜色等等),这些变化还可以叠加,使得用户可以自由调整文字的外观。此外,这些轮廓点上的变化量以变量集合 (delta set) 的方式贮存。因此,变化量数据所占用的存储空间很小,从而显著减小了字体家族的文件大小。使得在网页等应用场景中,即可以满足更多字体样式的需求,同时减少了页面加载的时间。
可变字体支持自定条件下的字形替换 (Conditional Glyph Substitution)。例如,当字重改变到一定程度时,字形本身的结构或骨骼也需要改变,比如大写 Q 的脚需要不穿透圆形内部。如果仅使用轮廓变化来做,那么某些轮廓点可能会出现奇怪的字形。为了解决这个问题,可变字体支持设计好另外一个可供替换的字形,并且默认隐藏,只有在插值变换达到一定程度时才会激活,直接替换原有的字形。不仅可以更美观,这种功能也可对非拉丁的复杂书写系统设计作出贡献。
提到复杂的书写系统,这里先跑个题,简单了解一下阿拉伯文字的奇妙之处(字母在单词中不同的位置有不同的字形),如下图所示,每一行是一个阿拉伯文字的字母,最右边白色的是原形(独立状态),左边三列表示了该字母在词中不同位置的不同写法。
如果你尝试输入阿拉伯文就会发现,每敲一下,整个单词就会变一点,直到整个单词输入完成。如下图所示, Windows 系统自带的 OpenType 字体 Aldhabi,实现了传统的阿拉伯书法效果。可见,阿拉伯文的书写系统相比拉丁体系更加丰富多变,更有创造性发挥的空间。同样的,中文的传统书法,在字体设计领域仍存在很多值得挖掘的空间。
可以用 FontForge 软件打开 aldhabi.ttf 查看字体设计细节。
OpenType 字体支持更丰富的字符替代和高级排版特性,如连字、连笔、上标和下标、大小写、分数、花体字等,这些特性在渲染时可以提供更专业的排版效果。例如分数(½ 而不是 1 / 2),以及下文重点关注的连笔特性。
- Ligature
连字(合体字),是指由多个字符组合而成的单个特殊字形,如下图fi。即用单个连字替换多个字形,可以使字母间距更均匀,阅读更流畅。
把两个或多个汉字挤压到一个汉字的空间内,成为一个连字/合字,在“等宽”的汉字体系下,这种合字的视觉效果是很显著的。以日本的年号为例,每出现一个新的年号,日本各大字库厂商都要赶制该年号的合字,以便第二天各大报刊所用。这样的设计就要求把两个字写的很瘦,或者很扁。同时需要将这两个字写的比例相当,而不是有主次关系。
在古人的书信手札中,“顿首”类似现代的“此致敬礼”,用在文末表示致敬。在很多古人的行草书作品中,“顿首”二字的高度草化,在某种意义上,也是把这两个字融合成一个符号化的新合字。在王羲之的草书中,不仅“顿首”,还有“再拜”、“奈何”等高频词都出现了相似的现象。类似的合字还有宋徽宗的“天下一人”花押,“八大山人”的落款等等,是否也可以将其设计到中文字库中呢?
- Contextual alternates
上下文连笔,与Ligature连字不同,不会合为单个字形。允许根据上下文环境来替换特定的字形。这种特性主要是通过连笔提高文本的视觉连贯性和可读性,尤其是在复杂的字体设计中,比如手写风格、书法风格或装饰性字体。
feature calt {
# GSUB feature: Contextual Alternates
# The character ' is used to mark the target of the rule.
sub b' o' r by b.alt o.alt; # replace two glyphs with two glyphs
sub o' v e' by o.alt2 e.end; # replace two glyphs with two glyphs
} calt;
- 如何实现汉字行草书的连字效果?
Contextual Alternates的实现需要精心的字体设计和编程应用,以确保上下文连笔效果不仅在视觉上吸引人,而且在阅读时也能提供流畅的体验。通常依赖于以下几个步骤:
- 字形设计:设计师首先为字体创建多种字形变体,这些变体会根据它们在文本中的上下文环境来使用。
- 锚点设置:在字体设计软件中,如 FontForge 、FontLab 或 Glyphs,设计师会为特定的字符设置锚点,这些锚点定义了字形的起始和结束位置。
- 类组定义:设计师定义不同的字形类组(glyph classes),这些类组帮助识别哪些字符可以触发上下文连字效果。
- 规则编写:在OpenType特性中,设计师编写特定的规则来指定在什么情况下使用哪个字形变体。这些规则考虑了前一个字符、后一个字符或一系列字符。
- 字体表构建:使用字体设计软件,设计师构建 OpenType 布局表(GPOS和GSUB表),这些表包含了字形类组、锚点和替换规则。
- 测试和调整:在字体投入使用之前,需要进行广泛的测试,以确保替换在不同的文本环境中都能正确工作,并且视觉效果良好。
- 软件支持:最终,字体需要在支持OpenType特性的软件中使用,如Adobe Illustrator、InDesign或其他专业的排版软件,以及网页浏览器。
- CSS控制:在Web开发中,可以通过CSS的
font-feature-settings
属性来启用或配置上下文连笔效果,例如:font-feature-settings: "calt";
。
- Swashes
中英文的花体字效果:
WOFF和WOFF2
WOFF(Web Open Font Format)和WOFF2是专为网络使用而设计的字体文件格式,它们旨在提高网络字体的加载速度和压缩效率。TTF和OTF文件可以通过工具转换到WOFF或WOFF2的格式。
扩展阅读:
1)字体分析工具
有时,麻烦的问题在于你没有字体的说明文档(正是因为这个,许多字体设计师和厂商会提供示例页面和 CSS)。也可以访问 wakamaifondue.com,把字体文件拖到提示的圈内,过一会就可以得到该字体的所有功能、特性的完整报告。Axis-praxis.org 也提供类似的功能,只需在相应的文本块中点击一下特性,就可以打开或关闭它们。
2)完整的 OpenType 特性的 CSS 演示
本文小结
本文深入探讨了汉字字形的数字化表示方法,重点关注了手写/书法风格的字体特效。两种数字化表示方法:点阵字形和矢量字形。点阵字形通过在网格上排列像素点定义字形,适用于简单的显示设备;矢量字形则使用数学公式描述字形轮廓,可以无损缩放。有关矢量字体的渲染没有过多展开,主要分析了在低分辨率下的栅格化失真问题,以及 TrueType 字体中相应的网格拟合(Grid-fitting)技术。进而,用大量示例展现了 OpenType 字体所支持的高级排版特性,如连字/合字、上下文替换/连笔和花体字效果等。此外,还介绍了WOFF和WOFF2格式,这些格式为网络字体优化了加载速度和压缩效率。
尽管在 OpenType 字体中已经能够实现复杂的字体特效,但汉字由于其数量庞大和形态复杂,实现类似手写/书法风格的连笔字效果仍然面临挑战,需要更高级的技术支持和更智能的造字软件。这也是一个非常有价值的科研方向。
参考资料
- Apple, TrueType Reference Manual
- Microsoft, Microsoft Typography documentation
- Tal Leming, The OpenType Cookbook
- John Hudson, Introducing OpenType Variable Fonts, Medium
- 3type, 丁卯点阵字体
- 华康字型, 华康金花体
- 谭沛然, 参数化设计与字体战争:从 OpenType 1.8 说起, TheType
- 郑初阳, 中文合字的探索, 3type
- eastecho, 如何将点阵汉字矢量化, Indienova
- 可变字体目录网站, https://v-fonts.com/
- 可变字体排版试验网站, https://play.typedetail.com/