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
Outlines and rendering
- Outline access —
Face::glyph_path(gid)returns a Y-upoxideav_core::Path(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-readyNode::Path(orNode::Imagefor CBDT colour glyphs). - 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; resampling to the requested size happens in scribe (bilinear, straight-alpha).
Shaping (GSUB / GPOS)
- GSUB substitution — LookupType 1 (single), 2 (multiple), 3
(alternate), 4 (ligature) are applied through the caller-driven
Face::shape_text(text, features)surface; contextual / chained / reverse-chained (types 5 / 6 / 8) flow throughShaper::shape/FaceChain::shape. The required-featureccmpruns as a pre-ligature pass andcaltas a post-ligature pass for every Latin / Cyrillic / Greek / DFLT run. Worked examples:face.shape_text("fi", &[*b"liga"])returns a single fi-ligature glyph;face.shape_text("Hi", &[*b"smcp"])returns small-caps where the font ships them. - GPOS positioning — single adjustment (type 1), pair kerning (type 2), cursive attachment (type 3, flag-clear semantics), mark-to-base (type 4), mark-to-ligature (type 5), and mark-to-mark stacking (type 6). Together this is enough for Latin / Cyrillic / Greek / basic CJK / Vietnamese / polytonic Greek.
- Feature-tag introspection —
Face::gsub_features_for_script/has_gsub_featurereport the feature tags the active face publishes under an OpenType script tag, for higher-level APIs that gate on feature presence. (A GPOS introspection mirror is a follow-up.) - Explicit-script + alternate-index shaping —
shape_text_with_scriptresolves features against one named script tag (no priority walk, avoiding cross-script collisions likeligaunder bothlatnandarab);shape_text_with_alternates/shape_text_with_script_and_alternatesname theAlternateSetentry the Type-3 walker picks. The auto-probeshape_textwalks a broad script-tag priority list (Latin / Cyrillic / Greek / DFLT / Arabic / Hebrew / Thai / Lao / the Indic v1+v2 scripts / Khmer / Myanmar / Hangul / Han / Kana).
Complex-script shaping
- Arabic contextual joining —
shaping::arabicpicksisol/init/medi/finaper character via the joining-class state machine;FaceChain::shaperewrites Arabic letters into their Presentation Forms-B equivalents before cmap so cmap-only fonts render the correct contextual shapes (including LAM-ALEF ligatures). - Indic + Brahmic non-Indic shaping —
shaping::indicclassifies Devanagari, Bengali, Tamil, Gurmukhi, Gujarati, Telugu, Kannada, Malayalam, Oriya, Sinhala, Khmer, Thai, Lao, and Myanmar / Burmese, segments runs into orthographic clusters, applies per-script pre-base matra reorder, identifies reph (or the Burmese kinzi), rewrites the leading RA to its reph form viarphf, and wires the cluster-position-aware GSUB features (half,pref/blwf/abvf/pstf, the presentation featurespres/psts/abvs/blws, and the context-awarelocl/nukt/akhn/cjct/init/haln). Per-script reorder rules are exposed asDEVANAGARI_RULES/BENGALI_RULES/ … /BURMESE_RULESfor callers reusing the cluster machine. Coverage misses pass through unchanged.
Variable fonts
- Outline interpolation —
Face::set_variation_coords/variation_axes/named_instances/is_variablesurface thefvardeclarations and let callers shape against a custom axis-coord vector.Shaper::with_variation_coords(..)is the per-call override path. Glyph outlines flow through the gvar interpolator so the emittedPathcarries the blended deltas. CFF2 variable charstrings (theblendoperator) are not yet emitted — scribe parses the CFF2 INDEX for table presence / axis count / glyph count viaFace::cff2()only. - Metric-variation tables —
Face::mvar()/metric_delta(tag)(global metrics),Face::hvar()/h_advance_delta(gid)(horizontal advance),Face::vvar()/v_advance_delta(gid)(vertical), andFace::stat()/stat_axes()/stat_axis_values()(Style Attributes) all resolve at the current variation coords. They share anItemVariationStore+DeltaSetIndexMapparser incrate::variations. name-id resolution —Face::name_id(nid)returns the highest-ranked Unicode string for aname-table id, resolvingaxis_name_id/subfamily_name_id/value_name_id.
Layout and bidirectional text
- Line layout — line measurement + word-wrap.
- High-level bidi bridge —
layout::reorder_line_visual(text, base_level) -> VisualLinedrives the complete UAX #9 pipeline over one display line (class assignment → P → X → W → N0 → N1/N2 → I → L1 → L2 → L3 → L4) and returns the characters in left-to-right visual order ready to feed glyph-by-glyph into the shaper.VisualLinepublishesvisual: Vec<char>(L4-mirrored, render order), thelogical_to_visual/visual_to_logicalpermutation pair (the latter precomputed for O(1) cursor hit-testing), and the resolvedbase_level.base_level: Option<u8>is the HL1 override. - Whole-text / paragraph drivers —
bidi::process_text(text, base_level) -> TextBidisplits a document into paragraphs (P1) and resolves each independently;bidi::process_paragraph(text, base_level)andprocess_paragraph_with_brackets(..)(N0 wired in between W7 and N1) compose the per-rule passes into oneParagraphBidicarrier withreorder_paragraph()/reorder_line_range(start..end)helpers. - Per-rule UAX #9 surface — the complete rule pipeline is also
exposed as individual public functions for callers needing finer
control:
bidi_class(full Unicode 16.0DerivedBidiClass.txtcoverage, data-driven from the UCD snapshots underdocs/text/unicode-bidi/),paragraph_level/split_paragraphs(P1/P2/P3),resolve_explicit_levels(X1..X9 stack),level_runs/isolating_run_sequences(X10 BD7/BD13 partition + sos/eos),resolve_weak_types(W1..W7),paired_bracket/bracket_pairs/resolve_bracket_pairs(N0, fullBidiBrackets.txt),resolve_neutral_types(N1/N2),resolve_implicit_levels(I1/I2),reset_trailing_levels/reorder_line(L1/L2),reorder_combining_marks(L3), andmirrored_glyph/apply_mirroring(L4, fullBidiMirroring.txt).
Out of scope
- Pixel work — bitmap rasterisation, alpha compositing, synthetic
bold dilation, stroke dilation. All in
oxideav-raster. - Bidi HL1..HL6 higher-level-protocol overrides — the rule pipeline itself is complete; HL overrides remain caller responsibility.
- CFF2 variable charstrings — the
blendoperator is not yet emitted (the INDEX walker is parsed for table metadata only). - TrueType bytecode hinting, subpixel LCD filtering, and the
GPOS cursive attachment RIGHT_TO_LEFT flag-set variant (needs
lookup-flag exposure in
oxideav-ttf's public GPOS API) — 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 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.