1
use std::borrow::Cow;
2

            
3
use pathfinder_geometry::transform2d::{Matrix2x2F, Transform2F};
4
use pathfinder_geometry::vector::Vector2F;
5

            
6
use crate::binary::read::ReadScope;
7
use crate::error::ParseError;
8
use crate::outline::{OutlineBuilder, OutlineSink};
9
use crate::tables::os2::Os2;
10
use crate::tables::variable_fonts::gvar::GvarTable;
11
use crate::tables::variable_fonts::OwnedTuple;
12
use crate::tables::{FontTableProvider, HheaTable, HmtxTable, MaxpTable};
13
use crate::tag;
14

            
15
use super::{
16
    CompositeGlyphComponent, CompositeGlyphFlagExt, CompositeGlyphScale, Glyph, LocaGlyf,
17
    SimpleGlyph, COMPOSITE_GLYPH_RECURSION_LIMIT,
18
};
19

            
20
use contour::{Contour, CurvePoint};
21

            
22
/// Context for visiting possibly variable outlines of glyphs from the `glyf` table
23
///
24
/// ### Example
25
///
26
/// ```
27
/// use std::fmt::Write;
28
///
29
/// use allsorts::binary::read::ReadScope;
30
/// use allsorts::error::ParseError;
31
/// use allsorts::outline::OutlineBuilder;
32
/// use allsorts::outline::OutlineSink;
33
/// use allsorts::tables::glyf::GlyfVisitorContext;
34
/// use allsorts::tables::glyf::LocaGlyf;
35
/// use allsorts::tables::glyf::VariableGlyfContext;
36
/// use allsorts::tables::glyf::VariableGlyfContextStore;
37
/// use allsorts::tables::variable_fonts::avar::AvarTable;
38
/// use allsorts::tables::variable_fonts::fvar::FvarTable;
39
/// use allsorts::tables::Fixed;
40
/// use allsorts::tables::FontTableProvider;
41
/// use allsorts::tables::OpenTypeFont;
42
/// use allsorts::tag;
43
/// use pathfinder_geometry::line_segment::LineSegment2F;
44
/// use pathfinder_geometry::vector::Vector2F;
45
///
46
/// struct DebugVisitor {
47
///     outlines: String,
48
/// }
49
///
50
/// impl OutlineSink for DebugVisitor {
51
///     fn move_to(&mut self, to: Vector2F) {
52
///         writeln!(&mut self.outlines, "move_to({}, {})", to.x(), to.y()).unwrap();
53
///     }
54
///
55
///     fn line_to(&mut self, to: Vector2F) {
56
///         writeln!(&mut self.outlines, "line_to({}, {})", to.x(), to.y()).unwrap();
57
///     }
58
///
59
///     fn quadratic_curve_to(&mut self, control: Vector2F, to: Vector2F) {
60
///         writeln!(
61
///             &mut self.outlines,
62
///             "quad_to({}, {}, {}, {})",
63
///             control.x(),
64
///             control.y(),
65
///             to.x(),
66
///             to.y()
67
///         )
68
///         .unwrap();
69
///     }
70
///
71
///     fn cubic_curve_to(&mut self, control: LineSegment2F, to: Vector2F) {
72
///         writeln!(
73
///             &mut self.outlines,
74
///             "curve_to({}, {}, {}, {}, {}, {})",
75
///             control.from_x(),
76
///             control.from_y(),
77
///             control.to_x(),
78
///             control.to_y(),
79
///             to.x(),
80
///             to.y()
81
///         )
82
///         .unwrap();
83
///     }
84
///
85
///     fn close(&mut self) {
86
///         writeln!(&mut self.outlines, "close()").unwrap();
87
///     }
88
/// }
89
///
90
/// fn main() -> Result<(), Box<dyn std::error::Error>> {
91
///     let buffer = std::fs::read("tests/fonts/variable/Inter[slnt,wght].abc.ttf")?;
92
///     let scope = ReadScope::new(&buffer);
93
///     let font_file = scope.read::<OpenTypeFont<'_>>()?;
94
///     let provider = font_file.table_provider(0)?;
95
///
96
///     // Load the tables
97
///     let fvar_data = provider.read_table_data(tag::FVAR)?;
98
///     let fvar = ReadScope::new(&fvar_data).read::<FvarTable<'_>>().unwrap();
99
///     let avar_data = provider.table_data(tag::AVAR)?;
100
///     let avar = avar_data
101
///         .as_ref()
102
///         .map(|avar_data| ReadScope::new(avar_data).read::<AvarTable<'_>>())
103
///         .transpose()?;
104
///     let mut loca_glyf = LocaGlyf::load(&provider)?;
105
///     let store = VariableGlyfContextStore::read(&provider)?;
106
///
107
///     // Get the variation tuple
108
///     //   Subfamily: ExtraBold Italic
109
///     // Coordinates: [800.0, -10.0]
110
///     let user_tuple = [Fixed::from(800), Fixed::from(-10)];
111
///     let tuple = fvar.normalize(user_tuple.iter().copied(), avar.as_ref())?;
112
///
113
///     // Visit the outlines
114
///     let var_context = VariableGlyfContext::new(&store)?;
115
///     let mut context = GlyfVisitorContext::new(&mut loca_glyf, Some(var_context));
116
///     let mut visitor = DebugVisitor {
117
///         outlines: String::new(),
118
///     };
119
///
120
///     context
121
///         .visit(1, Some(&tuple), &mut visitor)
122
///         .expect("error visiting glyph outline");
123
///
124
///     let expected = "move_to(456, -26)
125
/// quad_to(310, -26, 204.5, 23.5)
126
/// quad_to(99, 73, 50.5, 174)
127
/// quad_to(2, 275, 27, 426)
128
/// quad_to(49, 554, 108.5, 641.5)
129
/// quad_to(168, 729, 255.5, 784)
130
/// quad_to(343, 839, 451, 868)
131
/// quad_to(559, 897, 677, 907)
132
/// quad_to(809, 919, 891.5, 933)
133
/// quad_to(974, 947, 1015, 972.5)
134
/// quad_to(1056, 998, 1065, 1044)
135
/// line_to(1065, 1049)
136
/// quad_to(1077, 1126, 1032, 1168)
137
/// quad_to(987, 1210, 897, 1210)
138
/// quad_to(801, 1210, 733, 1168)
139
/// quad_to(665, 1126, 638, 1052)
140
/// line_to(190, 1068)
141
/// quad_to(232, 1208, 334, 1318.5)
142
/// quad_to(436, 1429, 593.5, 1492.5)
143
/// quad_to(751, 1556, 958, 1556)
144
/// quad_to(1104, 1556, 1222, 1521.5)
145
/// quad_to(1340, 1487, 1421.5, 1421.5)
146
/// quad_to(1503, 1356, 1538, 1261)
147
/// quad_to(1573, 1166, 1553, 1044)
148
/// line_to(1378, 0)
149
/// line_to(918, 0)
150
/// line_to(954, 214)
151
/// line_to(942, 214)
152
/// quad_to(888, 136, 815.5, 82)
153
/// quad_to(743, 28, 653, 1)
154
/// quad_to(563, -26, 456, -26)
155
/// close()
156
/// move_to(662, 294)
157
/// quad_to(739, 294, 808, 326)
158
/// quad_to(877, 358, 924.5, 414.5)
159
/// quad_to(972, 471, 985, 546)
160
/// line_to(1008, 692)
161
/// quad_to(987, 681, 955, 672)
162
/// quad_to(923, 663, 887, 655.5)
163
/// quad_to(851, 648, 813.5, 641.5)
164
/// quad_to(776, 635, 742, 630)
165
/// quad_to(670, 619, 617, 596)
166
/// quad_to(564, 573, 532.5, 536.5)
167
/// quad_to(501, 500, 494, 450)
168
/// quad_to(482, 375, 529, 334.5)
169
/// quad_to(576, 294, 662, 294)
170
/// close()
171
/// ";
172
///     assert_eq!(visitor.outlines, expected);
173
///
174
///     Ok(())
175
/// }
176
/// ```
177
pub struct GlyfVisitorContext<'a, 'data> {
178
    glyf: &'a mut LocaGlyf,
179
    variable: Option<VariableGlyfContext<'data>>,
180
}
181

            
182
/// Tables required to visit variable glyphs
183
pub struct VariableGlyfContext<'data> {
184
    /// [gvar][crate::tables::variable_fonts::gvar::GvarTable] table
