Files
codex-bot 2b3b3a1ae4
Verify and deploy Tabularium / verify-and-deploy (push) Successful in 5m47s
Expand document collections with Pretext tutorial and simplify library navigation
2026-10-28 13:42:51 +08:00

3.2 KiB

第三章:分析阶段如何把文字变成可布局的分段

3.1 三个容易混淆的单位

名称 含义 例子
UTF-16 码元 JavaScript 字符串索引使用的存储单位 一些 emoji 的 length 大于 1
字素簇,简称字素 用户通常感知的一个文字单位 字母加组合音标、由多个码点组成的 emoji
分段 segment Pretext 为测量与换行建立的单位 一个词、一段文本、空格或特殊控制段

分段并不总等于单词。字素也不总等于一个 JavaScript 字符。理解这两个区别,后面的超长单词断行和 emoji 处理才容易读懂。

3.2 先找哪里允许断行

对 hello world,可以先想象成:

['hello', ' ', 'world']

这个示意足以解释普通英文,但真实的中文标点、日文、泰文、软连字符和不可断空格需要更多规则。

analysis.ts 的 analyzeText()先处理空白与源文本,再按引擎选择扫描器,最后形成分段及标志。其中主要的引擎对应关系是:

浏览器引擎 主要实现入口
Blink,例如 Chrome line-breaks.ts 中的 getBlinkLineBreaks()
WebKit,例如 Safari line-breaks.ts 中的 getWebKitLineBreaks()
Gecko,例如 Firefox gecko-line-breaks.ts 中的 getGeckoLineBreaks()

选择由 measurement.ts 的 getEngineProfile() 完成,并缓存 profile。未知引擎采用 Blink 的模型。

3.3 表驱动扫描是什么意思

可以把扫描器想象成一个不断读取字符的机器:

当前状态 + 当前字符类别 → 新状态 + 是否出现边界

字符属于哪一类、某种状态遇到某一类应该怎么走,都放在表里。运行时主要是读字符、查类别、查转换表、更新状态。

规则数据来自对应引擎及其 Unicode/ICU 数据,保存在 src/generated/。数据经过压缩;页面按需解开自己的引擎所用的表。这样能把复杂规则变成较直接的扫描,但也增加了包体积与初始化成本。这是有代价的工程选择。

graphemes.ts 的 findGraphemeEnds()也采用类似方式寻找字素边界。这个版本不是对所有文字都调用 Intl.Segmenter:词典分词主要留给泰语、老挝语、高棉语、缅甸语等特定文字的运行片段。

3.4 为什么空白处理要先做

white-space: normal 下,连续普通空格通常被折叠;pre-wrap 则保留空格、换行与制表符的相关行为。若测量的是原始字符串,绘制时浏览器却折叠了它,预测自然会错。

所以分析阶段必须建立与目标 CSS 相符的文本视图。这个版本对孤立 \r 有明确限制:pre-wrap 下绘制前也应把它规范成 \n,详见根目录 README。

本章检查:为什么不能直接 text.split(' ')?因为断行机会远不止空格,而且许多语言没有用空格分隔每一个词。