news 2026/5/1 5:11:27

搞跨端渲染?你绕不开的HarfBuzz原理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
搞跨端渲染?你绕不开的HarfBuzz原理

文是HarfBuzz系列的第二篇:

在这里插入图片描述

本文概述

在这里插入图片描述

一、关键概念与结构

1.1 script

HarfBuzz 中 script 指的是文字系统的类型,注意不是指语言,不同语言也可能属于同一类书写系统,比如:

hb_script 举例

HB_SCRIPT_LATIN(拉丁文) 英语、法语、德语、越南语等

HB_SCRIPT_ARABIC(阿拉伯文) 阿拉伯语、波斯语(Farsi)、乌尔都语等

HB_SCRIPT_HAN(汉字) 中文、日语汉字(Kanji)、韩文汉字(Hanja)

HB_SCRIPT_DEVANAGARI(天城文) 印地语、梵语、尼泊尔语等

HB_SCRIPT_CYRILLIC(西里尔文) 俄语、保加利亚语等

... ...

我们在从0到1自定义文字排版引擎:原理篇中有提到:

Unicode 为每个 code point 定义了一个 script 属性,文本分段时就是按 script 属性连续来划分的。

比如对于Hello世界あい,从左往右扫描字符串,每遇到 Script 改变,就切分出一个 run,最后会被划分成:

Hello→ Latin

世界 → Han

あい → Hiragana

HarfBuzz 文本塑形时,一次只能处理同一 script 的字符串,因为不同 script 的文字系统,有不同的排版规则,比如:

在这里插入图片描述

HarfBuzz内部会根据script选取不同的shaping规则。

1.2 cluster

cluster 是HarfBuzz中的概念,表示一组不可分割的字符序列。单个字母或符号可以是一个cluster,连字之后的字形也可以视为一个cluster,比如f + i连字后变成fi,fi也是一个cluster。

1.3 grapheme

grapheme是Unicode中的概念,表示书写系统中的最小单位,比如单个的字母、符号、表情等,都是一个grapheme。

cluster与grapheme的区别:

二者没有相互关系,也不是一一对应的。举个例子,f + i连字后变成fi,那最终在HarfBuzz塑形完后只有fi 一个cluster,但是底层仍然是 f 和 i 两个grapheme构成的。

HarfBuzz中一般关注的是cluster。

1.4 blob

blob 是一个抽象概念,表示一段二进制数据的容器,用来管理原始数据的生命周期和权限,在HarfBuzz中用 hb_blob_t 结构体表示。

blob 的主要作用是封装字体文件的原始数据,当我们需要加载字体传给HarfBuzz时,这些字体文件一般会被加载到一个blob对象中。

blob 只关心数据本身,不理解数据的含义,也就是说blob并不知道这是一个字体文件,更不知道里面有什么表,它只负责管理对应内存的生命周期,确保数据在被使用期间是可访问的。

1.5 face

face 表示一个单独的字体,它会解析blob中的二进制字体数据,通过face可以访问字体中的各种table,如GSUB、GPOS、cmap表等,在HarfBuzz中用 hb_face_t 结构体表示。

需要注意的是,face是不带字号、缩放和其他渲染参数的,因此face无法直接用于塑形。

如果要塑形的话,需要通过face创建font。

1.6 font

font 表示字体实例,可以在face的基础上,设置字号、缩放等feature来创建一个font,在HarfBuzz中用 hb_font_t 结构体表示。

HarfBuzz 的核心排版函数 hb_shape() 接受的是 hb_font_t 对象,而不是 hb_face_t,这是因为 HarfBuzz 在计算字形位置和前进量时,需要知道字体的大小和比例。

1.7 buffer

buffer 在HarfBuzz中表示输入输出的缓冲区,用 hb_buffer_t 结构体表示。

比如我们在调用塑形函数** **hb_shape() 时,我们的输入字符串及其属性(方向、script、语言等)都是通过 buffer 完成的,塑形完成后,塑形结果也会通过 buffer 将字形及位置信息返回。

1.8 user data

上面提到的 hb_blob_t、hb_face_t、hb_font_t、hb_buffer_t 结构体,都有类似set_user_data()和get_user_data()的方法,主要作用是方便携带用户上下文。

1.9 小结

前面我们介绍了hb_blob_t、hb_face_t、hb_font_t、hb_buffer_t等概念,这些在HarfBuzz中被称为对象类型(注意并非OOP中面向对象的概念)。

