1use rusttype::{Font, Point, Rect, Scale};
2use svg::node::element::Path;
3
4pub struct Text {
5 pub path: Path,
6 pub data: String,
7 pub bounding_box: Rect<f32>,
8}
9
10impl Text {
11 pub fn new(path: Path, bounding_box: Rect<f32>, data: String) -> Self {
12 Self {
13 path,
14 bounding_box,
15 data,
16 }
17 }
18
19 pub fn builder() -> Builder<'static> {
20 Default::default()
21 }
22}
23
24pub struct Builder<'a> {
25 pub fill: &'a str,
26 pub size: f32,
27 pub start: Point<f32>,
28 pub letter_spacing: f32,
29}
30
31impl Default for Builder<'static> {
32 fn default() -> Self {
33 Self {
34 fill: "#000",
35 size: 32.,
36 start: Point { x: 0., y: 0. },
37 letter_spacing: 1.,
38 }
39 }
40}
41
42impl Builder<'_> {
43 pub fn size(mut self, size: f32) -> Self {
44 self.size = size;
45 self
46 }
47 pub fn start(mut self, start: Point<f32>) -> Self {
48 self.start = start;
49 self
50 }
51
52 pub fn build(&self, font: &Font, text: &str) -> Text {
53 let mut d = String::new();
54 let mut x = self.start.x;
55
56 let scale = Scale::uniform(self.size);
57 let v_metrics = font.v_metrics(scale);
58 let glyphs_height = v_metrics.ascent - v_metrics.descent;
59
60 for glyph in font.layout(
61 text,
62 scale,
63 Point {
64 x,
65 y: self.start.y + v_metrics.ascent,
66 },
67 ) {
68 let bounding_box = glyph.unpositioned().exact_bounding_box().unwrap();
69 x += bounding_box.min.x;
70
71 glyph.build_outline(&mut crate::Builder {
72 x: x,
73 y: glyphs_height + bounding_box.min.y,
74 d: &mut d,
75 });
76
77 x += bounding_box.width() + self.letter_spacing;
78 }
79
80 let bounding_box = Rect {
81 min: self.start,
82 max: Point {
83 x,
84 y: glyphs_height,
85 },
86 };
87 Text::new(
88 Path::new().set("d", d.clone()).set("fill", "#000"),
89 bounding_box,
90 d.clone(),
91 )
92 }
93}