oxideav-scribe 0.1.5

Pure-Rust vector font shaper + layout for the oxideav framework — TrueType / OTF outline access, GSUB ligatures, GPOS kerning, mark attachment, CBDT colour bitmaps. Pixel pipeline lives in oxideav-raster.
Documentation

oxideav-scribe

Pure-Rust vector font shaper + line layout for the oxideav framework. Parses TTF / OTF tables (via oxideav-ttf

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 oxideav_core::{Group, Node, VectorFrame};
use oxideav_raster::Renderer;
use oxideav_scribe::{Face, FaceChain, Shaper};

let bytes  = std::fs::read("DejaVuSans.ttf")?;
let face   = Face::from_ttf_bytes(bytes)?;
let chain  = FaceChain::new(face);

// 1. Shape: emit positioned vector glyph nodes.
let placed = Shaper::shape_to_paths(&chain, "Hello, world!", 16.0);

// 2. Wrap into a VectorFrame + render via oxideav-raster.
let mut root = Group::default();
for (_face_idx, glyph_node, transform) in placed {
    root.children.push(Node::Group(Group {
        transform,
        children: vec![glyph_node],
        ..Group::default()
    }));
}
let mut frame = VectorFrame::new(400.0, 80.0);
frame.root = root;
let rgba: oxideav_core::VideoFrame = Renderer::new(400, 80).render(&frame);

Capabilities

  • Outline accessFace::glyph_path(gid) returns a Y-up oxideav_core::Path (raw MoveTo / 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-ready Node::Path (or Node::Image for CBDT colour glyphs).
  • Shapercmap + 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::arabic picks isol / init / medi / fina per character via the Unicode joining-class state machine; FaceChain::shape rewrites 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)shaping::indic classifies Devanagari (U+0900..U+097F), Bengali (U+0980..U+09FF), and Tamil (U+0B80..U+0BFF) codepoints, segments runs into orthographic clusters, applies per-script pre-base matra reorder (Devanagari U+093F; Bengali U+09BF / U+09C7 / U+09C8; Tamil U+0BC6 / U+0BC7 / U+0BC8), and identifies reph (leading RA + halant + consonant — Devanagari + Bengali only; Tamil RA does not form a reph). When the active face publishes a rphf GSUB lookup for the active script, the leading RA glyph is rewritten to its reph form via Font::gsub_apply_lookup_type_1 and the halant glyph is dropped (round 10). Per-script reorder rules are exposed as DEVANAGARI_RULES / BENGALI_RULES / TAMIL_RULES for callers wanting to reuse the cluster machine on additional Indic scripts.
  • Variable fonts (round 9)Face::set_variation_coords / variation_axes / named_instances / is_variable surface the font's fvar declarations and let callers shape against a custom axis-coord vector (e.g. wght=600 / wdth=125 on 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 through oxideav-ttf's gvar interpolator so the emitted Path carries the blended deltas. CFF2 / OTF variable fonts are deferred until oxideav-otf exposes a CFF2 variation pipeline.
  • Vector text APIShaper::shape_to_paths returns one (face_idx, Node, Transform2D) per visible glyph. Each node is wrapped in an oxideav_core::Group { cache_key: Some(_), .. } so the downstream rasterizer's bitmap cache memoises the rendered glyph across renders, frames, and renderer instances.
  • Italic synthesisstyle.italic synthesises 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_idx tells the consumer which face owns each glyph.
  • CBDT/CBLC colour bitmaps — Noto Color Emoji and friends decode to Node::Image carrying a VideoFrame; 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), remaining Indic scripts (Telugu, Gujarati, Gurmukhi, Kannada, Malayalam, Oriya — same pattern as Bengali / Tamil with per-script categorisation tables), 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.