OpenFresco.Layout (open_fresco v0.2.0)

Copy Markdown View Source

Resolves a scene's computed layout before it is drawn:

  • Text wrap — each text/stamp element's resolved content is wrapped to its box width, storing the lines and the rendered block height. Measurement-accurate when measure: true + resvg is available (wrap points match the render); a character-count estimate otherwise.
  • Button auto-width — a button with auto_width: true gets its box widened to the measured label plus horizontal padding.
  • Anchor reflow — an element anchored to another (edge + gap + align) is repositioned relative to the target's rendered box; chains resolve in dependency order and cycles are rejected (validate/1) — during resolve/3 a cyclic anchor is dropped so rendering still proceeds.

Runs on every render (cheap in estimate mode; OpenFresco.render/3 opts into measurement). Results are stashed on transient :_lines / :_rendered_h element keys the SVG generator reads.

International text: the supported-scripts matrix

Line-break opportunities are honest about their limits:

  • Space-separated LTR scripts (Latin, Cyrillic, Greek, …) — fully supported in both estimate and measured modes.
  • CJK and other UAX #14 scripts — supported when the optional :unicode_string dependency is installed: break opportunities come from UAX #14 line-breaking segmentation instead of whitespace. Without it, unspaced CJK runs as one unbreakable unit (it will overflow rather than mis-break).
  • Thai / Khmer / Lao / Myanmar — NOT supported: correct breaking requires dictionary segmentation, which neither this library nor UAX #14 alone provides. Text renders, but break points inside unspaced runs will be wrong.
  • RTL / bidi (Arabic, Hebrew) — visual ordering and shaping are delegated to the rasterizer (resvg shapes with rustybuzz, including bidi). Set the element's :direction ("rtl") so renderers order runs correctly. Wrap-point measurement treats the text as a sequence of space-separated words, which is usually correct for Arabic/Hebrew prose.
  • Grapheme clusters (emoji ZWJ sequences, combining marks) — never split: breaking only happens at whitespace or UAX #14 opportunities, both of which respect cluster boundaries.

Summary

Functions

Resolve wrap, auto-width, and anchor positions. Returns the scene with updated element boxes and :_lines / :_rendered_h annotations. Never raises; a cyclic anchor is left un-applied.

:ok, or {:error, {:anchor_cycle, ids}} if the anchor graph has a cycle.

Functions

resolve(scene, values, opts \\ [])

@spec resolve(OpenFresco.Scene.t(), map(), keyword()) :: OpenFresco.Scene.t()

Resolve wrap, auto-width, and anchor positions. Returns the scene with updated element boxes and :_lines / :_rendered_h annotations. Never raises; a cyclic anchor is left un-applied.

validate(scene)

@spec validate(OpenFresco.Scene.t()) :: :ok | {:error, {:anchor_cycle, [String.t()]}}

:ok, or {:error, {:anchor_cycle, ids}} if the anchor graph has a cycle.