https://raphlinus.github.io/text/2020/10/26/text-layout.html

文本渲染有分为以下层次:

  • 簇 cluster
  • 脚本 script
  • 对象化 itemization
  • 富文本 rich text style 双向 BiDi
  • 段落 paragraph

![[Pasted image 20240908151204.png]]

分段

分段是最粗糙最简单的分段任务。

最常用的换行符是\n也就是\u000A,但是以下符号也可能换行:

  • U+000A LINE FEED
  • U+000B VERTICAL TAB
  • U+000C FORM FEED
  • U+000D CARRIAGE RETURN
  • U+000D U+000A (CR + LF)
  • U+0085 NEXT LINE
  • U+2008 LINE SEPARATOR
  • U+2009 PARAGRAPH SEPARATOR

AI:

  1. U+000A LINE FEED(换行符)
    • 功能:在文本中表示换行,移动到下一行的开头。
    • 实现:在大多数操作系统中,表示为单个字符 \n。在文本处理软件和编辑器中,通常用于显示文本的换行。
  2. U+000B VERTICAL TAB(垂直制表符)
    • 功能:垂直制表符通常用于在文本中指定垂直间隔,但在实际应用中较少使用。
    • 实现:在大多数情况下,操作系统和应用程序将其视为普通的文本字符,可能以不同的方式处理。
  3. U+000C FORM FEED(换页符)
    • 功能:用于指示打印系统在页面打印时移动到下一页。
    • 实现:在大多数文本编辑器中,可能会被忽略或显示为空白字符,因为大多数屏幕显示不需要明确的页面分隔。
  4. U+000D CARRIAGE RETURN(回车符)
    • 功能:将光标移动到当前行的开头,常与换行符一起使用形成“回车换行”(CR + LF)的组合。
    • 实现:通常表示为单个字符 \r,在文本处理软件和操作系统中用于处理文本的换行。
  5. U+000D U+000A (CR + LF)(回车换行符)
    • 功能:结合使用时,回车换行符表示在文本中换行并将光标移动到下一行的开头。
    • 实现:常用于文本文件和网络通信中,例如在 Windows 操作系统中,表示为 \r\n
  6. U+0085 NEXT LINE(下一行)
    • 功能:在某些文本处理系统中,用于表示换行或新的逻辑行的开始。
    • 实现:在不同的操作系统和应用程序中,可能有不同的处理方式,通常作为特殊控制字符处理。
  7. U+2008 LINE SEPARATOR(行分隔符)
    • 功能:在文本中标识行的结束或分隔不同行的段落。
    • 实现:在文本处理和排版软件中,用于分隔不同的文本行或段落。
  8. U+2009 PARAGRAPH SEPARATOR(段落分隔符)
    • 功能:在文本中表示段落的结束或分隔不同段落。
    • 实现:用于在文本处理软件中显示和排版段落。

此外,标签和属性也可以分段:<p> <br/> 等。

富文本与样式

字体的属性,比如:字体、字重、斜体,等等属性会影响布局。

一个段落会被划分成一些样式一致的片段。

有时颜色属下不会分割样式,在有连字的情况下渲染可能比较复杂。[[Text Rendering Hates You]]

双向分析 (Bidirectional analysis)

[[Unicode Bidirectional Algorithm basics]]

不同的书写方向让问题更复杂了。

项目化 Itemization (font coverage)

这是比较难搞的部分,没有通用标准和实现,不同的布局引擎有自己的实现。

这一步要在字体集中找到合适的字体,如果没有在备选列表中找到备选字体,最后的备选字体通常是系统字体。

unicode等价问题,不同的编码序列可能是等价的,但是字体的cmap中可能没有完全编码,可能需要字形引擎自己去找对应的字形。

emoji问题,有单色字体和图形两种方式。

脚本 script

字形的位置需要一系列规则,一些语言的脚本很复杂,比如:阿拉伯语,一些基本是直接映射,比如:中文。

字体中有字符到处理脚本的映射。

A simple example is “hello мир”. This string is broken into two script runs: “hello “ is Latn, and “мир” is Cyrl.

Shaping (cluster)

至此,我们已经有了确定的样式、字体、方向、和script,现在将字符串转换成有确定位置的字形(glyph),可以用HarfBuzz。

根据不同语言的要求和不同字体的描述,变成一簇字形。

其中很重要一步:在选择文本时(hit testing),需要确定字形边界和位置,因为文本和字形可能不是一一对应的,比如fi可能连字而变成一个字形,此时在选择文本范围时需要确定中间的边界,如果需要换行,还要确保相连的的字形簇不会被打断。

换行 line breaking

这里有一个问题:因为一个换行可能会影响接下来的其他换行,所以找到“完美”换行位置可能是个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