fea-rs-ast
A Rust port of Python's fontTools.feaLib.ast
library, providing a fontTools-compatible AST (Abstract Syntax Tree) for OpenType Feature Files.
This crate builds on top of the fea-rs
parser, providing a higher-level, more ergonomic interface that matches the familiar fontTools API
while leveraging Rust's type safety and performance.
Overview
OpenType Feature Files (.fea) define advanced typographic features for fonts using a domain-specific language. This crate provides:
- Parsing: Load and parse feature files into a structured AST using
fea-rs. - Construction: Programmatically build feature file structures
- Serialization: Convert AST back to valid feature file syntax via the [
AsFea] trait - Transformation: Modify AST using the visitor pattern
Architecture
The crate provides two main statement enums:
- [
Statement]: All possible statements in a feature file, regardless of context - [
ToplevelItem]: Only statements valid at the top level of a feature file
Both implement the [AsFea] trait for serialization back to .fea syntax.
Examples
Loading an Existing Feature File
Parse a feature file from a string:
use ;
let fea_code = r#"
languagesystem DFLT dflt;
feature smcp {
sub a by a.smcp;
sub b by b.smcp;
} smcp;
"#;
// Simple parsing without glyph name resolution
let feature_file = try_from.unwrap;
// Or with full resolution support
let feature_file = new_from_fea.unwrap;
// Serialize back to .fea syntax
let output = feature_file.as_fea;
println!;
Constructing New Statements
Build feature file structures programmatically:
use *;
// Create a glyph class definition
let lowercase = new;
// Create a single substitution statement
let subst = new;
// Create a feature block
let feature = new;
// Build the complete feature file
let feature_file = new;
// Serialize to .fea syntax
let output = feature_file.as_fea;
assert!;
assert!;
assert!;
Using the Visitor Pattern
Transform AST structures by implementing the [LayoutVisitor] trait:
use *;
// Create a visitor that renames all features
// Use the visitor
let fea_code = r#"
feature liga {
sub f i by fi;
} liga;
"#;
let mut feature_file = try_from.unwrap;
let mut visitor = FeatureRenamer ;
visitor.visit.unwrap;
let output = feature_file.as_fea;
assert!;
More Complex Visitor: Glyph Name Substitution
use *;
use HashMap;
// Visitor that replaces glyph names throughout the AST
Feature Coverage
This crate supports most OpenType feature file constructs:
- GSUB: Single, Multiple, Alternate, Ligature, Contextual, and Reverse Chaining substitutions
- GPOS: Single, Pair, Cursive, Mark-to-Base, Mark-to-Ligature, and Mark-to-Mark positioning
- Tables: GDEF, BASE, head, hhea, name, OS/2, STAT, vhea
- Contextual Rules: Chaining context and ignore statements
- Variable Fonts: Conditionsets and variation blocks
- Lookups: Lookup blocks with flags and references
- Features: Feature blocks with useExtension
Features which fea-rs parses which this crate does not currently support:
- Glyphs number variables in value records
- CID-keyed glyph names
Compatibility
The API closely mirrors fontTools' Python API where practical, making it easier to port existing Python code to Rust. Key differences:
- Rust's type system provides compile-time guarantees about statement validity
- The [
Statement] enum distinguishes between all possible statements - The [
ToplevelItem] enum ensures only valid top-level constructs - Location tracking uses byte ranges (
Range<usize>) instead of line/column numbers
Re-exports
This crate re-exports the underlying [fea_rs] parser for advanced use cases where
direct access to the parse tree is needed.
License
This project is licensed under the MIT License or Apache-2.0 License, at your option.