Skip to main content

typf_core/
traits.rs

1//! The contracts that bind every backend together
2//!
3//! Five traits, infinite possibilities. Each trait defines a role
4//! in the pipeline, allowing you to swap implementations without
5//! touching a single line of user code.
6//!
7//! ## The Players
8//!
9//! - [`Stage`] - The foundation every pipeline component builds upon
10//! - [`FontRef`] - Your window into font data and metrics
11//! - [`Shaper`] - Where characters become glyphs
12//! - [`Renderer`] - Where glyphs become images
13//! - [`Exporter`] - Where images become files
14
15// this_file: crates/typf-core/src/traits.rs
16
17use crate::{error::Result, types::*, PipelineContext, RenderParams, ShapingParams};
18use std::sync::Arc;
19
20/// Every pipeline dancer learns these same steps
21///
22/// Implement Stage and your component can join the six-stage procession
23/// that transforms text into rendered output.
24///
25/// ```ignore
26/// struct MyStage;
27///
28/// impl Stage for MyStage {
29///     fn name(&self) -> &'static str {
30///         "my-stage"
31///     }
32///
33///     fn process(&self, context: PipelineContext) -> Result<PipelineContext> {
34///         // Transform the context, pass it forward
35///         Ok(context)
36///     }
37/// }
38/// ```
39pub trait Stage: Send + Sync {
40    /// Who are you? Used for debugging and logging
41    fn name(&self) -> &'static str;
42
43    /// Do your work and pass the context forward
44    ///
45    /// Take the context, make your changes, and return it for the next stage.
46    fn process(&self, context: PipelineContext) -> Result<PipelineContext>;
47}
48
49/// Your key to unlocking font secrets
50///
51/// Every font format speaks the same language through this trait.
52/// TTF, OTF, WOFF - they all expose their data and metrics the same way.
53///
54/// ```ignore
55/// struct MyFont {
56///     data: Vec<u8>,
57///     // ... your internal state
58/// }
59///
60/// impl FontRef for MyFont {
61///     fn data(&self) -> &[u8] {
62///         &self.data
63///     }
64///
65///     fn units_per_em(&self) -> u16 {
66///         1000 // Common for Type 1 fonts
67///     }
68///
69///     fn glyph_id(&self, ch: char) -> Option<GlyphId> {
70///         // Turn Unicode into font-specific glyph IDs
71///         Some(42)
72///     }
73///
74///     fn advance_width(&self, glyph_id: GlyphId) -> f32 {
75///         // How far to move after this glyph
76///         500.0
77///     }
78/// }
79/// ```
80pub trait FontRef: Send + Sync {
81    /// Raw font bytes as they live in the file
82    fn data(&self) -> &[u8];
83
84    /// Optional shared font bytes for zero-copy downstream consumption.
85    ///
86    /// Implementations that store their bytes in an `Arc` SHOULD override this to avoid per-call
87    /// allocations/copies in downstream libraries that require shared ownership.
88    fn data_shared(&self) -> Option<Arc<dyn AsRef<[u8]> + Send + Sync>> {
89        None
90    }
91
92    /// The font's internal coordinate system scale
93    ///
94    /// Used to convert between font units and rendered pixels.
95    /// Type 1 fonts use 1000, TrueType often uses 2048.
96    fn units_per_em(&self) -> u16;
97
98    /// Optional font-wide metrics in font units (ascent/descent/line gap).
99    ///
100    /// Backends that can parse OpenType tables SHOULD provide this to allow consumers to make
101    /// baseline/layout decisions without depending on a specific font parser.
102    fn metrics(&self) -> Option<FontMetrics> {
103        None
104    }
105
106    /// Find the glyph that represents this character
107    ///
108    /// Returns None when the font doesn't contain this character.
109    fn glyph_id(&self, ch: char) -> Option<GlyphId>;
110
111    /// How wide this glyph stands in font units
112    ///
113    /// This spacing determines how glyphs sit next to each other.
114    fn advance_width(&self, glyph_id: GlyphId) -> f32;
115
116    /// How many glyphs this font contains
117    ///
118    /// Useful for validation when shapers return glyph IDs.
119    fn glyph_count(&self) -> Option<u32> {
120        None // Not all implementations can provide this
121    }
122
123    /// Variable font axes from the fvar table.
124    ///
125    /// Returns None for non-variable fonts or if axes cannot be parsed.
126    /// Returns Some(empty vec) if the font has an fvar table with no axes.
127    fn variation_axes(&self) -> Option<Vec<VariationAxis>> {
128        None // Static fonts return None
129    }
130
131    /// Whether this font is a variable font (has fvar table).
132    fn is_variable(&self) -> bool {
133        self.variation_axes().is_some_and(|axes| !axes.is_empty())
134    }
135}
136
137/// Where characters learn their positions
138///
139/// Text shaping is where script rules, font features, and character clusters
140/// collide to produce perfectly positioned glyphs ready for rendering.
141pub trait Shaper: Send + Sync {
142    /// Identify yourself in logs and error messages
143    fn name(&self) -> &'static str;
144
145    /// Transform characters into positioned glyphs
146    fn shape(
147        &self,
148        text: &str,
149        font: Arc<dyn FontRef>,
150        params: &ShapingParams,
151    ) -> Result<ShapingResult>;
152
153    /// Can you handle this script?
154    ///
155    /// Returns false by default - implementations should explicitly declare support.
156    fn supports_script(&self, _script: &str) -> bool {
157        false
158    }
159
160    /// Flush any cached shaping data
161    fn clear_cache(&self) {}
162}
163
164/// Where glyphs become visible
165///
166/// Rasterizers turn positioned glyphs into pixels. Vector renderers
167/// turn them into paths. Both implement this trait.
168pub trait Renderer: Send + Sync {
169    /// Your renderer's signature
170    fn name(&self) -> &'static str;
171
172    /// Convert glyphs to visual output
173    fn render(
174        &self,
175        shaped: &ShapingResult,
176        font: Arc<dyn FontRef>,
177        params: &RenderParams,
178    ) -> Result<RenderOutput>;
179
180    /// Do you understand this output format?
181    ///
182    /// Returns false by default - implementations should explicitly declare support.
183    fn supports_format(&self, _format: &str) -> bool {
184        false
185    }
186
187    /// Free up rendering resources
188    fn clear_cache(&self) {}
189}
190
191/// The final step: pixels become files
192///
193/// Exporters know how to encode rendered output into the format
194/// users actually want - PNG, SVG, JSON, and more.
195pub trait Exporter: Send + Sync {
196    /// Who are you?
197    fn name(&self) -> &'static str;
198
199    /// Encode the rendered output as bytes
200    fn export(&self, output: &RenderOutput) -> Result<Vec<u8>>;
201
202    /// What file extension should be used?
203    fn extension(&self) -> &'static str;
204
205    /// What MIME type identifies your format?
206    fn mime_type(&self) -> &'static str;
207}