fj_text/
lib.rs

1use anyhow::{anyhow, Result};
2use bezier_rs::{Bezier, TValueType};
3use fj::{
4    core::{
5        objects::Region,
6        operations::{build::BuildRegion, update::UpdateRegion},
7        Core,
8    },
9    math::Winding,
10};
11use font::{glyph::Segment, Font, Glyph, Offset, Read};
12
13const DEFAULT_RESOLUTION: usize = 5;
14
15pub struct GlyphRegionBuilder {
16    glyph: Glyph,
17}
18
19impl GlyphRegionBuilder {
20    pub fn try_new<T: Read>(font: &mut Font<T>, character: char) -> Result<Self> {
21        let glyph = font.glyph(character)?;
22        match glyph {
23            Some(glyph) => Ok(Self { glyph }),
24            None => Err(anyhow!("Character not in font: {}", character)),
25        }
26    }
27
28    pub fn build(self, core: &mut Core) -> Vec<Region> {
29        let mut point_lists = vec![];
30        let mut a = Offset::default();
31        for contour in self.glyph.iter() {
32            a += contour.offset;
33            let mut beziers = vec![];
34            for segment in contour.iter() {
35                match *segment {
36                    Segment::Linear(mut b) => {
37                        b += a;
38                        beziers.push(Bezier::from_linear_coordinates(
39                            a.0 as f64, a.1 as f64, b.0 as f64, b.1 as f64,
40                        ));
41                        a = b;
42                    }
43                    Segment::Quadratic(mut b, mut c) => {
44                        b += a;
45                        c += b;
46                        beziers.push(Bezier::from_quadratic_coordinates(
47                            a.0 as f64, a.1 as f64, b.0 as f64, b.1 as f64, c.0 as f64, c.1 as f64,
48                        ));
49                        a = c;
50                    }
51                    Segment::Cubic(mut b, mut c, mut d) => {
52                        b += a;
53                        c += b;
54                        d += c;
55                        beziers.push(Bezier::from_cubic_coordinates(
56                            a.0 as f64, a.1 as f64, b.0 as f64, b.1 as f64, c.0 as f64, c.1 as f64,
57                            d.0 as f64, d.1 as f64,
58                        ));
59                        a = d;
60                    }
61                }
62            }
63            let mut res = vec![];
64            for bezier in beziers.iter() {
65                let x = bezier
66                    .compute_lookup_table(Some(DEFAULT_RESOLUTION), Some(TValueType::Euclidean));
67                for p in x {
68                    if !res.contains(&[p.x, p.y]) {
69                        res.push([p.x, p.y]);
70                    }
71                }
72            }
73            point_lists.push(res);
74        }
75        let mut max = f64::MIN;
76        for point_list in &point_lists {
77            for point in point_list {
78                if point[1] > max {
79                    max = point[1]
80                }
81            }
82        }
83        let point_lists: Vec<Vec<[f64; 2]>> = point_lists
84            .iter()
85            .map(|point_list| {
86                point_list
87                    .iter()
88                    .map(|point| [point[0] / max, point[1] / max])
89                    .collect()
90            })
91            .collect();
92        let mut polygons: Vec<Region> = vec![];
93        for mut region_points in point_lists {
94            region_points.reverse();
95            let region = Region::polygon(region_points, core);
96            if region.exterior().winding(&core.layers.geometry) == Winding::Cw {
97                let last_polygon = polygons.remove(0);
98                let new_region = last_polygon.add_interiors([region.exterior().clone()], core);
99                polygons.insert(0, new_region);
100            } else {
101                polygons.push(region);
102            }
103        }
104        polygons
105    }
106}