185
    gvar: GvarTable<'data>,
186
    /// [hmtx][crate::tables::HmtxTable] table
187
    hmtx: HmtxTable<'data>,
188
    /// [vmtx][crate::tables::HmtxTable] table
189
    vmtx: Option<HmtxTable<'data>>,
190
    /// [OS/2][crate::tables::os2::Os2] table
191
    os2: Os2,
192
    /// [hhea][crate::tables::HheaTable] table
193
    hhea: HheaTable,
194
}
195

            
196
/// Holds data for tables required to visit variable glyphs
197
///
198
/// This type is used in conjunction with [VariableGlyfContext]. It exists to hold the data
199
/// parsed and held by the context. In an ideal world this data could be held by the context
200
/// itself, but this required self-referencing types, which are annoying.
201
pub struct VariableGlyfContextStore<'a> {
202
    maxp: Cow<'a, [u8]>,
203
    gvar: Cow<'a, [u8]>,
204
    hhea: Cow<'a, [u8]>,
205
    hmtx: Cow<'a, [u8]>,
206
    vhea: Option<Cow<'a, [u8]>>,
207
    vmtx: Option<Cow<'a, [u8]>>,
208
    os2: Cow<'a, [u8]>,
209
}
210

            
211
#[derive(Copy, Clone)]
212
struct GlyfVisitorState {
213
    offset: Vector2F,
214
    scale: Option<CompositeGlyphScale>,
215
    depth: u8,
216
}
217

            
218
impl GlyfVisitorState {
219
71118
    fn new() -> Self {
220
71118
        GlyfVisitorState {
221
71118
            offset: Vector2F::zero(),
222
71118
            scale: None,
223
71118
            depth: 0,
224
71118
        }
225
71118
    }
226

            
227
65880
    fn transform(&self) -> Transform2F {
228
65880
        let scale = self
229
65880
            .scale
230
65880
            .map_or_else(|| Matrix2x2F::from_scale(1.0), Matrix2x2F::from);
231
65880
        Transform2F {
232
65880
            vector: self.offset,
233
65880
            matrix: scale,
234
65880
        }
235
65880
    }
236
}
237

            
238
impl<'a, 'data> GlyfVisitorContext<'a, 'data> {
239
    /// Construct a new context for visiting glyphs
240
    ///
241
    /// To apply variation to visited glyphs a [VariableGlyfContext] must be supplied along with
242
    /// a tuple when calling [visit][Self::visit].
243
71118
    pub fn new(glyf: &'a mut LocaGlyf, variable: Option<VariableGlyfContext<'data>>) -> Self {
244
71118
        GlyfVisitorContext { glyf, variable }
245
71118
    }
246

            
247
69920
    fn visit_outline<S: OutlineSink>(
248
69920
        &mut self,
249
69920
        glyph_index: u16,
250
69920
        tuple: Option<&OwnedTuple>,
251
69920
        state: GlyfVisitorState,
252
69920
        sink: &mut S,
253
69920
    ) -> Result<(), ParseError> {
254
69920
        if state.depth > COMPOSITE_GLYPH_RECURSION_LIMIT {
255
            return Err(ParseError::LimitExceeded);
256
69920
        }
257

            
258
69920
        let glyph = self.glyf.glyph(glyph_index)?;
259
69920
        let glyph = match (&self.variable, tuple) {
260
            (Some(var), Some(tuple)) => {
261
                // Get a copy of the glyph that can be mutated in order to apply the variations
262
                let mut glyph = Glyph::clone(&glyph);
263
                glyph.apply_variations(
264
                    glyph_index,
265
                    tuple,
266
                    &var.gvar,
267
                    &var.hmtx,
268
                    var.vmtx.as_ref(),
269
                    Some(&var.os2),
270
                    &var.hhea,
271
                )?;
272
                Cow::Owned(glyph)
273
            }
274
69920
            _ => Cow::Borrowed(&*glyph),
275
        };
276

            
277
69920
        match &*glyph {
278
5619
            Glyph::Empty(_) => Ok(()),
279
64036
            Glyph::Simple(simple_glyph) => {
280
64036
                visit_simple_glyph_outline(sink, state.transform(), simple_glyph)
281
            }
282
265
            Glyph::Composite(composite) => {
283
265
                self.visit_composite_glyph_outline(sink, &composite.glyphs, tuple, state.depth)
284
            }
285
        }
286
69920
    }
287

            
288
265
    fn visit_composite_glyph_outline<S: OutlineSink>(
289
265
        &mut self,
290
265
        sink: &mut S,
291
265
        glyphs: &[CompositeGlyphComponent],
292
265
        tuple: Option<&OwnedTuple>,
293
265
        depth: u8,
294
265
    ) -> Result<(), ParseError> {
295
1060
        for composite_glyph in glyphs {
296
            // Argument1 and argument2 can be either x and y offsets to be added to the glyph (the
297
            // ARGS_ARE_XY_VALUES flag is set), or two point numbers (the ARGS_ARE_XY_VALUES flag
298
            // is not set). In the latter case, the first point number indicates the point that is
299
            // to be matched to the new glyph. The second number indicates the new glyph’s
300
            // “matched” point. Once a glyph is added, its point numbers begin directly after the
301
            // last glyphs (endpoint of first glyph + 1).
302
            //
303
            // https://docs.microsoft.com/en-us/typography/opentype/spec/glyf#composite-glyph-description
304
795
            let offset = if composite_glyph.flags.args_are_xy_values() {
305
                // NOTE: Casts are safe as max value of composite glyph is u16::MAX
306
795
                Vector2F::new(
307
795
                    i32::from(composite_glyph.argument1) as f32,
308
795
                    i32::from(composite_glyph.argument2) as f32,
309
                )
310
            } else {
311
                // TODO: support args as point numbers
312
                Vector2F::zero()
313
            };
314
795
            let state = GlyfVisitorState {
315
795
                offset,
316
795
                scale: composite_glyph.scale,
317
795
                depth: depth + 1,
318
795
            };
319

            
320
795
            self.visit_outline(composite_glyph.glyph_index, tuple, state, sink)?;
321
        }
322

            
323
265
        Ok(())
324
265
    }
325
}
326

            
327
impl<'a> VariableGlyfContextStore<'a> {
328
    /// Read the required tables from the supplied [FontTableProvider]
329
    pub fn read<F: FontTableProvider>(provider: &'a F) -> Result<Self, ParseError> {
330
        let maxp = provider.read_table_data(tag::MAXP)?;
331
        let gvar = provider.read_table_data(tag::GVAR)?;
332
        let hhea = provider.read_table_data(tag::HHEA)?;
333
        let hmtx = provider.read_table_data(tag::HMTX)?;
334
        let vhea = provider.table_data(tag::VHEA)?;
335
        let vmtx = provider.table_data(tag::VMTX)?;
336
        let os2 = provider.read_table_data(tag::OS_2)?;
337

            
338
        Ok(VariableGlyfContextStore {
339
            maxp,
340
            gvar,
341
            hhea,
342
            hmtx,
343
            vhea,
344
            vmtx,
345
            os2,
346
        })
347
    }
348
}
349

            
350
impl<'data> VariableGlyfContext<'data> {
351
    /// Construct a new `VariableGlyfContext` from the supplied store
352
    ///
353
    /// The resulting instance can be passed to [GlyfVisitorContext::new] in order to visit the outlines
354
    /// of a variable font.
355
    pub fn new(store: &'data VariableGlyfContextStore<'data>) -> Result<Self, ParseError> {
356
        let maxp = ReadScope::new(&store.maxp).read::<MaxpTable>()?;
357
        let gvar = ReadScope::new(&store.gvar).read::<GvarTable<'data>>()?;
358
        let hhea = ReadScope::new(&store.hhea).read::<HheaTable>()?;
359
        let hmtx = ReadScope::new(&store.hmtx).read_dep::<HmtxTable<'_>>((
360
            usize::from(maxp.num_glyphs),
361
            usize::from(hhea.num_h_metrics),
362
        ))?;
