Harfbuzz 是一个 OpenType 文本整形引擎。当前的 Harfbuzz 代码库,之前被称为 harfbuzz-ng,版本号为 1.x.x,它是稳定的且处于活跃的维护之中。Harfbuzz 的使用非常广泛,在最新版本的 Firefox,GNOME,ChromeOS,Chrome,LibreOffice,XeTeX,Android,和 KDE 等项目中都有应用。Harfbuzz 的代码可以在 这里 下载,也可以通过 GitHub 访问。
老的 HarfBuzz 代码库,现在被称为 harfbuzz-old,它从 FreeType,Pango,和 Qt 派上而来,可以在 这里 下载。老的 HarfBuzz 代码库目前已经不再维护了。
Harfbuzz 在代码结构上,与 harfbuzz-old 的差别非常大。前面 Behdad Esfahbod 发的那篇名为 Harfbuzz API 设计(Harfbuzz API design) 的邮件,所描述的是新 Harfbuzz API 的设计。
本文来看一下 Harfbuzz API 的基本用法。学习一个开源库的 API 的用法最方便的途径,常常正是库本身包含的一些示例程序或这测试程序。下载当前最新的发布版本 1.7.5 的源码。将源码解压缩之后,通过如下命令编译它:
编译过程将产生 Harfbuzz 的二进制库文件,和一些测试程序的可执行文件,位于 harfbuzz-1.7.5/src/test.cc 的即是其中一个测试程序。这个测试程序编译之后产生的可执行文件为 harfbuzz-1.7.5/src/test,通过如下方式执行:
即需要唯一的参数,字库文件的路径。我们对这个测试程序做一点简单的修改,让它处理泰语和缅甸语,字库文件我们使用 Android 7.1.1 代码库中,external/noto-fonts/other/ 下的 NotoSansThai-Regular.ttf 和 NotoSansMyanmar-Regular.ttf:
harfbuzz-1.7.5/src/test.cc 的完整源码如下:
接着再来看前面那段code的结构,上面这段代码执行的步骤如下:
- 读取字库文件中的数据,然后创建
hb_blot_t。
- 读取字库文件中的数据,然后创建
- 利用前面创建的那个含有字库文件数据的
blob,创建一个face。
- 利用前面创建的那个含有字库文件数据的
- 利用前面创建的
face,创建一个font。然后把字体大小的信息(ppem)及字体设计空间向用户空间转换的系数(scale)设置给font。计算ppem及scale的那段代码借用了android 4.2TextLayoutCache.cpp的一些做法。
- 利用前面创建的
- 创建一个
buffer,把文本添加进去。这个地方用UTF-16编码,是因为就手动编码 Unicode 而言,对于许多复杂语系的 Unicode 范围,UTF-16 比 UTF-8 要方便的多,因而也使我们可以更方便地修改它。
- 创建一个
- 调用 Harfbuzz 的主
shape接口执行shape动作。
- 调用 Harfbuzz 的主
- 最后从
shape之后的buffer中,取出glyph和position相关信息。
- 最后从
通常情况下对于 Harfbuzz API 的使用,大体上如上面所述。用一张图来简单说明上面的过程:

这样的用法,之所以称为基本用法,有如下这样一些原因:
- 前面的第 2、3 步中,在创建
face和font时,是直接通过字库文件的路径进行的。通常情况每个系统都会有自己的字库文件管理系统和 Glyph 管理系统,这种做法就完全没有考虑与现有系统的这些模块衔接的问题。在实际系统中,这两个对象应该通过相应有 callback 参数的那些接口来创建。 - 在 Harfbuzz API Design 中,我们看到有提到 Unicode callback 及 Script、Language 和 Direction 这些文本属性等,这些都是需要正确的设置给
buffer的,因而前面第 4 步所对应的这个测试程序的做法,所创建的buffer是不够完整的。 - 在打印位置信息时,我们看到有通过
HBFixedToFloat()这个函数来对 Harfbuzz 输出的位置信息做一个转换,转换为float格式的像素个数值。可以看到这个地方除了一个2048。这个系数在这个测试程序里用的是一个猜想的值。字体大小为36,所以猜想返回的advance值应该处于这一数量级。所以取了2048这个系数。
Done.