orbtk_tinyskia/tinyskia/
font.rs1use rusttype::OutlineBuilder;
2use tiny_skia::{FillRule, Paint, PathBuilder, Pixmap, Transform};
3
4#[derive(Debug)]
5struct GlyphTracer {
6 path_builder: PathBuilder,
7 position: rusttype::Point<f32>,
8}
9
10impl GlyphTracer {
11 #[inline(always)]
12 fn map_point(&self, x: f32, y: f32) -> (f32, f32) {
13 (self.position.x + x, self.position.y + y)
14 }
15}
16
17impl OutlineBuilder for GlyphTracer {
18 fn move_to(&mut self, x: f32, y: f32) {
19 let (x, y) = self.map_point(x, y);
20 self.path_builder.move_to(x, y);
21 }
22
23 fn line_to(&mut self, x: f32, y: f32) {
24 let (x, y) = self.map_point(x, y);
25 self.path_builder.line_to(x, y);
26 }
27
28 fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
29 let (x, y) = self.map_point(x, y);
30 let (x1, y1) = self.map_point(x1, y1);
31 self.path_builder.quad_to(x1, y1, x, y);
32 }
33
34 fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32) {
35 let (x, y) = self.map_point(x, y);
36 let (x1, y1) = self.map_point(x1, y1);
37 let (x2, y2) = self.map_point(x2, y2);
38 self.path_builder.cubic_to(x1, y1, x2, y2, x, y);
39 }
40
41 fn close(&mut self) {
42 self.path_builder.close();
43 }
44}
45
46#[derive(Debug, Clone)]
48pub struct Font {
49 inner: rusttype::Font<'static>,
50}
51
52impl Font {
53 pub fn from_bytes(bytes: &'static [u8]) -> Result<Self, &'static str> {
55 rusttype::Font::try_from_bytes(bytes)
56 .map(|font| Font { inner: font })
57 .ok_or("Could not load font from bytes")
58 }
59
60 pub fn measure_text(&self, text: &str, size: f64) -> (f64, f64) {
62 let scale = rusttype::Scale::uniform(size as f32);
63 let v_metrics = self.inner.v_metrics(scale);
64 let offset = rusttype::point(0.0, v_metrics.ascent);
65
66 let pixel_height = size.ceil();
67
68 let glyphs: Vec<rusttype::PositionedGlyph> =
70 self.inner.layout(text, scale, offset).collect();
71
72 let width = glyphs
73 .iter()
74 .rev()
75 .map(|g| g.position().x as f32 + g.unpositioned().h_metrics().advance_width)
76 .next()
77 .unwrap_or(0.0)
78 .ceil() as f64;
79
80 (width, pixel_height)
81 }
82
83 pub fn render_text(
85 &self,
86 font_size: f64,
87 paint: &Paint,
88 pixmap: &mut Pixmap,
89 position: (f64, f64),
90 text: &str,
91 ) {
92 let scale = rusttype::Scale::uniform(font_size as f32);
93
94 let v_metrics = self.inner.v_metrics(scale);
99 let offset = rusttype::point(0.0, v_metrics.ascent);
100
101 let glyphs: Vec<rusttype::PositionedGlyph> =
102 self.inner.layout(text, scale, offset).collect();
103
104 let mut glyph_tracer = GlyphTracer {
105 path_builder: PathBuilder::new(),
106 position: rusttype::point(0.0, 0.0),
107 };
108 for g in glyphs.iter() {
109 let mut gpos = match g.pixel_bounding_box() {
110 Some(bbox) => rusttype::point(bbox.min.x as f32, bbox.min.y as f32),
111 None => {
112 continue;
113 }
114 };
115 gpos.x += position.0 as f32;
116 gpos.y += position.1 as f32;
117 glyph_tracer.position = gpos;
118 g.build_outline(&mut glyph_tracer);
119 }
120 if let Some(path) = glyph_tracer.path_builder.finish() {
121 pixmap.fill_path(&path, paint, FillRule::Winding, Transform::identity(), None);
122 }
123 }
124}