oxideav-scribe
Pure-Rust vector font shaper + line layout for the
oxideav framework. Parses TTF / OTF
tables (via oxideav-ttf
oxideav-otf) and emits positioned glyphs asoxideav-coreNodevectors ready for the rasterizer inoxideav-raster.
Scribe contains no pixel kernel: outline flattening, scanline AA,
alpha compositing, synthetic bold and stroke dilation all live in
oxideav-raster. Producing a rasterised text run is a two-step pipeline:
use ;
use Renderer;
use ;
let bytes = read?;
let face = from_ttf_bytes?;
let chain = new;
// 1. Shape: emit positioned vector glyph nodes.
let placed = shape_to_paths;
// 2. Wrap into a VectorFrame + render via oxideav-raster.
let mut root = default;
for in placed
let mut frame = new;
frame.root = root;
let rgba: VideoFrame = new.render;
Capabilities
- Outline access —
Face::glyph_path(gid)returns a Y-upoxideav_core::Path(rawMoveTo/LineTo/QuadCurveTo/CubicCurveTo/Close). TT outlines decode quadratics; CFF charstrings decode cubics 1:1.Face::glyph_node(gid, size_px)bakes the Y-flip + scale into a render-readyNode::Path(orNode::Imagefor CBDT colour glyphs). - Shaper —
cmap+ GSUB type 4 (ligatures) + GPOS type 2 (pair kerning) + GPOS type 4/5/6 (mark-to-base, mark-to-mark stacking), enough for Latin / Cyrillic / Greek / basic CJK / Vietnamese / polytonic Greek. - Arabic contextual joining (round 7) —
shaping::arabicpicksisol/init/medi/finaper character via the Unicode joining-class state machine;FaceChain::shaperewrites Arabic letters into their Presentation Forms-B equivalents (U+FE70..U+FEFF) before cmap so cmap-only fonts render the visually-correct contextual shapes (including LAM-ALEF ligatures via the existing GSUB pass). - Indic complex-script shaping (rounds 8 + 10 + 11) —
shaping::indicclassifies Devanagari (U+0900..U+097F), Bengali (U+0980..U+09FF), Tamil (U+0B80..U+0BFF), Gurmukhi (U+0A00..U+0A7F), Gujarati (U+0A80..U+0AFF), Telugu (U+0C00..U+0C7F), Kannada (U+0C80..U+0CFF), Malayalam (U+0D00..U+0D7F), and Oriya (U+0B00..U+0B7F) codepoints, segments runs into orthographic clusters, applies per-script pre-base matra reorder, and identifies reph where applicable (Tamil + Malayalam are reph-disabled — Tamil RA does not form a reph; modern Malayalam uses chillu independent half-forms instead). When the active face publishes arphfGSUB lookup for the active script, the leading RA glyph is rewritten to its reph form viaFont::gsub_apply_lookup_type_1and the halant glyph is dropped (round 10). Round 11 also wires cluster-position-aware GSUB features:halffor non-final consonants in conjuncts;pref/blwf/abvf/pstf(cascaded — first that returns a substitute wins) for post-halant consonants; and the presentation- pass featurespres/psts/abvs/blwsover every glyph in the cluster. Coverage misses pass through unchanged so a font without a given lookup degrades gracefully. Per-script reorder rules are exposed asDEVANAGARI_RULES/BENGALI_RULES/TAMIL_RULES/GURMUKHI_RULES/GUJARATI_RULES/TELUGU_RULES/KANNADA_RULES/MALAYALAM_RULES/ORIYA_RULESfor callers reusing the cluster machine. - Variable fonts (round 9) —
Face::set_variation_coords/variation_axes/named_instances/is_variablesurface the font'sfvardeclarations and let callers shape against a custom axis-coord vector (e.g.wght=600 / wdth=125on Inter Variable).Shaper::with_variation_coords(vec![..]).shape_to_paths(&mut chain, text, size_px)is the per-call override path: it installs the coords on the primary face, runs the shape, then restores. Glyph outlines flow throughoxideav-ttf's gvar interpolator so the emittedPathcarries the blended deltas. CFF2 / OTF variable fonts are deferred untiloxideav-otfexposes a CFF2 variation pipeline. - Vector text API —
Shaper::shape_to_pathsreturns one(face_idx, Node, Transform2D)per visible glyph. Each node is wrapped in anoxideav_core::Group { cache_key: Some(_), .. }so the downstream rasterizer's bitmap cache memoises the rendered glyph across renders, frames, and renderer instances. - Italic synthesis —
style.italicsynthesises a 12° forward shear when the face is upright; falls back to the font's own slant when one is present. Bold synthesis is deferred to consumer code (or a real Bold face). - Face chain — multi-face fallback for missing codepoints; per-glyph
face_idxtells the consumer which face owns each glyph. - CBDT/CBLC colour bitmaps — Noto Color Emoji and friends decode to
Node::Imagecarrying aVideoFrame; the resampling to the requested size happens in scribe (bilinear, straight-alpha). - Layout — line measurement + word-wrap (no bidi; round-3 work).
Out of scope
- Pixel work — bitmap rasterisation, alpha compositing, synthetic
bold dilation, stroke dilation. All in
oxideav-raster. - Bidi (UAX #9), Sinhala / Burmese / Khmer / Thai / Lao
(Brahmic but with stack-form / split-vowel rules outside the
Indic2 cluster machine), variable-font metrics
(
MVAR/HVAR/VVAR/STAT), CFF2 variable fonts, TrueType bytecode hinting, subpixel LCD filtering, GPOS cursive attachment — deferred.
Test fixtures
Reuses crates/oxideav-ttf/tests/fixtures/DejaVuSans.ttf plus
DejaVuSansMono.ttf (Bitstream Vera license),
crates/oxideav-otf/tests/fixtures/SourceSans3-Regular.otf (SIL OFL),
and a vendored copy of InterVariable.ttf (SIL OFL — see
tests/fixtures/INTER-OFL-LICENSE.txt) for the round-9 variable-font
suite. Network-gated emoji/CJK fixtures fetch on demand; see
tests/font_fixtures/ and run with OXIDEAV_NETWORK_TESTS=1.
License
MIT — see LICENSE.