1use crate::parser::OptionLog;
2use rustybuzz::ttf_parser;
3
4struct Builder<'a>(&'a mut String);
5
6impl Builder<'_> {
7 fn finish(&mut self) {
8 if !self.0.is_empty() {
9 self.0.pop(); }
11 }
12}
13
14impl ttf_parser::OutlineBuilder for Builder<'_> {
15 fn move_to(&mut self, x: f32, y: f32) {
16 use std::fmt::Write;
17 write!(self.0, "M {} {} ", x, y).unwrap()
18 }
19
20 fn line_to(&mut self, x: f32, y: f32) {
21 use std::fmt::Write;
22 write!(self.0, "L {} {} ", x, y).unwrap()
23 }
24
25 fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
26 use std::fmt::Write;
27 write!(self.0, "Q {} {} {} {} ", x1, y1, x, y).unwrap()
28 }
29
30 fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32) {
31 use std::fmt::Write;
32 write!(self.0, "C {} {} {} {} {} {} ", x1, y1, x2, y2, x, y).unwrap()
33 }
34
35 fn close(&mut self) {
36 self.0.push_str("Z ")
37 }
38}
39
40trait XmlWriterExt {
41 fn write_color_attribute(&mut self, name: &str, ts: ttf_parser::RgbaColor);
42 fn write_transform_attribute(&mut self, name: &str, ts: ttf_parser::Transform);
43 fn write_spread_method_attribute(&mut self, method: ttf_parser::colr::GradientExtend);
44}
45
46impl XmlWriterExt for xmlwriter::XmlWriter {
47 fn write_color_attribute(&mut self, name: &str, color: ttf_parser::RgbaColor) {
48 self.write_attribute_fmt(
49 name,
50 format_args!("rgb({}, {}, {})", color.red, color.green, color.blue),
51 );
52 }
53
54 fn write_transform_attribute(&mut self, name: &str, ts: ttf_parser::Transform) {
55 if ts.is_default() {
56 return;
57 }
58
59 self.write_attribute_fmt(
60 name,
61 format_args!(
62 "matrix({} {} {} {} {} {})",
63 ts.a, ts.b, ts.c, ts.d, ts.e, ts.f
64 ),
65 );
66 }
67
68 fn write_spread_method_attribute(&mut self, extend: ttf_parser::colr::GradientExtend) {
69 self.write_attribute(
70 "spreadMethod",
71 match extend {
72 ttf_parser::colr::GradientExtend::Pad => &"pad",
73 ttf_parser::colr::GradientExtend::Repeat => &"repeat",
74 ttf_parser::colr::GradientExtend::Reflect => &"reflect",
75 },
76 );
77 }
78}
79
80pub(crate) struct GlyphPainter<'a> {
82 pub(crate) face: &'a ttf_parser::Face<'a>,
83 pub(crate) svg: &'a mut xmlwriter::XmlWriter,
84 pub(crate) path_buf: &'a mut String,
85 pub(crate) gradient_index: usize,
86 pub(crate) clip_path_index: usize,
87 pub(crate) palette_index: u16,
88 pub(crate) transform: ttf_parser::Transform,
89 pub(crate) outline_transform: ttf_parser::Transform,
90 pub(crate) transforms_stack: Vec<ttf_parser::Transform>,
91}
92
93impl<'a> GlyphPainter<'a> {
94 fn write_gradient_stops(&mut self, stops: ttf_parser::colr::GradientStopsIter) {
95 for stop in stops {
96 self.svg.start_element("stop");
97 self.svg.write_attribute("offset", &stop.stop_offset);
98 self.svg.write_color_attribute("stop-color", stop.color);
99 let opacity = f32::from(stop.color.alpha) / 255.0;
100 self.svg.write_attribute("stop-opacity", &opacity);
101 self.svg.end_element();
102 }
103 }
104
105 fn paint_solid(&mut self, color: ttf_parser::RgbaColor) {
106 self.svg.start_element("path");
107 self.svg.write_color_attribute("fill", color);
108 let opacity = f32::from(color.alpha) / 255.0;
109 self.svg.write_attribute("fill-opacity", &opacity);
110 self.svg
111 .write_transform_attribute("transform", self.outline_transform);
112 self.svg.write_attribute("d", self.path_buf);
113 self.svg.end_element();
114 }
115
116 fn paint_linear_gradient(&mut self, gradient: ttf_parser::colr::LinearGradient<'a>) {
117 let gradient_id = format!("lg{}", self.gradient_index);
118 self.gradient_index += 1;
119
120 let gradient_transform = paint_transform(self.outline_transform, self.transform);
121
122 self.svg.start_element("linearGradient");
129 self.svg.write_attribute("id", &gradient_id);
130 self.svg.write_attribute("x1", &gradient.x0);
131 self.svg.write_attribute("y1", &gradient.y0);
132 self.svg.write_attribute("x2", &gradient.x1);
133 self.svg.write_attribute("y2", &gradient.y1);
134 self.svg.write_attribute("gradientUnits", &"userSpaceOnUse");
135 self.svg.write_spread_method_attribute(gradient.extend);
136 self.svg
137 .write_transform_attribute("gradientTransform", gradient_transform);
138 self.write_gradient_stops(
139 gradient.stops(self.palette_index, self.face.variation_coordinates()),
140 );
141 self.svg.end_element();
142
143 self.svg.start_element("path");
144 self.svg
145 .write_attribute_fmt("fill", format_args!("url(#{})", gradient_id));
146 self.svg
147 .write_transform_attribute("transform", self.outline_transform);
148 self.svg.write_attribute("d", self.path_buf);
149 self.svg.end_element();
150 }
151
152 fn paint_radial_gradient(&mut self, gradient: ttf_parser::colr::RadialGradient<'a>) {
153 let gradient_id = format!("rg{}", self.gradient_index);
154 self.gradient_index += 1;
155
156 self.svg.start_element("radialGradient");
157 self.svg.write_attribute("id", &gradient_id);
158 self.svg.write_attribute("cx", &gradient.x1);
159 self.svg.write_attribute("cy", &gradient.y1);
160 self.svg.write_attribute("r", &gradient.r1);
161 self.svg.write_attribute("fr", &gradient.r0);
162 self.svg.write_attribute("fx", &gradient.x0);
163 self.svg.write_attribute("fy", &gradient.y0);
164 self.svg.write_attribute("gradientUnits", &"userSpaceOnUse");
165 self.svg.write_spread_method_attribute(gradient.extend);
166 self.svg
167 .write_transform_attribute("gradientTransform", self.transform);
168 self.write_gradient_stops(
169 gradient.stops(self.palette_index, self.face.variation_coordinates()),
170 );
171 self.svg.end_element();
172
173 self.svg.start_element("path");
174 self.svg
175 .write_attribute_fmt("fill", format_args!("url(#{})", gradient_id));
176 self.svg
177 .write_transform_attribute("transform", self.outline_transform);
178 self.svg.write_attribute("d", self.path_buf);
179 self.svg.end_element();
180 }
181
182 fn paint_sweep_gradient(&mut self, _: ttf_parser::colr::SweepGradient<'a>) {
183 println!("Warning: sweep gradients are not supported.")
184 }
185}
186
187fn paint_transform(
188 outline_transform: ttf_parser::Transform,
189 transform: ttf_parser::Transform,
190) -> ttf_parser::Transform {
191 let outline_transform = tiny_skia_path::Transform::from_row(
192 outline_transform.a,
193 outline_transform.b,
194 outline_transform.c,
195 outline_transform.d,
196 outline_transform.e,
197 outline_transform.f,
198 );
199
200 let gradient_transform = tiny_skia_path::Transform::from_row(
201 transform.a,
202 transform.b,
203 transform.c,
204 transform.d,
205 transform.e,
206 transform.f,
207 );
208
209 let gradient_transform = outline_transform
210 .invert()
211 .log_none(|| log::warn!("Failed to calculate transform for gradient in glyph."))
212 .unwrap_or_default()
213 .pre_concat(gradient_transform);
214
215 ttf_parser::Transform {
216 a: gradient_transform.sx,
217 b: gradient_transform.ky,
218 c: gradient_transform.kx,
219 d: gradient_transform.sy,
220 e: gradient_transform.tx,
221 f: gradient_transform.ty,
222 }
223}
224
225impl GlyphPainter<'_> {
226 fn clip_with_path(&mut self, path: &str) {
227 let clip_id = format!("cp{}", self.clip_path_index);
228 self.clip_path_index += 1;
229
230 self.svg.start_element("clipPath");
231 self.svg.write_attribute("id", &clip_id);
232 self.svg.start_element("path");
233 self.svg
234 .write_transform_attribute("transform", self.outline_transform);
235 self.svg.write_attribute("d", &path);
236 self.svg.end_element();
237 self.svg.end_element();
238
239 self.svg.start_element("g");
240 self.svg
241 .write_attribute_fmt("clip-path", format_args!("url(#{})", clip_id));
242 }
243}
244
245impl<'a> ttf_parser::colr::Painter<'a> for GlyphPainter<'a> {
246 fn outline_glyph(&mut self, glyph_id: ttf_parser::GlyphId) {
247 self.path_buf.clear();
248 let mut builder = Builder(self.path_buf);
249 match self.face.outline_glyph(glyph_id, &mut builder) {
250 Some(v) => v,
251 None => return,
252 };
253 builder.finish();
254
255 self.outline_transform = self.transform;
257 }
258
259 fn push_layer(&mut self, mode: ttf_parser::colr::CompositeMode) {
260 self.svg.start_element("g");
261
262 use ttf_parser::colr::CompositeMode;
263 let mode = match mode {
266 CompositeMode::SourceOver => "normal",
267 CompositeMode::Screen => "screen",
268 CompositeMode::Overlay => "overlay",
269 CompositeMode::Darken => "darken",
270 CompositeMode::Lighten => "lighten",
271 CompositeMode::ColorDodge => "color-dodge",
272 CompositeMode::ColorBurn => "color-burn",
273 CompositeMode::HardLight => "hard-light",
274 CompositeMode::SoftLight => "soft-light",
275 CompositeMode::Difference => "difference",
276 CompositeMode::Exclusion => "exclusion",
277 CompositeMode::Multiply => "multiply",
278 CompositeMode::Hue => "hue",
279 CompositeMode::Saturation => "saturation",
280 CompositeMode::Color => "color",
281 CompositeMode::Luminosity => "luminosity",
282 _ => {
283 println!("Warning: unsupported blend mode: {:?}", mode);
284 "normal"
285 }
286 };
287 self.svg.write_attribute_fmt(
288 "style",
289 format_args!("mix-blend-mode: {}; isolation: isolate", mode),
290 );
291 }
292
293 fn pop_layer(&mut self) {
294 self.svg.end_element(); }
296
297 fn push_translate(&mut self, tx: f32, ty: f32) {
298 self.push_transform(ttf_parser::Transform::new(1.0, 0.0, 0.0, 1.0, tx, ty));
299 }
300
301 fn push_scale(&mut self, sx: f32, sy: f32) {
302 self.push_transform(ttf_parser::Transform::new(sx, 0.0, 0.0, sy, 0.0, 0.0));
303 }
304
305 fn push_rotate(&mut self, angle: f32) {
306 let cc = (angle * std::f32::consts::PI).cos();
307 let ss = (angle * std::f32::consts::PI).sin();
308 self.push_transform(ttf_parser::Transform::new(cc, ss, -ss, cc, 0.0, 0.0));
309 }
310
311 fn push_skew(&mut self, skew_x: f32, skew_y: f32) {
312 let x = (-skew_x * std::f32::consts::PI).tan();
313 let y = (skew_y * std::f32::consts::PI).tan();
314 self.push_transform(ttf_parser::Transform::new(1.0, y, x, 1.0, 0.0, 0.0));
315 }
316
317 fn push_transform(&mut self, transform: ttf_parser::Transform) {
318 self.transforms_stack.push(self.transform);
319 self.transform = ttf_parser::Transform::combine(self.transform, transform);
320 }
321
322 fn paint(&mut self, paint: ttf_parser::colr::Paint<'a>) {
323 match paint {
324 ttf_parser::colr::Paint::Solid(color) => self.paint_solid(color),
325 ttf_parser::colr::Paint::LinearGradient(lg) => self.paint_linear_gradient(lg),
326 ttf_parser::colr::Paint::RadialGradient(rg) => self.paint_radial_gradient(rg),
327 ttf_parser::colr::Paint::SweepGradient(sg) => self.paint_sweep_gradient(sg),
328 }
329 }
330
331 fn pop_transform(&mut self) {
332 if let Some(ts) = self.transforms_stack.pop() {
333 self.transform = ts
334 }
335 }
336
337 fn push_clip(&mut self) {
338 self.clip_with_path(&self.path_buf.clone());
339 }
340
341 fn pop_clip(&mut self) {
342 self.svg.end_element();
343 }
344
345 fn push_clip_box(&mut self, clipbox: ttf_parser::colr::ClipBox) {
346 let x_min = clipbox.x_min;
347 let x_max = clipbox.x_max;
348 let y_min = clipbox.y_min;
349 let y_max = clipbox.y_max;
350
351 let clip_path = format!(
352 "M {} {} L {} {} L {} {} L {} {} Z",
353 x_min, y_min, x_max, y_min, x_max, y_max, x_min, y_max
354 );
355
356 self.clip_with_path(&clip_path);
357 }
358}