363
        let vhea = store
364
            .vhea
365
            .as_ref()
366
            .map(|vhea_data| ReadScope::new(vhea_data).read::<HheaTable>())
367
            .transpose()?;
368
        let vmtx = vhea
369
            .and_then(|vhea| {
370
                store.vmtx.as_ref().map(|vmtx_data| {
371
                    ReadScope::new(vmtx_data).read_dep::<HmtxTable<'_>>((
372
                        usize::from(maxp.num_glyphs),
373
                        usize::from(vhea.num_h_metrics),
374
                    ))
375
                })
376
            })
377
            .transpose()?;
378

            
379
        let os2 = ReadScope::new(&store.os2).read_dep::<Os2>(store.os2.len())?;
380

            
381
        Ok(VariableGlyfContext {
382
            gvar,
383
            hmtx,
384
            vmtx,
385
            os2,
386
            hhea,
387
        })
388
    }
389
}
390

            
391
impl OutlineBuilder for GlyfVisitorContext<'_, '_> {
392
    type Error = ParseError;
393
    type Output = ();
394

            
395
69125
    fn visit<V: OutlineSink>(
396
69125
        &mut self,
397
69125
        glyph_index: u16,
398
69125
        tuple: Option<&OwnedTuple>,
399
69125
        visitor: &mut V,
400
69125
    ) -> Result<(), Self::Error> {
401
69125
        self.visit_outline(glyph_index, tuple, GlyfVisitorState::new(), visitor)
402
69125
    }
403
}
404

            
405
64036
fn visit_simple_glyph_outline<S: OutlineSink>(
406
64036
    sink: &mut S,
407
64036
    transform: Transform2F,
408
64036
    simple_glyph: &SimpleGlyph,
409
64036
) -> Result<(), ParseError> {
410
84925
    for points_and_flags in simple_glyph.contours() {
411
84925
        let contour = Contour::new(points_and_flags);
412

            
413
        // Determine origin of the contour and move to it
414
84925
        let origin = contour.origin();
415
84925
        sink.move_to(transform * origin);
416

            
417
        // Consume the stream of points...
418
84925
        let mut points = contour.points();
419
        // It's assumed that the current location is on curve each time through this loop
420
1367501
        while let Some(next) = points.next() {
421
1314064
            match next {
422
341561
                CurvePoint::OnCurve(to) => {
423
341561
                    sink.line_to(transform * to);
424
341561
                }
425
972503
                CurvePoint::Control(control) => {
426
972503
                    match points.next() {
427
941015
                        Some(CurvePoint::OnCurve(to)) => {
428
941015
                            sink.quadratic_curve_to(transform * control, transform * to);
429
941015
                        }
430
                        Some(CurvePoint::Control(_)) => {
431
                            // Can't happen as the Points iterator inserts on curve mid-points
432
                            // when two consecutive control points are encountered
433
                            unreachable!("consecutive control points")
434
                        }
435
                        None => {
436
                            // Wrap around to the first point
437
31488
                            sink.quadratic_curve_to(transform * control, transform * origin);
438
31488
                            break;
439
                        }
440
                    }
441
                }
442
            }
443
        }
444

            
445
84925
        sink.close();
446
    }
447

            
448
64036
    Ok(())
449
64036
}
450

            
451
mod contour {
452
    use crate::tables::glyf::{Point, SimpleGlyphFlagExt, SimpleGlyphFlags};
453
    use pathfinder_geometry::vector::Vector2F;
454

            
455
    pub struct Contour<'points> {
456
        points_and_flags: &'points [(SimpleGlyphFlags, Point)],
457
    }
