typf-core: The Engine Room
Six stages from text to pixels. No magic, just engineering.
Text enters as Unicode chaos, exits as perfect pixels. This crate is the
engine room that makes text rendering work when half your characters are
Arabic and the other half are emoji.
The Pipeline: Brutal Simplicity
Every text walks this path, no shortcuts:
- Input Parsing - Raw strings → structured data. We don't guess encoding.
- Unicode Processing - Bidi, scripts, segmentation. The hard stuff.
- Font Selection - Right font for each character, period. Fallback that actually works.
- Shaping - Characters → positioned glyphs. Where HarfBuzz lives.
- Rendering - Glyphs → pixels/vectors. SIMD or die.
- Export - Your format, ready to ship.
Build Your First Pipeline
use typf_core::{Pipeline, RenderParams, ShapingParams};
use std::sync::Arc;
# use typf_core::traits::*;
# use typf_core::context::PipelineContext;
# use typf_core::error::TypfError;
# struct MyShaper;
# impl Stage for MyShaper {
# fn name(&self) -> &'static str { "test" }
# fn process(&self, _ctx: PipelineContext) -> Result<PipelineContext, TypfError> { unimplemented!() }
# }
# impl Shaper for MyShaper {
# fn name(&self) -> &'static str { "test" }
# fn shape(&self, _: &str, _: Arc<dyn FontRef>, _: &ShapingParams)
# -> typf_core::Result<typf_core::types::ShapingResult> { unimplemented!() }
# }
# struct MyRenderer;
# impl Stage for MyRenderer {
# fn name(&self) -> &'static str { "test" }
# fn process(&self, _ctx: PipelineContext) -> Result<PipelineContext, TypfError> { unimplemented!() }
# }
# impl Renderer for MyRenderer {
# fn name(&self) -> &'static str { "test" }
# fn render(&self, _: &typf_core::types::ShapingResult, _: Arc<dyn FontRef>, _: &RenderParams)
# -> typf_core::Result<typf_core::types::RenderOutput> { unimplemented!() }
# }
# struct MyExporter;
# impl Stage for MyExporter {
# fn name(&self) -> &'static str { "test" }
# fn process(&self, _ctx: PipelineContext) -> Result<PipelineContext, TypfError> { unimplemented!() }
# }
# impl Exporter for MyExporter {
# fn name(&self) -> &'static str { "test" }
# fn export(&self, _: &typf_core::types::RenderOutput)
# -> typf_core::Result<Vec<u8>> { unimplemented!() }
# fn extension(&self) -> &'static str { "png" }
# fn mime_type(&self) -> &'static str { "image/png" }
# }
# fn load_font() -> Arc<dyn FontRef> { unimplemented!() }
let pipeline = Pipeline::builder()
.shaper(Arc::new(MyShaper))
.renderer(Arc::new(MyRenderer))
.exporter(Arc::new(MyExporter))
.build()?;
let font = load_font();
let output = pipeline.process(
"Hello, World!",
font,
&ShapingParams::default(),
&RenderParams::default(),
)?;
# Ok::<(), typf_core::TypfError>(())
The Traits That Power Everything
Want to add your own backend? Implement one of these:
- [
Stage] - The foundation every pipeline component builds upon.
- [
Shaper] - Where characters become glyphs.
- [
Renderer] - Where glyphs become images.
- [
Exporter] - Where images become files.
- [
traits::FontRef] - Your window into font data.
Data flows through the types in [types] - these structures carry
the results from one stage to the next.