1use pathfinder_geometry::transform2d::{Matrix2x2F, Transform2F};
2use pathfinder_geometry::vector::Vector2F;
3
4use crate::error::ParseError;
5use crate::outline::{OutlineBuilder, OutlineSink};
6use crate::tables::glyf::{
7 CompositeGlyphComponent, CompositeGlyphScale, GlyfTable, Glyph, SimpleGlyph,
8 COMPOSITE_GLYPH_RECURSION_LIMIT,
9};
10
11use contour::{Contour, CurvePoint};
12
13impl<'a> GlyfTable<'a> {
14 fn visit_outline<S: OutlineSink>(
15 &mut self,
16 glyph_index: u16,
17 sink: &mut S,
18 offset: Vector2F,
19 scale: Option<CompositeGlyphScale>,
20 depth: u8,
21 ) -> Result<(), ParseError> {
22 if depth > COMPOSITE_GLYPH_RECURSION_LIMIT {
23 return Err(ParseError::LimitExceeded);
24 }
25
26 let glyph = self.get_parsed_glyph(glyph_index)?;
27 let scale = scale.map_or(Matrix2x2F::from_scale(1.0), Matrix2x2F::from);
28 let transform = Transform2F {
29 vector: offset,
30 matrix: scale,
31 };
32
33 match &glyph {
34 Glyph::Empty(_) => Ok(()),
35 Glyph::Simple(simple_glyph) => {
36 Self::visit_simple_glyph_outline(sink, transform, simple_glyph)
37 }
38 Glyph::Composite(composite) => {
39 let glyphs = composite.glyphs.clone();
42 self.visit_composite_glyph_outline(sink, &glyphs, depth)
43 }
44 }
45 }
46
47 fn visit_simple_glyph_outline<S: OutlineSink>(
48 sink: &mut S,
49 transform: Transform2F,
50 simple_glyph: &SimpleGlyph<'_>,
51 ) -> Result<(), ParseError> {
52 for points_and_flags in simple_glyph.contours() {
53 let contour = Contour::new(points_and_flags);
54
55 let origin = contour.origin();
57 sink.move_to(transform * origin);
58
59 let mut points = contour.points();
61 while let Some(next) = points.next() {
63 match next {
64 CurvePoint::OnCurve(to) => {
65 sink.line_to(transform * to);
66 }
67 CurvePoint::Control(control) => {
68 match points.next() {
69 Some(CurvePoint::OnCurve(to)) => {
70 sink.quadratic_curve_to(transform * control, transform * to);
71 }
72 Some(CurvePoint::Control(_)) => {
73 unreachable!("consecutive control points")
76 }
77 None => {
78 sink.quadratic_curve_to(transform * control, transform * origin);
80 break;
81 }
82 }
83 }
84 }
85 }
86
87 sink.close();
88 }
89
90 Ok(())
91 }
92
93 fn visit_composite_glyph_outline<S: OutlineSink>(
94 &mut self,
95 sink: &mut S,
96 glyphs: &[CompositeGlyphComponent],
97 depth: u8,
98 ) -> Result<(), ParseError> {
99 for composite_glyph in glyphs {
100 let offset = if composite_glyph.flags.args_are_xy_values() {
109 Vector2F::new(
111 i32::from(composite_glyph.argument1) as f32,
112 i32::from(composite_glyph.argument2) as f32,
113 )
114 } else {
115 Vector2F::zero()
117 };
118
119 self.visit_outline(
120 composite_glyph.glyph_index,
121 sink,
122 offset,
123 composite_glyph.scale,
124 depth + 1,
125 )?;
126 }
127
128 Ok(())
129 }
130}
131
132impl<'a> OutlineBuilder for GlyfTable<'a> {
133 type Error = ParseError;
134
135 fn visit<V: OutlineSink>(
136 &mut self,
137 glyph_index: u16,
138 visitor: &mut V,
139 ) -> Result<(), Self::Error> {
140 self.visit_outline(glyph_index, visitor, Vector2F::new(0., 0.), None, 0)
141 }
142}
143
144mod contour {
145 use crate::tables::glyf::{Point, SimpleGlyphFlag};
146 use pathfinder_geometry::vector::Vector2F;
147
148 pub struct Contour<'points> {
149 points_and_flags: &'points [(SimpleGlyphFlag, Point)],
150 }
151
152 #[derive(Debug, PartialEq)]
153 pub enum CurvePoint {
154 OnCurve(Vector2F),
155 Control(Vector2F),
156 }
157
158 pub struct Points<'a, 'points> {
159 contour: &'a Contour<'points>,
160 i: usize,
161 until: usize,
162 mid: Option<Vector2F>,
163 }
164
165 impl<'points> Contour<'points> {
166 pub fn new(points_and_flags: &'points [(SimpleGlyphFlag, Point)]) -> Self {
167 assert!(points_and_flags.len() > 0);
168 Contour { points_and_flags }
169 }
170
171 pub fn origin(&self) -> Vector2F {
172 self.calculate_origin().0
173 }
174
175 pub fn calculate_origin(&self) -> (Vector2F, usize, usize) {
176 match (self.first(), self.last()) {
177 (CurvePoint::OnCurve(first), _) => {
178 (first, 1, self.len())
180 }
181 (CurvePoint::Control(_), CurvePoint::OnCurve(last)) => {
182 (last, 0, self.len() - 1) }
186 (CurvePoint::Control(first), CurvePoint::Control(last)) => {
187 (first.lerp(last, 0.5), 0, self.len())
190 }
191 }
192 }
193
194 pub fn points<'a>(&'a self) -> Points<'a, 'points> {
195 let (_, start, until) = self.calculate_origin();
196 Points {
197 contour: self,
198 i: start,
199 until,
200 mid: None,
201 }
202 }
203
204 pub fn first(&self) -> CurvePoint {
205 self.get(0)
206 }
207
208 pub fn last(&self) -> CurvePoint {
209 self.get(self.points_and_flags.len() - 1)
210 }
211
212 pub fn len(&self) -> usize {
213 self.points_and_flags.len()
214 }
215
216 fn get(&self, index: usize) -> CurvePoint {
217 let (flags, point) = self.points_and_flags[index];
218 CurvePoint::new(point, flags.is_on_curve())
219 }
220 }
221
222 impl<'a, 'points> Iterator for Points<'a, 'points> {
223 type Item = CurvePoint;
224
225 fn next(&mut self) -> Option<Self::Item> {
226 if let Some(mid) = self.mid {
227 self.mid = None;
228 return Some(CurvePoint::OnCurve(mid));
229 }
230
231 if self.i >= self.until {
232 return None;
233 }
234
235 let point = match self.contour.get(self.i) {
236 point @ CurvePoint::OnCurve(_) => point,
237 CurvePoint::Control(control) => {
238 match self.contour.get((self.i + 1) % self.contour.len()) {
240 CurvePoint::OnCurve(_) => CurvePoint::Control(control),
241 CurvePoint::Control(control2) => {
242 self.mid = Some(control.lerp(control2, 0.5));
245 CurvePoint::Control(control)
246 }
247 }
248 }
249 };
250
251 self.i += 1;
252 Some(point)
253 }
254 }
255
256 impl CurvePoint {
257 fn new(point: Point, on_curve: bool) -> Self {
258 if on_curve {
259 CurvePoint::OnCurve(Vector2F::from(point))
260 } else {
261 CurvePoint::Control(Vector2F::from(point))
262 }
263 }
264 }
265}
266
267#[cfg(test)]
268mod tests {
269 use pathfinder_geometry::line_segment::LineSegment2F;
270 use pathfinder_geometry::vector::vec2f;
271
272 use crate::tables::glyf::tests::{composite_glyph_fixture, simple_glyph_fixture};
273 use crate::tables::glyf::{GlyfRecord, Point, SimpleGlyphFlag};
274
275 use super::*;
276
277 struct TestVisitor {}
278
279 impl OutlineSink for TestVisitor {
280 fn move_to(&mut self, to: Vector2F) {
281 println!("move_to({}, {})", to.x(), to.y());
282 }
283
284 fn line_to(&mut self, to: Vector2F) {
285 println!("line_to({}, {})", to.x(), to.y());
286 }
287
288 fn quadratic_curve_to(&mut self, control: Vector2F, to: Vector2F) {
289 println!(
290 "quad_to({}, {}, {}, {})",
291 control.x(),
292 control.y(),
293 to.x(),
294 to.y()
295 );
296 }
297
298 fn cubic_curve_to(&mut self, control: LineSegment2F, to: Vector2F) {
299 println!(
300 "curve_to({}, {}, {}, {}, {}, {})",
301 control.from_x(),
302 control.from_y(),
303 control.to_x(),
304 control.to_y(),
305 to.x(),
306 to.y()
307 );
308 }
309
310 fn close(&mut self) {
311 println!("close()");
312 }
313 }
314
315 #[test]
316 fn iter_simple_glyph_contours() {
317 let simple_glyph = simple_glyph_fixture();
318 let contours = simple_glyph
319 .contours()
320 .map(|contour| contour.iter().map(|(_, point)| *point).collect::<Vec<_>>())
321 .collect::<Vec<_>>();
322 let expected = &[&[
323 Point(433, 77),
324 Point(499, 30),
325 Point(625, 2),
326 Point(756, -27),
327 Point(915, -31),
328 Point(891, -47),
329 Point(862, -60),
330 Point(832, -73),
331 Point(819, -103),
332 ]];
333 assert_eq!(&contours, expected);
334 }
335
336 #[test]
337 fn iter_points() {
338 let points_and_flags = &[
339 (SimpleGlyphFlag::ON_CURVE_POINT, Point::zero()),
340 (SimpleGlyphFlag::empty(), Point(10, 40)), (SimpleGlyphFlag::empty(), Point(30, 40)), (SimpleGlyphFlag::ON_CURVE_POINT, Point(40, 10)),
343 ];
344 let contour = Contour::new(points_and_flags);
345 let points = contour.points().collect::<Vec<_>>();
346 let expected = &[
347 CurvePoint::Control(vec2f(10., 40.)),
348 CurvePoint::OnCurve(vec2f(20., 40.)), CurvePoint::Control(vec2f(30., 40.)),
350 CurvePoint::OnCurve(vec2f(40., 10.)),
351 ];
352 assert_eq!(contour.origin(), vec2f(0., 0.));
353 assert_eq!(&points, expected);
354 }
355
356 #[test]
357 fn outlines() {
358 let mut glyf = GlyfTable {
359 records: vec![
360 GlyfRecord::Parsed(Glyph::Simple(simple_glyph_fixture())),
361 GlyfRecord::Parsed(Glyph::Composite(composite_glyph_fixture(&[]))),
362 GlyfRecord::Parsed(Glyph::Simple(simple_glyph_fixture())),
363 GlyfRecord::Parsed(Glyph::Simple(simple_glyph_fixture())),
364 GlyfRecord::Parsed(Glyph::Simple(simple_glyph_fixture())),
365 GlyfRecord::Parsed(Glyph::Simple(simple_glyph_fixture())),
366 ],
367 };
368 let mut visitor = TestVisitor {};
369 glyf.visit(1, &mut visitor)
370 .expect("error visiting glyph outline");
371 }
372}