Skip to content

Text shaping and layout

Rune Engine splits text into two layers: native shaping produces immutable glyph data, and managed code decides how paragraphs are wrapped, aligned, trimmed, and recorded as draw commands.

Mental model

text
UTF-16 text + Font
  -> native shaping
  -> immutable ShapedText handle
  -> managed line layout and glyph ranges
  -> CmdDrawShapedText replay

Responsibilities and boundaries

  • Native shaping owns script analysis, bidi analysis, font fallback, glyph shaping, and immutable glyph storage.
  • Managed layout owns line breaking, trimming, line positioning, and the decision about which glyph range each draw command should render.
  • Replay owns final glyph drawing, including post-shaping letter spacing adjustments from the recorded command.

Stable current behavior

ShapedText is the stable public handle

The public shaped-text surface exposes:

  • glyph count
  • total advance
  • glyph advances
  • cluster_map[]
  • source text length
  • ascent and descent metrics

That is enough for layout and line wrapping without exposing backend glyph IDs or fallback-run internals.

Shaping is native and immutable

rune_shape_text() uses the current DirectWrite-based pipeline to analyze bidi and script runs, map fallback fonts, reorder runs into visual order, and store glyph output in one immutable handle. Snapshots then keep that handle alive through resource_refs[] just like images or fonts.

Layout is currently managed

The shipped text layout policy lives above the engine:

  • shape one paragraph
  • build prefix sums from glyph advances
  • use the source string and cluster_map[] to find whitespace break opportunities
  • force-break long words when necessary
  • record one CmdDrawShapedText range per visual line

Letter spacing is post-shaping: layout adds it to the advance model, and the same value is recorded so replay applies the same gap between adjacent glyphs.

Current limitations

  • The current public Engine contract does not expose an engine-owned paragraph object, paragraph layout API, caret-navigation list, glyph ID array, glyph offset array, or public text hit-test API.
  • SetShapeCacheLimit() is a reserved compatibility knob today. The current audited shaping path does not publish reusable shaped-segment cache behavior.
  • Layout policy is currently defined by shipped managed callers such as Story's text sprite rather than by a general engine paragraph contract.

How the pieces connect

Text stays efficient because shaping and layout do not fight each other. Native code handles complex scripts, fallback, and bidi once. Managed code then wraps, aligns, and trims against the stable advances and cluster map it actually needs. Replay finally draws the selected glyph ranges with the same immutable shaped resource.

That split keeps the public text surface honest: powerful shaping is already available, but higher-level caret, editing, and paragraph policies remain a framework concern.

Go deeper

Rune Project brings Rune Story authoring together with the Rune Engine rendering foundation.