458

            
459
    #[derive(Debug, PartialEq)]
460
    pub enum CurvePoint {
461
        OnCurve(Vector2F),
462
        Control(Vector2F),
463
    }
464

            
465
    pub struct Points<'a, 'points> {
466
        contour: &'a Contour<'points>,
467
        i: usize,
468
        until: usize,
469
        mid: Option<Vector2F>,
470
    }
471

            
472
    impl<'points> Contour<'points> {
473
87534
        pub fn new(points_and_flags: &'points [(SimpleGlyphFlags, Point)]) -> Self {
474
87534
            assert!(!points_and_flags.is_empty());
475
87534
            Contour { points_and_flags }
476
87534
        }
477

            
478
87534
        pub fn origin(&self) -> Vector2F {
479
87534
            self.calculate_origin().0
480
87534
        }
481

            
482
175068
        pub fn calculate_origin(&self) -> (Vector2F, usize, usize) {
483
175068
            match (self.first(), self.last()) {
484
175068
                (CurvePoint::OnCurve(first), _) => {
485
                    // Origin is the first point, so start on the second point
486
175068
                    (first, 1, self.len())
487
                }
488
                (CurvePoint::Control(_), CurvePoint::OnCurve(last)) => {
489
                    // Origin is the last point, so start on the first point and consider
490
                    // the last point already processed
491
                    (last, 0, self.len() - 1) // TODO: Test this
492
                }
493
                (CurvePoint::Control(first), CurvePoint::Control(last)) => {
494
                    // Origin is the mid-point between first and last control points.
495
                    // Start on the first point
496
                    (first.lerp(last, 0.5), 0, self.len())
497
                }
498
            }
499
175068
        }
500

            
501
87534
        pub fn points<'a>(&'a self) -> Points<'a, 'points> {
502
87534
            let (_, start, until) = self.calculate_origin();
503
87534
            Points {
504
87534
                contour: self,
505
87534
                i: start,
506
87534
                until,
507
87534
                mid: None,
508
87534
            }
509
87534
        }
