rassa is a Rust rewrite of the ASS subtitle rendering stack. The project is split into small Rust crates for parsing, font lookup, shaping, rasterization, layout, rendering, and C ABI packaging.
The preferred integration target for new applications is the rassa-branded shared library, librassa.so, built from the rassa crate. A separate compatibility package can still build libass.so for applications that specifically need a libass-shaped linker target.
Status: experimental.
librassa.sois the forward-looking ABI target and may evolve with rassa.libass.sois provided as a compatibility target, but this project does not promise to track every upstream libass behavior forever.
crates/rassa-core— shared public Rust data types.crates/rassa-parse— ASS/SSA parser and style/event model.crates/rassa-fonts— font provider integration.crates/rassa-shape— text shaping.crates/rassa-raster— Rust glyph rasterization helpers.crates/rassa-layout— subtitle event layout.crates/rassa-render— image-plane renderer.crates/rassa— Rust API crate and native shared-library target, producinglibrassa.so.crates/rassa-capi— C ABI implementation exported from Rust.crates/rassa-libass-capi— libass-compatible shared-library target namedass, producinglibass.so.crates/rassa-check— command-line checker/debug utility.crates/rassa-test— compatibility and pixel-diff tests.include/ass— current public C headers.pkgconfig/rassa.pc— pkg-config metadata for the native rassa shared library.pkgconfig/libass.pc— pkg-config metadata for the libass-compatible target.
- Rust 1.85 or newer.
- A Unix-like development environment for the current C ABI packaging flow.
- A C compiler if you want to compile C smoke tests or downstream C/C++ applications.
Build all Rust crates:
cargo build --workspaceBuild the release rassa shared object for new applications:
cargo build --release -p rassaThe native shared library is produced by Cargo as:
# Linux
target/release/librassa.so
# Windows GNU target
target/x86_64-pc-windows-gnu/release/rassa.dll
# Web/wasm target
target/wasm32-unknown-unknown/release/rassa.wasm
Build the release libass-compatible shared object only when an application expects libass.so or -lass:
cargo build --release -p rassa-libass-capiThe compatibility shared library is produced by Cargo as:
# Linux
target/release/libass.so
# Windows GNU target
target/x86_64-pc-windows-gnu/release/ass.dll
# Web/wasm target
target/wasm32-unknown-unknown/release/ass.wasm
The compatibility crate sets the ELF SONAME to libass.so only on ELF targets, so Windows DLL, Darwin dylib/check, and WebAssembly builds do not receive ELF-only linker flags.
Build the checker utility and C ABI crates used during development:
cargo build --release -p rassa-check -p rassa -p rassa-libass-capiUse this path for new applications that want to depend on rassa without making a strict libass compatibility promise:
- Header include path:
include/ - Current public headers:
include/ass/ass.handinclude/ass/ass_types.h - Library name:
librassa.so - Linker flag:
-lrassa - pkg-config file:
pkgconfig/rassa.pc
For local development from the repository root:
cargo build --release -p rassa
export PKG_CONFIG_PATH="$PWD/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}"Compile a C file against the rassa shared object:
cc example.c $(pkg-config --cflags --libs rassa) -Wl,-rpath,"$PWD/target/release" -o exampleOr without pkg-config:
cc example.c -Iinclude -Ltarget/release -lrassa -Wl,-rpath,"$PWD/target/release" -o exampleThe current native C ABI reuses the same public entry points as the compatibility layer. New applications should link to librassa.so / -lrassa so their dependency is on rassa itself, not on the libass.so drop-in package name.
Use this path only for applications or plugins that already expect a libass-shaped package:
- Header include path:
include/ - Public headers:
include/ass/ass.handinclude/ass/ass_types.h - Library name:
libass.so - Linker flag:
-lass - pkg-config file:
pkgconfig/libass.pc
For local development from the repository root:
cargo build --release -p rassa-libass-capi
export PKG_CONFIG_PATH="$PWD/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}"Compile a C file against rassa's libass-compatible ABI:
cc example.c $(pkg-config --cflags --libs libass) -Wl,-rpath,"$PWD/target/release" -o exampleOr without pkg-config:
cc example.c -Iinclude -Ltarget/release -lass -Wl,-rpath,"$PWD/target/release" -o exampleMinimal C smoke test:
#include <ass/ass.h>
#include <stdio.h>
int main(void) {
ASS_Library *library = ass_library_init();
if (!library) {
return 1;
}
ASS_Renderer *renderer = ass_renderer_init(library);
if (!renderer) {
ass_library_done(library);
return 1;
}
ass_set_frame_size(renderer, 640, 360);
printf("libass ABI version: 0x%x\n", ass_library_version());
ass_renderer_done(renderer);
ass_library_done(library);
return 0;
}Build and run it from the repository root:
cat > /tmp/rassa-smoke.c <<'C'
#include <ass/ass.h>
#include <stdio.h>
int main(void) {
ASS_Library *library = ass_library_init();
if (!library) return 1;
ASS_Renderer *renderer = ass_renderer_init(library);
if (!renderer) {
ass_library_done(library);
return 1;
}
ass_set_frame_size(renderer, 640, 360);
printf("libass ABI version: 0x%x\n", ass_library_version());
ass_renderer_done(renderer);
ass_library_done(library);
return 0;
}
C
cargo build --release -p rassa-libass-capi
cc /tmp/rassa-smoke.c -Iinclude -Ltarget/release -lass -Wl,-rpath,"$PWD/target/release" -o /tmp/rassa-smoke
/tmp/rassa-smokeFor consumers that currently link against libass:
- Build
rassa-libass-capiin release mode. - Put
target/release/libass.soin the runtime library search path used by your application. - Put
include/in the C/C++ compiler include path. - Put
pkgconfig/inPKG_CONFIG_PATHif your build usespkg-config --libs libass. - Rebuild your downstream application and run its subtitle-rendering tests.
Example:
cargo build --release -p rassa-libass-capi
export PKG_CONFIG_PATH="$PWD/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}"
export LD_LIBRARY_PATH="$PWD/target/release${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"Then build the downstream project as usual. If it uses pkg-config, it should see:
pkg-config --cflags --libs libassExpected output shape:
-I.../include -L.../target/release -lass
Use the rassa crate for native Rust applications. The public facade exports the parser, renderer, font provider traits, core geometry/color types, and a small safe Script/Renderer API.
Add the crate from crates.io:
[dependencies]
rassa = "0.1.0"Or, inside this repository, use a path dependency while developing:
[dependencies]
rassa = { path = "crates/rassa" }Minimal render example:
use rassa::{Renderer, Script};
fn main() -> rassa::RassaResult<()> {
let script = Script::parse(r#"[Script Info]
PlayResX: 640
PlayResY: 360
[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,sans,48,&H00FFFFFF,&H0000FFFF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,2,1,5,10,10,10,1
[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,0:00:00.00,0:00:05.00,Default,,0000,0000,0000,,Hello from rassa
"#)?;
let frame = Renderer::new().render_frame(&script, 500)?;
println!("rendered {} image plane(s)", frame.planes.len());
for plane in &frame.planes {
println!(
"plane at ({}, {}) size {}x{} stride {} color 0x{:08x}",
plane.destination.x,
plane.destination.y,
plane.size.width,
plane.size.height,
plane.stride,
plane.color.0,
);
}
Ok(())
}If you need control over font lookup or frame sizing, build a provider/config explicitly:
use rassa::{FontconfigProvider, Renderer, RendererConfig, Script, Size};
fn main() -> rassa::RassaResult<()> {
let ass_text = std::fs::read_to_string("subtitle.ass")
.expect("failed to read subtitle.ass");
let script = Script::parse(&ass_text)?;
let provider = FontconfigProvider::new();
let config = RendererConfig {
frame: Size { width: 1920, height: 1080 },
storage: script.play_res(),
..script.default_config()
};
let frame = Renderer::new().render_frame_with_config(&script, &provider, 12_500, &config)?;
println!("{} plane(s)", frame.planes.len());
Ok(())
}The Rust API is not the same as either shared-library package. Use the rassa crate directly for native Rust integration, librassa.so for new C/C++ integrations, and rassa-libass-capi only when you need the libass.so package name.
The first published version is 0.1.0.
| Crate | Purpose | Docs |
|---|---|---|
rassa |
Safe Rust facade and native librassa.so target |
https://docs.rs/rassa |
rassa-core |
Shared data types and ASS enums | https://docs.rs/rassa-core |
rassa-parse |
ASS/SSA parser | https://docs.rs/rassa-parse |
rassa-fonts |
Cross-platform font provider traits and discovery | https://docs.rs/rassa-fonts |
rassa-unibreak |
Pure-Rust Unicode line and word breaking | https://docs.rs/rassa-unibreak |
rassa-unicode |
Pure-Rust Unicode bidi and segmentation helpers | https://docs.rs/rassa-unicode |
rassa-shape |
Text shaping | https://docs.rs/rassa-shape |
rassa-raster |
Glyph rasterization helpers | https://docs.rs/rassa-raster |
rassa-layout |
Subtitle event layout | https://docs.rs/rassa-layout |
rassa-render |
Image-plane renderer | https://docs.rs/rassa-render |
rassa-capi |
Shared C ABI implementation | https://docs.rs/rassa-capi |
rassa-libass-capi |
libass.so compatibility target |
https://docs.rs/rassa-libass-capi |
rassa-check |
CLI/library smoke checker and PNG/JPEG/PGM renderer | https://docs.rs/rassa-check |
Generate local docs for the whole workspace:
cargo doc --workspace --no-deps --openGenerate docs for only the public facade:
cargo doc -p rassa --no-deps --openInstall the checker from crates.io:
cargo install rassa-checkRender the built-in smoke script to PNG:
rassa-check --output rassa-check.png --time-ms 500 --width 640 --height 360Render your own ASS/SSA file:
rassa-check --input subtitles.ass --output frame.png --time-ms 12500 --width 1920 --height 1080Supported output formats are inferred from the file extension: .png, .jpg/.jpeg, or .pgm. You can also set them explicitly with --format png|jpg|pgm.
Common local checks:
cargo fmt --all
cargo test -q
cargo clippy --all-targets -- -D warnings
cargo build --release -p rassa-check -p rassa -p rassa-libass-capiBroad corpus pixel-diff report (runs all generated upstream frames and logs mismatches without treating known parity gaps as a master-branch failure):
cargo test -p rassa-test upstream_generated_broad_corpus_pixel_diff_report -- --ignored --nocaptureOpt-in strict broad corpus gate for renderer-parity work where the references are expected to be pixel-perfect:
RASSA_STRICT_BROAD_PIXEL_DIFF=1 \
cargo test -p rassa-test upstream_generated_broad_corpus_pixel_diff_report -- --ignored --nocaptureDump actual/target compare PNGs for debugging:
RASSA_DUMP_COMPARE=/tmp/rassa-compare-dump \
cargo test -p rassa-test upstream_generated_broad_corpus_pixel_diff_report -- --ignored --nocaptureFilter the broad corpus by fixture name:
RASSA_BROAD_FILTER=broad_box RASSA_STRICT_BROAD_PIXEL_DIFF=1 \
cargo test -p rassa-test upstream_generated_broad_corpus_pixel_diff_report -- --ignored --nocapture- Keep this project a Rust rewrite. Do not vendor, compile, or wrap upstream libass C source in production code.
- Upstream libass fixtures, public headers, and generated reference artifacts may be used as compatibility test oracles when their licensing allows it.
- Keep ABI compatibility and rendering parity separate when reporting status: a symbol may exist before its behavior is fully compatible.
- Add tests for compatibility fixes. Prefer small focused tests plus corpus coverage for renderer changes.
- Run formatting, tests, clippy, and the relevant pixel-diff gates before submitting changes.
This repository is licensed under the MIT License. See LICENSE for details.
Some public compatibility headers keep their original notices where applicable.