https://raphlinus.github.io/text/2020/10/26/text-layout.html
文本渲染有分为以下层次:
![[Pasted image 20240908151204.png]]
分段是最粗糙最简单的分段任务。
最常用的换行符是\n
也就是\u000A
,但是以下符号也可能换行:
AI:
\n
。在文本处理软件和编辑器中,通常用于显示文本的换行。\r
,在文本处理软件和操作系统中用于处理文本的换行。\r\n
。此外,标签和属性也可以分段:<p>
<br/>
等。
字体的属性,比如:字体、字重、斜体,等等属性会影响布局。
一个段落会被划分成一些样式一致的片段。
有时颜色属下不会分割样式,在有连字的情况下渲染可能比较复杂。[[Text Rendering Hates You]]
[[Unicode Bidirectional Algorithm basics]]
不同的书写方向让问题更复杂了。
这是比较难搞的部分,没有通用标准和实现,不同的布局引擎有自己的实现。
这一步要在字体集中找到合适的字体,如果没有在备选列表中找到备选字体,最后的备选字体通常是系统字体。
unicode等价问题,不同的编码序列可能是等价的,但是字体的cmap中可能没有完全编码,可能需要字形引擎自己去找对应的字形。
emoji问题,有单色字体和图形两种方式。
字形的位置需要一系列规则,一些语言的脚本很复杂,比如:阿拉伯语,一些基本是直接映射,比如:中文。
字体中有字符到处理脚本的映射。
A simple example is “hello мир”. This string is broken into two script runs: “hello “ is Latn
, and “мир” is Cyrl
.
至此,我们已经有了确定的样式、字体、方向、和script,现在将字符串转换成有确定位置的字形(glyph),可以用HarfBuzz。
根据不同语言的要求和不同字体的描述,变成一簇字形。
其中很重要一步:在选择文本时(hit testing),需要确定字形边界和位置,因为文本和字形可能不是一一对应的,比如fi可能连字而变成一个字形,此时在选择文本范围时需要确定中间的边界,如果需要换行,还要确保相连的的字形簇不会被打断。
这里有一个问题:因为一个换行可能会影响接下来的其他换行,所以找到“完美”换行位置可能是个NP难问题,进而有许多不同算法可以找到合适的换行位置,结果可能不一样。高质量的换行可能需要启发式搜索和复杂的算法。
我们的问题是找到一些候选的换行位置,使一行的宽度满足段落宽度。
英语也有音节结构,如果换行需要打断一个单词,连字符不能出现在音节之内(似乎是计算机领域一个未解决的问题:Syllabification | Is there any perfect syllabification algorithm in English language? https://en.wikipedia.org/wiki/List_of_unsolved_problems_in_computer_science)
安卓有一个 Minkin
库处理换行,微软有 DirectWrite
,苹果有 Core Text
,GTK用 Pango
。