Expand description
Pure-Rust font shaper + layout for the oxideav framework.
Scribe is a vector-only shaper: parse TTF / OTF tables → emit
positioned vector glyphs as oxideav_core::Nodes. All pixel
work — outline flattening, scanline anti-aliasing, alpha
compositing — happens downstream in
oxideav-raster.
Scope:
- Shaper —
cmap+ GSUB type 4 (ligatures) + GPOS type 2 (pair kerning) + mark-to-base / mark-to-mark, enough for Latin / Cyrillic / Greek / basic CJK / Vietnamese / polytonic Greek. - Arabic contextual joining (round 7) —
shaping::arabiccomputes the joining form per character using the Unicode joining classes + an adjacency state machine;FaceChain::shapethen translates Arabic letters into their Arabic Presentation Forms-B equivalents (U+FE70..U+FEFF) before cmap, so a font that ships the PF-B block (DejaVuSans, Noto Sans Arabic, Amiri) renders visually-correct contextual shapes — including LAM-ALEF ligatures via the existing GSUB pass. - Indic + Brahmic complex-script shaping (rounds 8 + 10 + 11 +
12 + 13) —
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), Oriya (U+0B00..U+0B7F), Sinhala (U+0D80..U+0DFF), Khmer (U+1780..U+17FF), Thai (U+0E00..U+0E7F), Lao (U+0E80..U+0EFF), and Myanmar / Burmese (U+1000..U+109F) codepoints into syllabic categories, segments runs into orthographic clusters, and applies per-script cluster transformations: pre-base matra reorder (a uniform mechanism across all scripts that have one) plus reph identification (the Indic core scripts; Tamil + Malayalam + Sinhala + Khmer + Thai + Lao are reph-disabled; Burmese identifies a kinzi NGA+Asat+Virama+Cons instead viaRephKind::BurmeseKinzi). Khmer’s halant role is played by U+17D2 COENG which stacks subjoined consonants underneath the base; Thai + Lao have no halant and their pre-base vowels are already in storage order before their consonant. TheFaceChain::shapepipeline applies the reorder before cmap so cmap-only fonts render simple clusters with the matra in the correct visual position. When the active face publishes arphfGSUB lookup for the script, identified reph clusters get the leading RA glyph substituted to its reph-form and the halant glyph dropped viaFont::gsub_apply_lookup_type_1. Round 11 wires cluster-position GSUB features (half/pref/blwf/abvf/pstf/pres/psts/abvs/blws). Round 13 adds a multi-glyph context-aware GSUB pass dispatchinglocl/nukt/akhn/cjct/init/halnviaFont::gsub_apply_lookup_type_5(Contextual) +gsub_apply_lookup_type_6(Chained Context). - Variable fonts (rounds 9 + 14) —
Face::set_variation_coords/variation_axes/named_instances/is_variablesurface the font’sfvardeclarations; outlines flow throughoxideav-ttf’s gvar interpolator.Shaper::with_variation_coordsis the per-call override. Round 14 closes the metric-variation gap:Face::mvar()/metric_delta(tag),Face::hvar()/h_advance_delta(gid),Face::vvar()/v_advance_delta(gid),Face::stat()/stat_axes()/stat_axis_values()(withname_id(nid)resolving the human-readable label).Face::cff2()parses the CFF2 INDEX walker for presence + axis count + glyph count; full Type 2 v3 charstring evaluation with theblendoperator is deferred. Face::glyph_path/glyph_node— TrueType + OTF (CFF) outlines asoxideav_core::Path; CBDT/sbix colour bitmaps asNode::Imagecarrying aVideoFrame.Shaper::shape_to_paths— vector text API: positioned(face_idx, Node, Transform2D)triples ready to compose into aVectorFrame. Each glyph is wrapped in a cache-keyedGroupso the downstream rasterizer’s bitmap cache reuses the same memoised glyph across renders.- Face chain — multi-face fallback (primary → fallback chain), per-codepoint resolution.
- Layout — line measurement + word-wrap (no bidi).
- BiDi (rounds 186 + 191) —
bidi::bidi_class(c)returns the UAX #9 §3.2 normative bidirectional class for every code point scribe needs today (the 12 explicit-format controls, ASCII / Latin-1, Hebrew, four core Arabic blocks plus Syriac, Thaana, N’Ko, the two Arabic Presentation Forms blocks, and combining- mark NSMs);bidi::paragraph_level(text)implements UAX #9 rules P1 / P2 / P3, returning the paragraph embedding level (0 = LTR, 1 = RTL).bidi::split_paragraphs(text)is P1’s split (every type-Bcharacter is kept with the previous paragraph). Round 191 landsbidi::resolve_weak_types(classes, sos, eos), the §3.3.4 weak-type resolution pass (rules W1..W7) operating on one isolating run sequence in place: NSM inheritance (W1), EN-after-AL → AN (W2), AL → R (W3), single-separator-between-two-numbers collapse (W4), ET-adjacent-to-EN collapse (W5), leftover-separator neutralisation (W6), and EN-after-L → L (W7). Round 198 landsbidi::resolve_neutral_types(classes, embedding_level, sos, eos), the §3.3.5 neutral / isolate-formatting resolution pass (rules N1 + N2): every maximal NI run (B / S / WS / ON / LRI / RLI / FSI / PDI) collapses to a strong direction — matching neighbours via N1 (withEN/ANcounting asR) or the embedding direction via N2. Round 204 landsbidi::resolve_implicit_levels(classes, embedding_level), the §3.3.6 implicit-level pass (rules I1 + I2): every character’s resolved type drives a per-character level adjustment per UAX #9 Table 5 (L stays at even, R goes +1; AN / EN go +2; under odd, L / EN / AN all go +1). Round 210 lands the §3.4 line-level pair:bidi::reset_trailing_levels(orig_classes, levels, paragraph_level)is rule L1 (separator + trailing whitespace + isolate-formatting characters reset back to the paragraph embedding level using the original class slice per §3.4) andbidi::reorder_line(levels) -> Vec<usize>is rule L2 (the progressive top-down reversal that produces a logical-to-visual permutation a renderer walks to emit glyphs in display order). Round 217 landsbidi::resolve_explicit_levels(classes, paragraph_level), the §3.3.2 explicit-level / override / isolate stack pass (rules X1..X9): produces a per-character embedding level + an override-rewritten effective-class slice + the X9-removed- character flag set, ready for X10’s isolating-run-sequence partition to feed the W / N / I phases. Round 227 landsbidi::process_paragraph(text, base_level)and the class-driven mirrorbidi::process_paragraph_classes(classes, base_level), the §3 whole-paragraph driver that composes P → X → W → N → I in one call and returns aParagraphBidicarrier carrying the paragraph level + the original classes + the X9-removed flag set + the resolved per-character level vector. The carrier exposesreorder_paragraph()(whole-paragraph L1 + L2 convenience) andreorder_line_range(start..end)(per-line variant for callers that have a line-breaker). Round 247 landsbidi::reorder_combining_marks(orig_classes, levels, &mut visual), the §3.4 rule L3 in-place permutation adjuster: every L2-reversed[NSM, …, NSM, base]block in the visual stream (identified by its strictly-decreasing logical indices) is reversed back to[base, NSM, …, NSM]so callers running a non-scribe mark-attachment policy get the spec’s “expects them to follow” alternative shape; the function is idempotent and ignores even-level (LTR) runs. Round 257 lands the §3.3.5 N0 bracket-pair pass:bidi::paired_bracket(c)is the BD14 / BD15 lookup for the six ASCII brackets ((↔),[↔],{↔});bidi::bracket_pairs(chars, classes)runs the BD16 stack walk (63-deep, overflow → empty list, sort by opener);bidi::resolve_bracket_pairs(classes, pairs, embedding, sos)applies the N0 a / b / c / d cases in place (EN/AN counted as R for the inside-strong + preceding-strong walks, sequential ordering so inner pairs see the rewrites of outer ones, trailing-NSM inheritance per the §3.3.5 note); andbidi::process_paragraph_with_brackets(text, base_level)/process_paragraph_classes_with_brackets(classes, chars, base_level)are the §3 paragraph driver with N0 wired in between W7 and N1. The full UnicodeBidiBrackets.txttable (the ~60 paired-bracket entries across the Mathematical Operators / CJK Symbols / Ornamental Brackets blocks) is not yet vendored underdocs/text/unicode-bidi/; non-ASCII bracket pairs are deferred until the table lands. Round 268 lands the §3.4 L4 mirroring rule:bidi::mirrored_glyph(c)is theBidi_Mirroring_Glyphacceptable-mirror-pair lookup for the same six ASCII brackets (an involution,Noneoutside the seed set including the §3.4-excluded ornate parentheses U+FD3E / U+FD3F), andbidi::apply_mirroring(chars, levels)rewrites every odd-resolved-level (directionality R) position that has a mirror pair in place over the line’s logical character sequence. The widerBidiMirroring.txtpair table (mathematical operators, angle brackets, CJK bracket blocks) is deferred until that data file is vendored.
See README.md for a tour and the deferral list.
Re-exports§
pub use bidi::apply_mirroring;pub use bidi::bidi_class;pub use bidi::bracket_pairs;pub use bidi::isolating_run_sequences;pub use bidi::level_runs;pub use bidi::mirrored_glyph;pub use bidi::paired_bracket;pub use bidi::paragraph_level;pub use bidi::process_paragraph;pub use bidi::process_paragraph_classes;pub use bidi::process_paragraph_classes_with_brackets;pub use bidi::process_paragraph_with_brackets;pub use bidi::process_text;pub use bidi::reorder_combining_marks;pub use bidi::reorder_line;pub use bidi::reset_trailing_levels;pub use bidi::resolve_bracket_pairs;pub use bidi::resolve_explicit_levels;pub use bidi::resolve_implicit_levels;pub use bidi::resolve_neutral_types;pub use bidi::resolve_weak_types;pub use bidi::split_paragraphs;pub use bidi::BidiClass;pub use bidi::BracketKind;pub use bidi::ExplicitLevels;pub use bidi::IsolatingRunSequence;pub use bidi::LevelRun;pub use bidi::ParagraphBidi;pub use bidi::ParagraphSlice;pub use bidi::TextBidi;pub use bidi::MAX_DEPTH;pub use color::Rgba;pub use color::TRANSPARENT;pub use color::WHITE;pub use color_glyph::ColorGlyphBitmap;pub use face::Face;pub use face::FaceKind;pub use face_chain::FaceChain;pub use layout::reorder_line_visual;pub use layout::run_width;pub use layout::wrap_lines;pub use layout::VisualLine;pub use shaper::PositionedGlyph;pub use shaper::Shaper;pub use shaper::ShaperBuilder;pub use shaping::bengali_category;pub use shaping::burmese_category;pub use shaping::cluster_boundaries;pub use shaping::cluster_boundaries_with;pub use shaping::compute_forms;pub use shaping::devanagari_category;pub use shaping::gujarati_category;pub use shaping::gurmukhi_category;pub use shaping::joining_class;pub use shaping::kannada_category;pub use shaping::khmer_category;pub use shaping::lao_category;pub use shaping::malayalam_category;pub use shaping::oriya_category;pub use shaping::presentation_form;pub use shaping::reorder_cluster;pub use shaping::reorder_cluster_with;pub use shaping::shape_text_with_font;pub use shaping::shape_text_with_script_with_font;pub use shaping::sinhala_category;pub use shaping::tamil_category;pub use shaping::telugu_category;pub use shaping::thai_category;pub use shaping::ClusterFlags;pub use shaping::IndicCategory;pub use shaping::JoiningClass;pub use shaping::JoiningForm;pub use shaping::ReorderRules;pub use shaping::RephKind;pub use shaping::Script;pub use shaping::BENGALI_RULES;pub use shaping::BURMESE_RULES;pub use shaping::DEVANAGARI_RULES;pub use shaping::GUJARATI_RULES;pub use shaping::GURMUKHI_RULES;pub use shaping::KANNADA_RULES;pub use shaping::KHMER_RULES;pub use shaping::LAO_RULES;pub use shaping::MALAYALAM_RULES;pub use shaping::ORIYA_RULES;pub use shaping::SINHALA_RULES;pub use shaping::TAMIL_RULES;pub use shaping::TELUGU_RULES;pub use shaping::THAI_RULES;pub use style::synthetic_italic_shear;pub use style::Style;pub use style::DEFAULT_SYNTHETIC_ITALIC_DEG;pub use style::ITALIC_ANGLE_EPSILON_DEG;
Modules§
- bidi
- Unicode Bidirectional Algorithm — UAX #9 character classes,
paragraph-level resolution (rules P1 / P2 / P3), explicit-level
/ override / isolate stack (rules X1..X9), weak-type resolution
(rules W1..W7), bracket-pair resolution (rule N0 — full
normative
BidiBrackets.txttable, Unicode 16.0), neutral-type resolution (rules N1 and N2), implicit-level resolution (rules I1 / I2), line-level reordering (rules L1 / L2 / L3), and bidi mirroring (rule L4 — fullBidiMirroring.txttable, Unicode 16.0). - color
- Small colour primitives used by the rasterizer + composer.
- color_
glyph - Color-glyph rasterizer — bridges
oxideav_ttf::ColorBitmap(raw CBDT PNG bytes + per-glyph metrics) to a straight-alpha RGBA8 buffer. - face
Face— owning wrapper aroundoxideav_ttf::Font/oxideav_otf::Fontplus per-face identity for the glyph-bitmap cache.- face_
chain FaceChain— ordered list of faces consulted in priority order when shaping. Round-2 fallback support: when the primary face doesn’t have a glyph for a codepoint, the chain walks down the list until one does, falling back to the primary’s.notdefonly if no face provides a glyph.- layout
- Single-line measurement + word-wrap helpers for round-1.
- shaper
- Text shaper: cmap → ligature substitution → pair kerning → mark-to-base attachment → mark-to-mark stacking → cursive attachment.
- shaping
- Script-aware shaping helpers layered on top of the cmap → GSUB →
GPOS pipeline in
crate::shaper. - style
- Font request style — italic knob the shaper honours when emitting glyph paths.
- variations
- Variable-font metrics + style-attribute tables that scribe parses
locally —
MVAR,HVAR,VVAR,STAT, plusnametable id resolution and the CFF2 INDEX walker.
Structs§
- Named
Instance - One named instance (a pre-defined coordinate vector).
- Variation
Axis - One variation axis as published in the font’s
fvartable. All values are in user-space units (Fixed 16.16 scaled to f32 here).
Enums§
- Error
- Errors emitted by the scribe pipeline.