Skip to main content
All types are exported from @chenglou/pretext and re-exported from the package’s main entry point.
An opaque handle returned by prepare(). Pass it to layout().
declare const preparedTextBrand: unique symbol

type PreparedText = {
  readonly [preparedTextBrand]: true
}
PreparedText is intentionally opaque — its internal structure is not part of the public API. Do not attempt to read or construct it directly. Obtain one from prepare() and pass it to layout().If you need access to the underlying segment strings (for custom rendering), use prepareWithSegments() instead, which returns PreparedTextWithSegments.
Extends PreparedText with an exposed segments array. Returned by prepareWithSegments(). Pass to layoutWithLines(), walkLineRanges(), or layoutNextLine().
type PreparedTextWithSegments = PreparedText & {
  segments: string[]
}
segments
string[]
The segment strings produced during the prepare phase, aligned with Pretext’s internal parallel arrays. For example, ['hello', ' ', 'world']. Use these to reconstruct or render individual line fragments when building a custom renderer.
Treat PreparedTextWithSegments as an escape hatch for experiments and custom rendering. For pure height measurement, prefer the opaque PreparedText from prepare().
Optional configuration passed as the third argument to prepare() and prepareWithSegments().
type PrepareOptions = {
  whiteSpace?: 'normal' | 'pre-wrap'
}
whiteSpace
'normal' | 'pre-wrap'
Controls whitespace handling during text analysis.
Returned by layout(). Contains the computed line count and total block height.
type LayoutResult = {
  lineCount: number
  height: number
}
lineCount
number
The number of wrapped lines produced at the given maxWidth. For example, 3.
height
number
Total block height in pixels. Equal to lineCount * lineHeight, where lineHeight is the value passed to layout(). For example, 57.
A position within a PreparedTextWithSegments handle, expressed as a segment index and a grapheme index within that segment. Used as start and end on LayoutLine and LayoutLineRange.
type LayoutCursor = {
  segmentIndex: number
  graphemeIndex: number
}
segmentIndex
number
Index into the segments array of the PreparedTextWithSegments handle.
graphemeIndex
number
Grapheme index within the segment at segmentIndex. 0 at segment boundaries; non-zero only when a word was broken mid-grapheme by overflow-wrap: break-word.
Pass a line’s end cursor as the start argument to the next layoutNextLine() call to stream lines one at a time.
A single laid-out line, including its rendered text, measured width, and start/end cursors. Appears in the lines array of LayoutLinesResult and is returned by layoutNextLine().
type LayoutLine = {
  text: string
  width: number
  start: LayoutCursor
  end: LayoutCursor
}
text
string
The full rendered text content of this line. For example, 'hello world'. Trailing whitespace is not included; a trailing visible hyphen is included when a soft hyphen was chosen as the break point.
width
number
The measured rendered width of this line in pixels. For example, 87.5. Trailing whitespace does not contribute to width.
start
LayoutCursor
Inclusive start position of this line in the prepared segment stream.
end
LayoutCursor
Exclusive end position of this line. Pass as start to the next layoutNextLine() call.
Like LayoutLine but without the text field. Produced by walkLineRanges(). Useful when you need line geometry (widths and cursors) without paying the cost of building line text strings.
type LayoutLineRange = {
  width: number
  start: LayoutCursor
  end: LayoutCursor
}
width
number
The measured rendered width of this line in pixels. For example, 87.5.
start
LayoutCursor
Inclusive start position of this line in the prepared segment stream.
end
LayoutCursor
Exclusive end position of this line.
A common pattern is to use walkLineRanges() to binary-search for a satisfying container width, then call layoutWithLines() once with that width to get the full LayoutLine objects:
import { prepareWithSegments, walkLineRanges, layoutWithLines } from '@chenglou/pretext'

const prepared = prepareWithSegments('AGI 春天到了. بدأت الرحلة 🚀', '16px Inter')

let maxLineWidth = 0
walkLineRanges(prepared, 320, line => {
  if (line.width > maxLineWidth) maxLineWidth = line.width
})
// maxLineWidth is the tightest container width that still fits the text
Extends LayoutResult with a lines array. Returned by layoutWithLines().
type LayoutLinesResult = LayoutResult & {
  lines: LayoutLine[]
}
lineCount
number
The number of wrapped lines produced at the given maxWidth.
height
number
Total block height in pixels. Equal to lineCount * lineHeight.
lines
LayoutLine[]
One LayoutLine per wrapped line, in document order. Each entry contains the rendered text string, measured width, and start/end cursors.
import { prepareWithSegments, layoutWithLines } from '@chenglou/pretext'

const prepared = prepareWithSegments('AGI 春天到了. بدأت الرحلة 🚀', '18px "Helvetica Neue"')
const { lines } = layoutWithLines(prepared, 320, 26)
for (let i = 0; i < lines.length; i++) {
  ctx.fillText(lines[i].text, 0, i * 26)
}

Build docs developers (and LLMs) love