在HarfBuzz中,所有对象类型都提供了特定的生命周期管理API,对象采用引用计数方式管理生命周期,通过各种create() 方法构建,初始创建时,引用计数为1,通过 reference() 方法引用(引用计数+1),通过 destroy() 方法解除引用(引用计数-1),当引用计数为0时释放。

比如,hb_buffer_t 对象可以通过 hb_buffer_create() 创建,通过 hb_buffer_reference() 引用,通过 hb_buffer_destroy() 解除引用。

HarfBuzz 所有对象的生命周期管理 API 都是线程安全的(除非你从源代码编译 HarfBuzz 时使用了HB_NO_MT配置标志),即便对象整体并非线程安全,引用(reference())或销毁(destroy())NULL 值也是允许的。

二、塑形操作

塑形大多以来字体中的 GSUB 和 GPOS 表,这一节我们来看塑形过程中的常见的操作:

1)字形替换

一对一替换(Simplified Forms):根据feature、语言设置的不同,会进行简体 ↔ 繁体、半角 ↔ 全角、普通 ↔ 装饰体等的转换。比如在日文中会将标点符号「。」替换为它的全角版本「。」

多对一替换/连字(Standard Ligatures):比如把 f + i → 合成为 fi 连字

一对多替换/分解(Glyph Composition/Decomposition):把一个“复合”字形拆解成多个独立的字形,通常用于预处理阶段,主要为了后续定位、重音调整等更方便,一般不会影响最终的视觉效果;比如某些字体可能将预组合的 é 字形分解为 e + ´

上下文替换(Initial Forms/Medial Forms/Final Forms/Isolated Forms):根据字形在单词中的位置来替换,比如阿拉伯文字母 ه 会根据其在词首、词中、词尾或孤立出现,被替换为 هـ、ـهـ、ـه、ه 四种完全不同的形态

辅音连缀(Conjunct Forms):比如क + ् + ष 会被替换字形为 क्ष

2)字形定位:不修改字形,但是会影响position

字距调整(Kern):比如T与o 相邻时 o 的 x 偏移量为负,使它更靠近 T

标记定位(Mark):带重音符的如é,会将 ´ 的前进量设为 0,并设置 y_offset 使其移动到 e 的正上方

草书连接 (Cursive Attachment):在“草书”类文字中(如阿拉伯文 Arabic、叙利亚文 Syriac、南印度文等),字符之间并不是并排放置的,而是通过笔画自然地连接起来的

3)字形重排

主要用于印度系文字,比如逻辑顺序上 辅音क+ 元音ि 时,元音在视觉顺序上需要重排到辅音的左侧,即: कि

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/1 5:09:07

DX12-1-DirectX3D初始化

什么是 Direct3D 12?DirectX 12 引入了 Direct3D 的下一个版本,即 DirectX 的核心 3D 图形 API。此版本的 Direct3D 比任何以前的版本更快、更高效。Direct3D 12 可实现更丰富的场景、更多的对象、更复杂的效果,以及充分利用现代 GPU 硬件。若要为 Wind…

作者头像 李华
网站建设 2026/4/30 20:20:43

RFSOC学习记录(六)混频模式分析

混频混频器(Mixer)是RFSOC通过ip核实现在数字域的频率搬移,主要功能是在不改变采样率的情况下,把信号的频谱中心移动到目标频率附近他主要通过一个数控振荡器,生成复指数信号与本振信号相乘,在ip核设置的过…

作者头像 李华
网站建设 2026/5/1 5:09:54

大模型应用开发面经汇总+学习资料包,助你轻松拿下30K+AI岗位!

本文作者分享了近半年面试阿里、腾讯等20多家大模型相关公司的经验,指出大模型应用面试更注重实践而非高深理论,RAG、微调和Agent是重点考察内容。掌握微调原理并实际操作是提升面试通过率的关键。作者还提供了面试准备建议和不同公司的岗位特点&#xf…

作者头像 李华
网站建设 2026/5/1 5:10:05

Chapter-1 Memory Management (section 1.1-1.5)

操作系统为什么需要内存管理?这应该是一个很经典的问题,内存池 (Memory Pool) 也可以认为是一种内存管理的方式,所以关于内存管理四个字有点像谜底就在谜面上,更多的只是你如何管理的方式。比如 FreeRTOS 中的好几种分配方式&…

作者头像 李华
网站建设 2026/4/24 17:03:34

企业AI编程实战:可治理、可审计的完整解决方案

本文介绍了一套企业级AI编程方法论,通过三层Prompt治理模型(System、Project、Task)实现AI编程的可治理、可审计与可评估。这套体系将AI纳入工程管理而非简单工具使用,通过明确的规则约束AI行为,防止风险,确…

作者头像 李华