510

            
511
175068
        pub fn first(&self) -> CurvePoint {
512
175068
            self.get(0)
513
175068
        }
514

            
515
175068
        pub fn last(&self) -> CurvePoint {
516
175068
            self.get(self.points_and_flags.len() - 1)
517
175068
        }
518

            
519
1171854
        pub fn len(&self) -> usize {
520
1171854
            self.points_and_flags.len()
521
1171854
        }
522

            
523
3013146
        fn get(&self, index: usize) -> CurvePoint {
524
3013146
            let (flags, point) = self.points_and_flags[index];
525
3013146
            CurvePoint::new(point, flags.is_on_curve())
526
3013146
        }
527
    }
528

            
529
    impl Iterator for Points<'_, '_> {
530
        type Item = CurvePoint;
531

            
532
2401056
        fn next(&mut self) -> Option<Self::Item> {
533
2401056
            if let Some(mid) = self.mid {
534
647298
                self.mid = None;
535
647298
                return Some(CurvePoint::OnCurve(mid));
536
1753758
            }
537

            
538
1753758
            if self.i >= self.until {
539
87534
                return None;
540
1666224
            }
541

            
542
1666224
            let point = match self.contour.get(self.i) {
543
669438
                point @ CurvePoint::OnCurve(_) => point,
544
996786
                CurvePoint::Control(control) => {
545
                    // Check the next point, wrapping around if needed
546
996786
                    match self.contour.get((self.i + 1) % self.contour.len()) {
547
349488
                        CurvePoint::OnCurve(_) => CurvePoint::Control(control),
548
647298
                        CurvePoint::Control(control2) => {
549
                            // Next point is a control point, yield mid point as on curve point
550
                            // after this one
551
647298
                            self.mid = Some(control.lerp(control2, 0.5));
552
647298
                            CurvePoint::Control(control)
553
                        }
554
                    }
555
                }
556
            };
557

            
558
1666224
            self.i += 1;
559
1666224
            Some(point)
560
2401056
        }
561
    }
