1#[derive(Debug, Default, Clone, Copy, PartialEq)]
5pub struct BBox {
6 pub x_min: f32,
8 pub y_min: f32,
10 pub x_max: f32,
12 pub y_max: f32,
14}
15
16impl BBox {
17 #[inline]
19 #[must_use = "Use the width value"]
20 pub fn width(&self) -> f32 {
21 self.x_max - self.x_min
22 }
23
24 #[inline]
26 #[must_use = "Use the height value"]
27 pub fn height(&self) -> f32 {
28 self.y_max - self.y_min
29 }
30
31 #[inline]
33 pub fn extend_by(&mut self, x: f32, y: f32) {
34 self.x_min = self.x_min.min(x);
35 self.y_min = self.y_min.min(y);
36 self.x_max = self.x_max.max(x);
37 self.y_max = self.y_max.max(y);
38 }
39}
40
41#[derive(Debug, Clone)]
43pub struct Outline {
44 bbox: std::cell::Cell<Option<BBox>>,
46 cff: bool,
48 contours: Vec<Contour>,
49}
50
51impl Outline {
52 #[must_use]
54 pub fn new(face: &ttf_parser::Face, glyph_id: ttf_parser::GlyphId) -> Option<Self> {
55 let mut outline = Outline {
56 bbox: std::cell::Cell::new(None),
57 cff: face.tables().cff.is_some()
59 || face.tables().cff2.is_some(),
60 contours: Vec::new(),
61 };
62 let mut outline_builder = OutlineBuilder::new(&mut outline);
63 let _ = face.outline_glyph(glyph_id, &mut outline_builder)?;
64 Some(outline)
65 }
66
67 pub fn bbox(&self) -> BBox {
69 if let Some(bbox) = self.bbox.get() {
70 bbox
71 } else {
72 let mut bbox = BBox::default();
73 for (i, p) in self.contours.iter().flat_map(|c| &c.points).enumerate() {
74 if i == 0 {
75 bbox.x_min = p.x;
76 bbox.y_min = p.y;
77 bbox.x_max = p.x;
78 bbox.y_max = p.y;
79 } else {
80 bbox.extend_by(p.x, p.y);
81 }
82 }
83
84 self.bbox.set(Some(bbox));
85 bbox
86 }
87 }
88
89 pub fn embolden(&mut self, strength: f32) {
91 self.bbox.set(None);
92 for c in &mut self.contours {
93 let num_points = c.points.len();
94 if num_points == 0 {
95 continue;
96 }
97
98 let closed = num_points > 1 && c.points.last() == c.points.first();
99 let last = if closed {
100 num_points - 2
101 } else {
102 num_points - 1
103 };
104
105 let mut in_pt = Point::default();
106 let mut in_len = 0f32;
107
108 let mut anchor_pt = Point::default();
109 let mut anchor_len = 0f32;
110
111 let mut i = last;
112 let mut j = 0;
113 let mut k: Option<usize> = None;
114 while i != j && Some(i) != k {
115 let (out_pt, out_len) = if Some(j) != k {
116 let x = c.points[j].x - c.points[i].x;
117 let y = c.points[j].y - c.points[i].y;
118 let len = (x * x + y * y).sqrt();
119 if len != 0.0 {
120 (Point::new(x / len, y / len), len)
121 } else {
122 j = if j < last { j + 1 } else { 0 };
123 continue;
124 }
125 } else {
126 (anchor_pt, anchor_len)
127 };
128
129 if in_len != 0.0 {
130 if k.is_none() {
131 k = Some(i);
132 anchor_pt = in_pt;
133 anchor_len = in_len;
134 }
135
136 let d = (in_pt.x * out_pt.x) + (in_pt.y * out_pt.y);
137 let shift_pt = if d > -0.9375 {
138 let d = d + 1.0;
139 let mut q = out_pt.x * in_pt.y - out_pt.y * in_pt.x;
140 if !self.cff {
141 q = -q;
142 }
143
144 let len = in_len.min(out_len);
145 let (x, y) = if self.cff {
146 (in_pt.y + out_pt.y, -(in_pt.x + out_pt.x))
147 } else {
148 (-(in_pt.y + out_pt.y), in_pt.x + out_pt.x)
149 };
150 if (strength * q) <= (len * d) {
151 Point::new(x * strength / d, y * strength / d)
152 } else {
153 Point::new(x * len / q, y * len / q)
154 }
155 } else {
156 Point::default()
157 };
158
159 while i != j {
160 let pt = &mut c.points[i];
161 pt.x += strength + shift_pt.x;
162 pt.y += strength + shift_pt.y;
163 i = if i < last { i + 1 } else { 0 };
164 }
165 } else {
166 i = j;
167 }
168
169 in_pt = out_pt;
170 in_len = out_len;
171 j = if j < last { j + 1 } else { 0 };
172 }
173
174 if closed {
175 let first = &c.points[0];
176 c.points[num_points - 1] = *first;
177 }
178 }
179 }
180
181 pub fn oblique(&mut self, x_skew: f32) {
183 self.bbox.set(None);
184 for c in &mut self.contours {
185 for p in &mut c.points {
186 if p.y != 0.0 {
187 p.x += p.y * x_skew;
188 }
189 }
190 }
191 }
192
193 pub fn emit(&self, builder: &mut dyn ttf_parser::OutlineBuilder) {
195 let mut points = self.contours.iter().flat_map(|c| &c.points);
196 for v in self.contours.iter().flat_map(|c| &c.verbs) {
197 match v {
198 PathVerb::MoveTo => {
199 let p = points.next().unwrap();
200 builder.move_to(p.x, p.y);
201 }
202 PathVerb::LineTo => {
203 let p = points.next().unwrap();
204 builder.line_to(p.x, p.y);
205 }
206 PathVerb::QuadTo => {
207 let p1 = points.next().unwrap();
208 let p = points.next().unwrap();
209 builder.quad_to(p1.x, p1.y, p.x, p.y);
210 }
211 PathVerb::CurveTo => {
212 let p1 = points.next().unwrap();
213 let p2 = points.next().unwrap();
214 let p = points.next().unwrap();
215 builder.curve_to(p1.x, p1.y, p2.x, p2.y, p.x, p.y);
216 }
217 PathVerb::Close => {
218 builder.close();
219 }
220 }
221 }
222 }
223}
224
225#[derive(Debug, Default, Clone)]
226struct Contour {
227 verbs: Vec<PathVerb>,
228 points: Vec<Point>,
229}
230
231#[derive(Debug, Clone, Copy)]
232enum PathVerb {
233 MoveTo,
234 LineTo,
235 QuadTo,
236 CurveTo,
237 Close,
238}
239
240#[derive(Debug, Default, Clone, Copy, PartialEq)]
241struct Point {
242 x: f32,
243 y: f32,
244}
245
246impl Point {
247 #[inline]
248 const fn new(x: f32, y: f32) -> Self {
249 Self { x, y }
250 }
251}
252
253struct OutlineBuilder<'a> {
254 outline: &'a mut Outline,
255 current_contour: usize,
256}
257
258impl<'a> OutlineBuilder<'a> {
259 #[inline]
260 fn new(outline: &'a mut Outline) -> Self {
261 Self {
262 outline,
263 current_contour: 1,
264 }
265 }
266
267 #[inline]
268 fn current_contour(&mut self) -> &mut Contour {
269 if self.current_contour > self.outline.contours.len() {
270 self.outline.contours.push(Contour::default());
271 }
272
273 &mut self.outline.contours[self.current_contour - 1]
274 }
275}
276
277impl ttf_parser::OutlineBuilder for OutlineBuilder<'_> {
278 fn move_to(&mut self, x: f32, y: f32) {
279 let c = self.current_contour();
280 c.verbs.push(PathVerb::MoveTo);
281 c.points.push(Point::new(x, y));
282 }
283
284 fn line_to(&mut self, x: f32, y: f32) {
285 let c = self.current_contour();
286 c.verbs.push(PathVerb::LineTo);
287 c.points.push(Point::new(x, y));
288 }
289
290 fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
291 let c = self.current_contour();
292 c.verbs.push(PathVerb::QuadTo);
293 c.points.push(Point::new(x1, y1));
294 c.points.push(Point::new(x, y));
295 }
296
297 fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32) {
298 let c = self.current_contour();
299 c.verbs.push(PathVerb::CurveTo);
300 c.points.push(Point::new(x1, y1));
301 c.points.push(Point::new(x2, y2));
302 c.points.push(Point::new(x, y));
303 }
304
305 fn close(&mut self) {
306 self.current_contour().verbs.push(PathVerb::Close);
307 self.current_contour += 1;
308 }
309}