Donner SVG Editor & Engine
SVG-native editor and embeddable SVG2 + CSS3 engine in C++20, with GPU (WebGPU) and compact CPU renderers, built for correctness, security, and performance.
Loading...
Searching...
No Matches
Text Rendering: Testing and Validation

Back to hub

Author: Claude Opus 4.6

Testing and Validation

Unit tests

  • FontManager tests: load TTF/OTF/WOFF1, verify stbtt_fontinfo initialization, test cascade fallback, verify font metrics.
  • TextEngine helper tests: verify chunking, text-anchor, textLength, lengthAdjust, and baseline-shift calculations without real fonts.
  • TextBackend tests: verify shaping, metrics, and outline extraction for both base and full backends.
  • Glyph outline tests: extract outlines for known glyphs, verify path geometry matches expected curves.

Golden image tests

  • Add text-specific SVG test cases to renderer_tests:
    • Basic Latin text.
    • Multi-span positioning (dx/dy).
    • text-anchor (start/middle/end).
    • @font-face with embedded WOFF1 font.
    • @font-face with WOFF2 font (requires text_full).
    • Stroke + fill text.
    • Per-glyph rotation.
  • Resvg text test subset: most e-text-* and e-tspan-* tests now pass with tight thresholds. Primary remaining failures are in e-textPath-* and a-font-size-*.

Feature-gated test skipping

{"text_basic.svg", Params::RequiresFeature(Feature::Text)},
{"text_woff2_font.svg", Params::RequiresFeature(Feature::TextFull)},

CI runs all feature combinations to ensure both enabled and disabled paths work.

Backend parity

  • RendererTinySkia and RendererGeode both consume the same TextEngine/TextBackend* layout output, so glyph positions should match between them at both the base and text_full tiers. Residual pixel differences still require root-cause analysis. See 0038-geode_tinyskia_text_parity.md for the parity gate.
  • The base tier (stb_truetype, TextBackendSimple) and text_full tier (HarfBuzz, TextBackendFull) can still diverge in glyph advances/kerning between each other, since only text_full processes GPOS.

Current Snapshot

Base-tier (text, TinySkia) resvg status for the most relevant text slices. This is a living reference: regenerate the counts with bazel test //donner/svg/renderer/tests:resvg_test_suite_default_text and read bazel-testlogs/.../resvg_test_suite_default_text/test.xml (enabled = status="run", disabled = status="notrun"). The suite is green, so every enabled test passes — some via blessed per-test goldens catalogued in 0009.

Slice Enabled passing Disabled Notes
e-text-* 31/31 15 Disabled are mostly text-full-only or explicit skips
e-tspan-* 24/24 7
a-text-decoration-* 19/19 2 Includes a custom golden for the indirect case
a-lengthAdjust-* 1/1 3 Enabled coverage is very thin
a-dominant-baseline-* 7/7 14 Coverage is still thin
a-letter-spacing-* 9/9 3 Arabic case requires text-full; 3 disabled/UB cases remain
a-textLength-* 7/7 5 a-textLength-008 still disabled
a-writing-mode-* 13/13 10 10 disabled mixed-script / rotate / dx/dy cases remain
e-textPath-* 33/33 11 Enabled via blessed sub-pixel/curved-baseline goldens (see 0009)

Release-significant gaps from the current snapshot:

  • BiDi and advanced mixed-script vertical text remain explicitly deferred (the bulk of the disabled a-writing-mode-* / a-dominant-baseline-* cases).
  • Several slices (a-lengthAdjust-*, a-dominant-baseline-*) have thin enabled coverage even though what is enabled passes.

Known gaps and disabled tests

The suite is green: every enabled resvg text test passes (some via blessed per-test goldens catalogued in 0009). The remaining work is the set of tests still marked Params::Skip(...) / DISABLED_ in donner/svg/renderer/tests/resvg_test_suite.cc. That source — its per-test skip reasons — is the authoritative list; the categories below summarize it.

Unimplemented features

  • Bidirectional / RTL text (UAX#9) — the largest gap. direction and unicode-bidi properties, BiDi reordering, and any span mixing LTR Latin with RTL Arabic are not implemented; several letter-spacing / writing-mode / mixed-script cases are blocked on it.
  • <tref> (deprecated in SVG 2) — not implemented (all e-tref-* link/position/ style/xml-space cases skipped).
  • textLength + lengthAdjust — the combined form (and lengthAdjust parented to textLength, including the Arabic and zero cases) is not implemented.
  • Font propertiesfont shorthand, font-kerning (HarfBuzz feature toggle), font-size-adjust, and the deprecated SVG 1.1 kerning attribute are not implemented.
  • dominant-baseline — the full keyword set is not implemented (enabled coverage is thin).
  • SVG 2 text featuresclipPath on <text> and text children, filter on <textPath>, <a> as a text-content element, and the <textPath> method="stretch", spacing="auto", side="right", and path attributes are not implemented.
  • Vertical text on a pathwriting-mode=tb on <textPath> is deferred, as is transform-origin on textPath (entangled with textPath layout).

Known rendering bugs (skipped pending fix)

  • Gradient stroke / radial-gradient fill on <text> renders incorrectly.
  • Kerning is wrong on <textPath> and when font-size changes mid-run; rotation indices are misapplied across nested <tspan>s.
  • Whitespace-only text nodes are lost across xml:space preserve/default nesting boundaries.
  • Letter-spacing edge cases, and (text-full only) some non-ASCII underline, mixed-language, and CJK-punctuation cases still differ from the reference.

UB / spec-ambiguous

A few cases are skipped as undefined behavior (e.g. baseline-shift + rotate combinations) rather than bugs — see the skip reason on each.