562

            
563
    impl CurvePoint {
564
3013146
        fn new(point: Point, on_curve: bool) -> Self {
565
3013146
            if on_curve {
566
1304262
                CurvePoint::OnCurve(Vector2F::from(point))
567
            } else {
568
1708884
                CurvePoint::Control(Vector2F::from(point))
569
            }
570
3013146
        }
571
    }
572
}
573

            
574
#[cfg(test)]
575
mod tests {
576
    use pathfinder_geometry::line_segment::LineSegment2F;
577
    use pathfinder_geometry::vector::vec2f;
578

            
579
    use crate::binary::read::ReadScope;
580
    use crate::binary::write::{WriteBinaryDep, WriteBuffer};
581
    use crate::tables::glyf::tests::{composite_glyph_fixture, simple_glyph_fixture};
582
    use crate::tables::glyf::{GlyfRecord, GlyfTable, Point, SimpleGlyphFlag, SimpleGlyphFlags};
583
    use crate::tables::variable_fonts::avar::AvarTable;
584
    use crate::tables::variable_fonts::fvar::FvarTable;
585
    use crate::tables::{Fixed, IndexToLocFormat, OpenTypeFont};
586
    use crate::tests::read_fixture;
587

            
588
    use super::*;
589

            
590
    struct TestVisitor {}
591

            
592
    impl OutlineSink for TestVisitor {
593
        fn move_to(&mut self, to: Vector2F) {
594
            println!("move_to({}, {})", to.x(), to.y());
595
        }
596

            
597
        fn line_to(&mut self, to: Vector2F) {
598
            println!("line_to({}, {})", to.x(), to.y());
599
        }
600

            
601
        fn quadratic_curve_to(&mut self, control: Vector2F, to: Vector2F) {
602
            println!(
603
                "quad_to({}, {}, {}, {})",
604
                control.x(),
605
                control.y(),
606
                to.x(),
607
                to.y()
608
            );
609
        }
610

            
611
        fn cubic_curve_to(&mut self, control: LineSegment2F, to: Vector2F) {
612
            println!(
613
                "curve_to({}, {}, {}, {}, {}, {})",
614
                control.from_x(),
615
                control.from_y(),
616
                control.to_x(),
617
                control.to_y(),
618
                to.x(),
619
                to.y()
620
            );
621
        }
622

            
623
        fn close(&mut self) {
624
            println!("close()");
625
        }
626
    }
627

            
628
    #[test]
629
    fn iter_simple_glyph_contours() {
630
        let simple_glyph = simple_glyph_fixture();
631
        let contours = simple_glyph
632
            .contours()
633
            .map(|contour| contour.iter().map(|(_, point)| *point).collect::<Vec<_>>())
634
            .collect::<Vec<_>>();
635
        let expected = &[&[
636
            Point(433, 77),
637
            Point(499, 30),
638
            Point(625, 2),
639
            Point(756, -27),
640
            Point(915, -31),
641
            Point(891, -47),
642
            Point(862, -60),
643
            Point(832, -73),
644
            Point(819, -103),
645
        ]];
646
        assert_eq!(&contours, expected);
647
    }
648

            
649
    #[test]
650
    fn iter_points() {
651
        let points_and_flags: &[(SimpleGlyphFlags, Point)] = &[
652
            (SimpleGlyphFlag::ON_CURVE_POINT.into(), Point::zero()),
653
            (SimpleGlyphFlags::empty(), Point(10, 40)), // control
654
            (SimpleGlyphFlags::empty(), Point(30, 40)), // control
655
            (SimpleGlyphFlag::ON_CURVE_POINT.into(), Point(40, 10)),
656
        ];
657
        let contour = Contour::new(points_and_flags);
658
        let points = contour.points().collect::<Vec<_>>();
659
        let expected = &[
660
            CurvePoint::Control(vec2f(10., 40.)),
661
            CurvePoint::OnCurve(vec2f(20., 40.)), // mid point
662
            CurvePoint::Control(vec2f(30., 40.)),
663
            CurvePoint::OnCurve(vec2f(40., 10.)),
664
        ];
665
        assert_eq!(contour.origin(), vec2f(0., 0.));
666
        assert_eq!(&points, expected);
667
    }
668

            
669
    #[test]
670
    fn outlines() {
671
        let glyphs = GlyfTable {
672
            records: vec![
673
                GlyfRecord::Parsed(Glyph::Simple(simple_glyph_fixture())),
674
                GlyfRecord::Parsed(Glyph::Composite(composite_glyph_fixture(&[]))),
675
                GlyfRecord::Parsed(Glyph::Simple(simple_glyph_fixture())),
676
                GlyfRecord::Parsed(Glyph::Simple(simple_glyph_fixture())),
677
                GlyfRecord::Parsed(Glyph::Simple(simple_glyph_fixture())),
678
                GlyfRecord::Parsed(Glyph::Simple(simple_glyph_fixture())),
679
            ],
680
        };
681
        let mut buf = WriteBuffer::new();
682
        let loca = GlyfTable::write_dep(&mut buf, glyphs, IndexToLocFormat::Short)
683
            .expect("unable to write glyf table");
684
        let glyf = buf.into_inner().into_boxed_slice();
685
        let mut loca_glyf = LocaGlyf::loaded(loca, glyf);
686
        let mut visitor = TestVisitor {};
687
        let mut context = GlyfVisitorContext::new(&mut loca_glyf, None);
688
        context
689
            .visit(1, None, &mut visitor)
690
            .expect("error visiting glyph outline");
691
    }
692

            
693
    #[test]
694
    fn variable_outlines() -> Result<(), ParseError> {
695
        let buffer = read_fixture("tests/fonts/variable/Inter[slnt,wght].abc.ttf");
696
        let scope = ReadScope::new(&buffer);
697
        let font_file = scope.read::<OpenTypeFont<'_>>()?;
698
        let provider = font_file.table_provider(0)?;
699

            
700
        // Load the tables
701
        let fvar_data = provider.read_table_data(tag::FVAR)?;
702
        let fvar = ReadScope::new(&fvar_data).read::<FvarTable<'_>>().unwrap();
703
        let avar_data = provider.table_data(tag::AVAR)?;
704
        let avar = avar_data
705
            .as_ref()
706
            .map(|avar_data| ReadScope::new(avar_data).read::<AvarTable<'_>>())
707
            .transpose()?;
708
        let mut loca_glyf = LocaGlyf::load(&provider)?;
709
        let store = VariableGlyfContextStore::read(&provider)?;
710

            
711
        // Get the variation tuple
712
        //   Subfamily: ExtraBold Italic
713
        // Coordinates: [800.0, -10.0]
714
        let user_tuple = [Fixed::from(800), Fixed::from(-10)];
715
        let tuple = fvar.normalize(user_tuple.iter().copied(), avar.as_ref())?;
716

            
717
        // Visit the outlines
718
        let var_context = VariableGlyfContext::new(&store)?;
719
        let mut context = GlyfVisitorContext::new(&mut loca_glyf, Some(var_context));
720
        let mut visitor = TestVisitor {};
721

            
722
        context
723
            .visit(1, Some(&tuple), &mut visitor)
724
            .expect("error visiting glyph outline");
725

            
726
        Ok(())
727
    }
728
}