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}