pretext

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Pretext – DOM-Free Text Measurement & Layout

Pretext – 无DOM文本测量与布局

Pretext is a pure-JavaScript library that measures and lays out multiline text without touching the DOM. It uses the browser's own font engine (via canvas
measureText
) as ground truth, then does all line-breaking and height calculation in pure arithmetic.
Pretext是一个纯JavaScript库,无需操作DOM即可完成多行文本的测量与布局。它通过canvas的
measureText
调用浏览器自带的字体引擎作为基准,随后通过纯算术运算完成所有换行和高度计算逻辑。

Why This Matters

为什么它很重要

DOM-based text measurement (
getBoundingClientRect
,
offsetHeight
) triggers layout reflow — one of the most expensive operations in a browser. Every call forces the engine to recalculate styles and geometry for potentially the entire document. In a virtualized list with 10,000 rows, that means 10,000 reflows just to get row heights.
Pretext replaces that with a two-phase approach: one
prepare()
call does text analysis and canvas measurement (the expensive part, done once), then
layout()
computes height from cached widths using pure math (the cheap part, called on every resize). On benchmarks this is 300–600× faster than DOM measurement, and it eliminates layout thrashing entirely.
The result is pixel-perfect across browsers (7680/7680 on Chrome, Safari, and Firefox accuracy sweeps), supports all languages including emoji, mixed-bidi, CJK, Thai, Arabic, and more.
基于DOM的文本测量(
getBoundingClientRect
offsetHeight
)会触发布局重排——这是浏览器中开销最高的操作之一。每次调用都会强制引擎重新计算可能整个文档的样式和几何尺寸,如果是包含10000行的虚拟列表,仅获取行高就会产生10000次重排。
Pretext采用两阶段方案替代了传统方式:一次
prepare()
调用完成文本分析和canvas测量(这是开销较大的部分,仅需执行一次),之后
layout()
使用缓存的宽度通过纯数学计算得到高度(这部分开销极低,每次容器尺寸调整时调用即可)。基准测试显示,它比DOM测量快300–600倍,并且完全消除了布局抖动问题。
最终计算结果在不同浏览器中都能实现像素级准确(在Chrome、Safari和Firefox的准确率测试中达到7680/7680的满分),支持所有语言,包括emoji、混合双向文本、CJK、泰语、阿拉伯语等等。

Installation

安装

sh
npm install @chenglou/pretext
sh
npm install @chenglou/pretext

Two Paths: Pick the Simpler One

两种使用路径:选择更适配的方案

Fast Path (90% of cases — you just need the height)

快速路径(90%的场景:仅需获取文本高度)

ts
import { prepare, layout } from '@chenglou/pretext';

const prepared = prepare(text, fontString, options?);
const { height, lineCount } = layout(prepared, maxWidth, lineHeight);
This covers virtualized lists, dynamic containers, scroll anchoring — anything where you need to know how tall text will be at a given width. The
prepared
handle is reusable: on resize, just call
layout()
again with the new width.
ts
import { prepare, layout } from '@chenglou/pretext';

const prepared = prepare(text, fontString, options?);
const { height, lineCount } = layout(prepared, maxWidth, lineHeight);
该方案适用于虚拟列表、动态容器、滚动锚定等所有需要获取指定宽度下文本高度的场景。
prepared
句柄支持复用:容器尺寸调整时,只需传入新宽度重新调用
layout()
即可。

Rich Path (you need the actual lines)

丰富路径(需要获取实际的每行文本内容)

When you need to render text yourself (Canvas, SVG, WebGL) or do variable-width layout (flowing around images), switch to the rich APIs:
ts
import { prepareWithSegments, layoutWithLines, walkLineRanges, layoutNextLine } from '@chenglou/pretext';
  • layoutWithLines()
    — all lines at a fixed width (Canvas/SVG rendering)
  • walkLineRanges()
    — line widths without building strings (shrink-wrap, binary search for optimal width)
  • layoutNextLine()
    — one line at a time with varying width (text flowing around obstacles)
当你需要自行渲染文本(Canvas、SVG、WebGL)或实现可变宽度布局(文本环绕图片)时,可以使用功能更丰富的API:
ts
import { prepareWithSegments, layoutWithLines, walkLineRanges, layoutNextLine } from '@chenglou/pretext';
  • layoutWithLines()
    — 获取固定宽度下的所有行内容(适用于Canvas/SVG渲染场景)
  • walkLineRanges()
    — 无需构建字符串即可获取行宽(适用于收缩包裹、二分搜索最优宽度场景)
  • layoutNextLine()
    — 逐行计算布局,支持可变宽度(适用于文本绕排障碍物场景)

Key Things to Get Right

使用注意要点

  • Cache
    prepared
    handles.
    Same text + same font = reuse the handle. Only call
    prepare()
    again if the text or font actually changed. On resize, just re-call
    layout()
    .
  • Match your CSS exactly. The
    font
    string must match your CSS
    font
    shorthand (size, weight, style, family). The
    lineHeight
    must match your CSS
    line-height
    . Mismatches produce wrong heights.
  • Use
    { whiteSpace: 'pre-wrap' }
    for textareas
    where spaces, tabs, and newlines should be preserved.
  • Avoid
    system-ui
    on macOS
    — canvas and DOM can resolve to different fonts, breaking accuracy.
  • clearCache()
    is rarely needed
    — only when cycling through many different fonts and you want to release memory.
  • 缓存
    prepared
    句柄。
    相同文本+相同字体可以复用句柄,仅当文本或字体实际发生变化时才需要重新调用
    prepare()
    ,调整尺寸时只需重新调用
    layout()
    即可。
  • 完全匹配你的CSS配置。
    font
    字符串必须与CSS的
    font
    简写属性完全匹配(字号、字重、样式、字体族),
    lineHeight
    必须与CSS的
    line-height
    匹配,配置不匹配会导致高度计算错误。
  • 对于需要保留空格、制表符和换行符的textarea,使用
    { whiteSpace: 'pre-wrap' }
    配置。
  • 在macOS上避免使用
    system-ui
    — canvas和DOM可能解析为不同的字体,破坏计算准确率。
  • clearCache()
    很少需要调用
    — 仅当你切换了大量不同字体,需要释放内存时使用即可。

Ready-to-Use Examples

开箱即用示例

See the
references/examples/
directory for complete, copy-paste-ready code:
  • basic-height.ts
    — measure text height for a container
  • pre-wrap-textarea.ts
    — auto-size a textarea
  • canvas-manual-layout.ts
    — render wrapped text on a canvas
  • flow-around-image.ts
    — route text around a floated image
查看
references/examples/
目录获取完整的可直接复制使用的代码:
  • basic-height.ts
    — 测量容器内的文本高度
  • pre-wrap-textarea.ts
    — 自适应高度的textarea
  • canvas-manual-layout.ts
    — 在canvas上渲染自动换行的文本
  • flow-around-image.ts
    — 实现文本环绕浮动图片

Deep Reference

深度参考

For the full API surface (types, glossary, all function signatures), read
references/pretext-readme.md
.
For practical patterns, gotchas, and decision guidance when building with Pretext, read
references/pretext-agents.md
.
For current accuracy and benchmark numbers, read
references/pretext-status.md
.
如需了解完整的API清单(类型、术语表、所有函数签名),请阅读
references/pretext-readme.md
。 如需了解使用Pretext构建项目时的实用模式、踩坑指南和决策建议,请阅读
references/pretext-agents.md
。 如需了解当前的准确率和基准测试数据,请阅读
references/pretext-status.md