1
use std::collections::BTreeMap;
2
use std::ops::RangeInclusive;
3

            
4
use pathfinder_geometry::rect::RectF;
5
use pathfinder_geometry::transform2d::{Matrix2x2F, Transform2F};
6
use pathfinder_geometry::vector::{vec2f, Vector2F};
7

            
8
use crate::error::ParseError;
9
use crate::tables::glyf::{
10
    calculate_phantom_points, BoundingBox, ComponentOffsets, CompositeGlyph,
11
    CompositeGlyphArgument, CompositeGlyphComponent, CompositeGlyphFlag, CompositeGlyphFlagExt,
12
    EmptyGlyph, GlyfRecord, GlyfTable, Glyph, PhantomPoints, Point, SimpleGlyph, SimpleGlyphFlags,
13
};
14
use crate::tables::os2::Os2;
15
use crate::tables::variable_fonts::gvar::{GvarTable, NumPoints};
16
use crate::tables::variable_fonts::OwnedTuple;
17
use crate::tables::{HheaTable, HmtxTable};
18
use crate::SafeFrom;
19

            
20
impl<'a> Glyph {
21
    /// Apply glyph variation to the supplied glyph according to the variation
22
    /// instance `user_instance`.
23
    pub(crate) fn apply_variations(
24
        &mut self,
25
        glyph_index: u16,
26
        instance: &OwnedTuple,
27
        gvar: &GvarTable<'a>,
28
        hmtx: &HmtxTable<'a>,
29
        vmtx: Option<&HmtxTable<'a>>,
30
        os2: Option<&Os2>,
31
        hhea: &HheaTable,
32
    ) -> Result<(), ParseError> {
33
        let Some(deltas) = glyph_deltas(self, glyph_index, instance, gvar)? else {
34
            // The glyph has no deltas but we still need to populate the phantom points
35
            let phantom_points =
36
                calculate_phantom_points(glyph_index, self.bounding_box(), hmtx, vmtx, os2, hhea)?;
37
            self.set_phantom_points(phantom_points);
38
            return Ok(());
39
        };
40

            
41
        match self {
42
            Glyph::Empty(empty) => {
43
                let mut phantom_points =
44
                    calculate_phantom_points(glyph_index, None, hmtx, vmtx, os2, hhea)?;
45
                apply_phantom_point_deltas(&mut phantom_points, &deltas);
46
                empty.phantom_points = Some(phantom_points);
47
                Ok(())
48
            }
49
            Glyph::Simple(simple_glyph) => {
50
                // Calculate the phantom points before variations are applied
51
                let mut phantom_points = calculate_phantom_points(
52
                    glyph_index,
53
                    Some(simple_glyph.bounding_box),
54
                    hmtx,
55
                    vmtx,
56
                    os2,
57
                    hhea,
58
                )?;
59

            
60
                // Apply the deltas to the coordinates of the glyph and calculate the updated
61
                // bounding box as we go.
62
                let mut bbox = BoundingBox::empty();
63
                simple_glyph
64
                    .coordinates
65
                    .iter_mut()
66
                    .zip(deltas.iter().copied())
67
                    .enumerate()
68
                    .for_each(|(i, ((_flag, point), delta))| {
69
                        // NOTE(cast): Since Rust 1.45.0 floating point casts like these are
70
                        // saturating casts.
71
                        // https://blog.rust-lang.org/2020/07/16/Rust-1.45.0.html#fixing-unsoundness-in-casts
72
                        point.0 = (point.0 as f32 + delta.x()).round() as i16;
73
                        point.1 = (point.1 as f32 + delta.y()).round() as i16;
74
                        if i == 0 {
75
                            bbox = BoundingBox {
76
                                x_min: point.0,
77
                                x_max: point.0,
78
                                y_min: point.1,
79
                                y_max: point.1,
80
                            }
81
                        } else {
82
                            bbox.add(*point)
83
                        }
84
                    });
85
                simple_glyph.bounding_box = bbox;
86

            
87
                // Apply deltas to the phantom points of the glyph
88
                apply_phantom_point_deltas(
89
                    &mut phantom_points,
90
                    &deltas[simple_glyph.coordinates.len()..],
91
                );
92
                simple_glyph.phantom_points = Some(Box::new(phantom_points));
93

            
94
                // TODO: Update flag 1 in head?
95
                Ok(())
96
            }
97
            Glyph::Composite(composite) => {
98
                // Calculate the phantom points before variations are applied
99
                let mut phantom_points = calculate_phantom_points(
100
                    glyph_index,
101
                    Some(composite.bounding_box),
102
                    hmtx,
103
                    vmtx,
104
                    os2,
105
                    hhea,
106
                )?;
107

            
108
                // Use the deltas to reposition the sub-glyphs of the composite glyph
109
                composite
110
                    .glyphs
111
                    .iter_mut()
112
                    .zip(deltas.iter().copied())
113
                    .for_each(|(composite_glyph, delta)| {
114
                        add_composite_glyph_delta(composite_glyph, delta)
115
                    });
116

            
117
                // Apply deltas to phantom  points
118
                apply_phantom_point_deltas(&mut phantom_points, &deltas[composite.glyphs.len()..]);
119
                composite.phantom_points = Some(Box::new(phantom_points));
120

            
121
                Ok(())
122
            }
123
        }
124
    }
125

            
126
    /// Calculate the bounding box from the points of this glyph.
127
    ///
128
    /// For simple glyphs this just returns the bounding box of the glyph. For
129
    /// composite glyphs the sub-glyphs are traversed to calculate the
130
    /// bounding box that contains them all.
131
    pub(crate) fn calculate_bounding_box(&self, glyf: &GlyfTable<'a>) -> Result<RectF, ParseError> {
132
        match self {
133
            Glyph::Empty(glyph) => glyph.calculate_bounding_box(),
134
            Glyph::Simple(glyph) => glyph.calculate_bounding_box(),
135
            Glyph::Composite(glyph) => glyph.calculate_bounding_box(glyf),
136
        }
137
    }
138

            
139
    fn set_phantom_points(&mut self, phantom_points: [Point; 4]) {
140
        match self {
141
            Glyph::Empty(empty) => empty.phantom_points = Some(phantom_points),
142
            Glyph::Simple(simple) => simple.phantom_points = Some(Box::new(phantom_points)),
143
            Glyph::Composite(composite) => {
144
                composite.phantom_points = Some(Box::new(phantom_points))
145
            }
146
        }
147
    }
148
}
149

            
150
fn apply_phantom_point_deltas(phantom_points: &mut PhantomPoints, deltas: &[Vector2F]) {
151
    phantom_points
152
        .iter_mut()
153
        .zip(deltas.iter().copied())
154
        .for_each(|(point, delta)| {
155
            // NOTE(cast): saturating
156
            point.0 = (point.0 as f32 + delta.x()).round() as i16;
157
            point.1 = (point.1 as f32 + delta.y()).round() as i16;
158
        });
159
}
160

            
161
impl EmptyGlyph {
162
    pub(crate) fn calculate_bounding_box(&self) -> Result<RectF, ParseError> {
163
        Ok(RectF::default())
164
    }
165
}
166

            
167
impl SimpleGlyph {
168
    pub(crate) fn calculate_bounding_box(&self) -> Result<RectF, ParseError> {
169
        Ok(RectF::from_points(
170
            vec2f(
171
                self.bounding_box.x_min as f32,
172
                self.bounding_box.y_min as f32,
173
            ),
174
            vec2f(
175
                self.bounding_box.x_max as f32,
176
                self.bounding_box.y_max as f32,
177
            ),
178
        ))
179
    }
180
}
181

            
182
impl CompositeGlyph {
183
    pub(crate) fn calculate_bounding_box(&self, glyf: &GlyfTable<'_>) -> Result<RectF, ParseError> {
184
        let mut bbox: Option<RectF> = None;
185
        for child in &self.glyphs {
186
            let record: &GlyfRecord<'_> = glyf
187
                .records
188
                .get(usize::from(child.glyph_index))
189
                .ok_or(ParseError::BadIndex)?;
190
            let GlyfRecord::Parsed(child_glyph) = record else {
191
                panic!("glyph is not parsed");
192
            };
193
            let mut child_bbox = child_glyph.calculate_bounding_box(glyf)?;
194

            
195
            // Scale the bbox
196
            let offset = Vector2F::new(
197
                i32::from(child.argument1) as f32,
198
                i32::from(child.argument2) as f32,
199
            );
200
            match child.scale {
201
                Some(scale) => {
202
                    let scale = Matrix2x2F::from(scale);
203
                    match child.flags.component_offsets() {
204
                        // translate, then scale
205
                        ComponentOffsets::Scaled => {
206
                            let transform = Transform2F {
207
                                matrix: scale,
208
                                vector: Vector2F::zero(),
209
                            };
210
                            child_bbox = transform * (child_bbox + offset);
211
                        }
212
                        // scale, then translate - this the default for Transform2F
213
                        ComponentOffsets::Unscaled => {
214
                            let transform = Transform2F {
215
                                matrix: scale,
216
                                vector: offset,
217
                            };
218
                            child_bbox = transform * child_bbox;
219
                        }
220
                    }
221
                }
222
                // just translate
223
                None => child_bbox = child_bbox + offset,
224
            }
225

            
226
            // combine the scaled bbox with the overall bbox
227
            match bbox.as_mut() {
228
                Some(rect) => *rect = rect.union_rect(child_bbox),
229
                None => bbox = Some(child_bbox),
230
            }
231
        }
232
        Ok(bbox.unwrap_or_default())
233
    }
234
}
235

            
236
fn add_composite_glyph_delta(composite_glyph: &mut CompositeGlyphComponent, delta: Vector2F) {
237
    // > if ARGS_ARE_XY_VALUES (bit 1) is set, then X and Y offsets are used; if that bit is clear,
238
    // > then point numbers are used. If the position of a component is represented using X and Y
239
    // > offsets — the ARGS_ARE_XY_VALUES flag is set — then adjustment deltas can be applied to
240
    // > those offsets. However, if the position of a component is represented using point numbers —
241
    // > the ARGS_ARE_XY_VALUES flag is not set — then adjustment deltas have no effect on that
242
    // > component and should not be specified.
243
    //
244
    // https://learn.microsoft.com/en-us/typography/opentype/spec/gvar#point-numbers-and-processing-for-composite-glyphs
245
    if composite_glyph.flags.args_are_xy_values() {
246
        composite_glyph.argument1 = add_delta(composite_glyph.argument1, delta.x());
247
        composite_glyph.argument2 = add_delta(composite_glyph.argument2, delta.y());
248
        // add_delta always uses I16 so ensure the ARG_1_AND_2_ARE_WORDS flag is set
249
        composite_glyph.flags |= CompositeGlyphFlag::ARG_1_AND_2_ARE_WORDS;
250
    }
251
}
252

            
253
fn add_delta(arg: CompositeGlyphArgument, delta: f32) -> CompositeGlyphArgument {
254
    // If ARGS_ARE_XY_VALUES is set we should only get I8 or I16 values in practice
255
    // but handle them all nonetheless.
256
    let adjusted = match arg {
257
        CompositeGlyphArgument::U8(val) => val as f32 + delta,
258
        CompositeGlyphArgument::I8(val) => val as f32 + delta,
259
        CompositeGlyphArgument::U16(val) => val as f32 + delta,
260
        CompositeGlyphArgument::I16(val) => val as f32 + delta,
261
    };
262
    let adjusted = adjusted.round();
263

            
264
    // TODO: use smaller types when appropriate
265
    CompositeGlyphArgument::I16(adjusted as i16)
266
}
267

            
268
/// Calculate the point deltas for the supplied glyph according to the variation
269
/// instance `instance`.
270
///
271
/// If deltas are present the resulting vector will include a delta for each
272
/// coordinate in the glyph, including the four phantom points.
273
///
274
/// If the glyph has no variation data then `Ok(None)` is returned.
275
fn glyph_deltas(
276
    glyph: &Glyph,
277
    glyph_index: u16,
278
    instance: &OwnedTuple,
279
    gvar: &GvarTable<'_>,
280
) -> Result<Option<Vec<Vector2F>>, ParseError> {
281
    let num_points = NumPoints::new(glyph.number_of_points()?);
282
    let Some(variations) = gvar.glyph_variation_data(glyph_index, num_points)? else {
283
        return Ok(None);
284
    };
285

            
286
    let applicable = variations.determine_applicable(gvar, instance);
287

            
288
    // Now the deltas need to be calculated for each point.
289
    // The delta is multiplied by the scalar. The sum of deltas is applied to the
290
    // default position
291
    let mut final_deltas = vec![Vector2F::zero(); usize::safe_from(num_points.get())];
292
    let mut region_deltas = vec![Vector2F::zero(); usize::safe_from(num_points.get())];
293
    for (scale, region) in applicable {
294
        let variation_data =
295
            region.variation_data(num_points, variations.shared_point_numbers())?;
296
        // This is the output for this region, by the end every point needs to have a delta assigned.
297
        // Either explicitly or inferred. This buffer is reused between regions so we re-fill it
298
        // with zeros for each new region.
299
        region_deltas.fill(Vector2F::zero());
300

            
301
        // This maps point numbers to deltas, in order. It allows direct lookup of deltas for a
302
        // point as well as navigating between explicit points.
303
        let explicit_deltas = variation_data.iter().collect::<BTreeMap<_, _>>();
304

            
305
        // Fill in the explicit deltas
306
        for (number, delta) in &explicit_deltas {
307
            let region_delta = region_deltas
308
                .get_mut(usize::safe_from(*number))
309
                .ok_or(ParseError::BadIndex)?;
310
            *region_delta = Vector2F::new(delta.0 as f32, delta.1 as f32);
311
        }
312

            
313
        // > Calculation of inferred deltas is done for a given glyph and a given region on a
314
        // > contour-by-contour basis.
315
        // >
316
        // > For a given contour, if the point number list does not include any of the points in
317
        // > that contour, then none of the points in the contour are affected and no inferred deltas
318
        // > need to be computed.
319
        // >
320
        // > If the point number list includes some but not all of the points in a given contour,
321
        // > then inferred deltas must be derived for the points that were not included in the point
322
        // > number list, as follows.
323

            
324
        // Only need to do this for simple glyphs
325
        if let Glyph::Simple(simple_glyph) = glyph {
326
            // Deltas need to be inferred if not all points were assigned explicit deltas
327
            if explicit_deltas.len() != usize::safe_from(num_points.get()) {
328
                infer_unreferenced_points(&mut region_deltas, &explicit_deltas, simple_glyph)?;
329
            }
330
        }
331

            
332
        // Scale and accumulate the deltas from this variation region onto the final deltas
333
        final_deltas
334
            .iter_mut()
335
            .zip(region_deltas.iter().copied())
336
            .for_each(|(out, delta)| *out += delta * scale)
337
    }
338

            
339
    // Now all the deltas need to be applied to the glyph points
340
    Ok(Some(final_deltas))
341
}
342

            
343
fn infer_unreferenced_points(
344
    deltas: &mut [Vector2F],
345
    raw_deltas: &BTreeMap<u32, (i16, i16)>,
346
    simple_glyph: &SimpleGlyph,
347
) -> Result<(), ParseError> {
348
    // Iterate over the contours of the glyph and ensure that all points of the
349
    // contour have a delta
350
    let mut begin = 0;
351
    for end in simple_glyph.end_pts_of_contours.iter().copied() {
352
        let start = begin;
353
        let end = u32::from(end);
354
        begin = end + 1;
355
        let range = start..=end;
356
        let range_len = usize::safe_from(end.saturating_sub(start)) + 1; // Plus 1 because range is inclusive
357

            
358
        let explicit_count = raw_deltas.range(range.clone()).count();
359
        match explicit_count {
360
            0 => {
361
                // No points in this contour were referenced; no inferred deltas need to
362
                // be computed.
363
                continue;
364
            }
365
            1 => {
366
                // If exactly one point from the contour is referenced in the point number list,
367
                // then every point in that contour uses the same X and Y delta values as that
368
                // point. Find the one referenced point and use it to update the
369
                // others NOTE(unwrap): Safe as we confirmed we have one delta
370
                // to get into this block
371
                let (_referenced_point_number, reference_delta) = raw_deltas
372
                    .range(range.clone())
373
                    .next()
374
                    .map(|(n, (x, y))| (*n, Vector2F::new(*x as f32, *y as f32)))
375
                    .unwrap();
376
                // Get the delta for this point
377
                let usize_range = usize::safe_from(*range.start())..=usize::safe_from(*range.end());
378
                // Set all the deltas in this contour to `reference_delta`
379
                deltas[usize_range].fill(reference_delta);
380
                continue;
381
            }
382
            n if n == range_len => {
383
                // All points in this contour were referenced; no inferred deltas need to
384
                // be computed.
385
                continue;
386
            }
387
            _ => {
388
                // If the point number list includes some but not all of the points in a given
389
                // contour, then inferred deltas must be derived for the points that were not
390
                // included in the point number list.
391
                infer_contour(&range, deltas, raw_deltas, simple_glyph)?;
392
            }
393
        }
394
    }
395
    Ok(())
396
}
397

            
398
fn infer_contour(
399
    contour_range: &RangeInclusive<u32>,
400
    deltas: &mut [Vector2F],
401
    explicit_deltas: &BTreeMap<u32, (i16, i16)>,
402
    simple_glyph: &SimpleGlyph,
403
) -> Result<(), ParseError> {
404
    for target in contour_range.clone() {
405
        if explicit_deltas.contains_key(&target) {
406
            continue;
407
        }
408

            
409
        // This is an unreferenced point
410
        //
411
        // > First, for any un-referenced point, identify the nearest points before and after, in
412
        // > point number order, that are referenced. Note that the same referenced points will be
413
        // > used for calculating both X and Y inferred deltas. If there is no lower point number
414
        // > from that contour that was referenced, then the highest, referenced point number from
415
        // > that contour is used. Similarly, if no higher point number from that contour was
416
        // > referenced, then the lowest, referenced point number is used.
417

            
418
        // NOTE(unwrap): Due to checks above regarding the number of referenced points we should
419
        // always find a next/prev point
420
        let next = explicit_deltas
421
            .range(target..=*contour_range.end())
422
            .chain(explicit_deltas.range(*contour_range.start()..target))
423
            .next()
424
            .unwrap();
425
        let prev = explicit_deltas
426
            .range(target..=*contour_range.end())
427
            .chain(explicit_deltas.range(*contour_range.start()..target))
428
            .next_back()
429
            .unwrap();
430

            
431
        let target = usize::safe_from(target);
432
        deltas[target] = infer_delta(target, prev, next, &simple_glyph.coordinates)?;
433
    }
434
    Ok(())
435
}
436

            
437
// > Once the adjacent, referenced points are identified, then inferred-delta
438
// > calculation is done
439
// > separately for X and Y directions.
440
fn infer_delta(
441
    target: usize,
442
    (prev_number, prev_delta): (&u32, &(i16, i16)),
443
    (next_number, next_delta): (&u32, &(i16, i16)),
444
    coordinates: &[(SimpleGlyphFlags, Point)],
445
) -> Result<Vector2F, ParseError> {
446
    // https://learn.microsoft.com/en-us/typography/opentype/spec/gvar#inferred-deltas-for-un-referenced-point-numbers
447
    let prev_coord = coordinates
448
        .get(usize::safe_from(*prev_number))
449
        .ok_or(ParseError::BadIndex)?
450
        .1;
451
    let target_coord = coordinates.get(target).ok_or(ParseError::BadIndex)?.1;
452
    let next_coord = coordinates
453
        .get(usize::safe_from(*next_number))
454
        .ok_or(ParseError::BadIndex)?
455
        .1;
456

            
457
    let delta_x = do_infer(
458
        prev_coord.0,
459
        target_coord.0,
460
        next_coord.0,
461
        prev_delta.0,
462
        next_delta.0,
463
    );
464
    let delta_y = do_infer(
465
        prev_coord.1,
466
        target_coord.1,
467
        next_coord.1,
468
        prev_delta.1,
469
        next_delta.1,
470
    );
471
    Ok(Vector2F::new(delta_x, delta_y))
472
}
473

            
474
// > The (X or Y) grid coordinate values of the adjacent, referenced points are compared. If
475
// > these coordinates are the same, then the delta values for the adjacent points are compared: if
476
// > the delta values are the same, then this value is used as the inferred delta for the target,
477
// > un-referenced point. If the delta values are different, then the inferred delta for the target
478
// > point is zero.
479
fn do_infer(
480
    prev_coord: i16,
481
    target_coord: i16,
482
    next_coord: i16,
483
    prev_delta: i16,
484
    next_delta: i16,
485
) -> f32 {
486
    if prev_coord == next_coord {
487
        if prev_delta == next_delta {
488
            prev_delta as f32
489
        } else {
490
            0.
491
        }
492
    } else {
493
        // > But if the coordinate of the target point is not between the coordinates of the
494
        // > adjacent points, then the inferred delta is the delta for whichever of the adjacent
495
        // > points is closer in the given direction.
496
        if target_coord <= prev_coord.min(next_coord) {
497
            if prev_coord < next_coord {
498
                prev_delta as f32
499
            } else {
500
                next_delta as f32
501
            }
502
        } else if target_coord >= prev_coord.max(next_coord) {
503
            if prev_coord > next_coord {
504
                prev_delta as f32
505
            } else {
506
                next_delta as f32
507
            }
508
        } else {
509
            // > If the coordinate of the target point is between the coordinates of the adjacent
510
            // > points, then a delta is interpolated
511

            
512
            // > Note: The logical flow of the algorithm to this point implies that the coordinates
513
            // > of the two adjacent points are different. This avoids a division by zero in the
514
            // > following calculations that would otherwise occur.
515
            let proportion =
516
                (target_coord as f32 - prev_coord as f32) / (next_coord as f32 - prev_coord as f32);
517
            (1. - proportion) * prev_delta as f32 + proportion * next_delta as f32
518
        }
519
    }
520
}
521

            
522
#[cfg(test)]
523
mod tests {
524
    use super::*;
525
    use crate::binary::read::ReadScope;
526
    use crate::error::ReadWriteError;
527
    use crate::font_data::FontData;
528
    use crate::tables::glyf::{GlyfTable, SimpleGlyphFlag};
529
    use crate::tables::loca::LocaTable;
530
    use crate::tables::variable_fonts::avar::AvarTable;
531
    use crate::tables::variable_fonts::fvar::FvarTable;
532
    use crate::tables::{FontTableProvider, HeadTable, MaxpTable, NameTable};
533
    use crate::tests::read_fixture;
534
    use crate::{assert_close, tag};
535
    use pathfinder_geometry::vector::vec2i;
536

            
537
    #[test]
538
    fn apply_variations() -> Result<(), ReadWriteError> {
539
        let buffer = read_fixture("tests/fonts/opentype/NotoSans-VF.abc.ttf");
540
        let scope = ReadScope::new(&buffer);
541
        let font_file = scope.read::<FontData<'_>>()?;
542
        let provider = font_file.table_provider(0)?;
543
        let head = ReadScope::new(&provider.read_table_data(tag::HEAD)?).read::<HeadTable>()?;
544
        let maxp = ReadScope::new(&provider.read_table_data(tag::MAXP)?).read::<MaxpTable>()?;
545
        let loca_data = provider.read_table_data(tag::LOCA)?;
546
        let loca = ReadScope::new(&loca_data)
547
            .read_dep::<LocaTable<'_>>((maxp.num_glyphs, head.index_to_loc_format))?;
548
        let glyf_data = provider.read_table_data(tag::GLYF)?;
549
        let mut glyf = ReadScope::new(&glyf_data).read_dep::<GlyfTable<'_>>(&loca)?;
550
        let fvar_data = provider
551
            .read_table_data(tag::FVAR)
552
            .expect("unable to read fvar table data");
553
        let fvar = ReadScope::new(&fvar_data).read::<FvarTable<'_>>().unwrap();
554
        let avar_data = provider.table_data(tag::AVAR)?;
555
        let avar = avar_data
556
            .as_ref()
557
            .map(|avar_data| ReadScope::new(avar_data).read::<AvarTable<'_>>())
558
            .transpose()?;
559
        let gvar_data = provider.read_table_data(tag::GVAR)?;
560
        let gvar = ReadScope::new(&gvar_data).read::<GvarTable<'_>>().unwrap();
561
        let name_table_data = provider
562
            .read_table_data(tag::NAME)
563
            .expect("unable to read name table data");
564
        let name_table = ReadScope::new(&name_table_data)
565
            .read::<NameTable<'_>>()
566
            .unwrap();
567

            
568
        // Pick a glyph. Glyph 2 is 'b'
569
        let glyph_index = 2u16;
570
        let glyph = glyf.get_parsed_glyph(glyph_index)?;
571

            
572
        // Pick an instance
573
        let mut instance = None;
574
        for inst in fvar.instances() {
575
            let inst = inst?;
576
            let subfamily = name_table.string_for_id(inst.subfamily_name_id);
577
            if subfamily.as_deref() == Some("Display Condensed Thin") {
578
                // - wght = min: 100, max: 900, default: 400
579
                // - wdth = min: 62.5, max: 100, default: 100
580
                // - CTGR = min: 0, max: 100, default: 0
581
                //
582
                // Coordinates: [100.0, 76.24969, 100.0]
583
                instance = Some(inst);
584
                break;
585
            }
586
        }
587
        let user_instance = instance.unwrap();
588
        let instance = fvar
589
            .normalize(user_instance.coordinates.iter(), avar.as_ref())
590
            .unwrap();
591

            
592
        let varied = glyph_deltas(glyph, glyph_index, &instance, &gvar)?
593
            .expect("there should be glyph deltas");
594

            
595
        // These values were obtained by feeding the same parameters into
596
        // [skrifa](https://docs.rs/crate/skrifa/0.11.0).
597
        let expected_deltas = &[
598
            (-73.86737060546875, -80.800537109375),
599
            (-73.86737060546875, -65.200439453125),
600
            (-71.24325561523438, -51.0),
601
            (-70.29388427734375, -50.599853515625),
602
            (-73.1673583984375, -50.599853515625),
603
            (-84.32525634765625, -30.09991455078125),
604
            (-88.37908935546875, -7.4000244140625),
605
            (-95.0008544921875, -7.4000244140625),
606
            (-114.1365966796875, -7.4000244140625),
607
            (-153.50152587890625, -5.29998779296875),
608
            (-153.50152587890625, 0.10003662109375),
609
            (-153.50152587890625, 4.1998291015625),
610
            (-135.71871948242188, 3.89984130859375),
611
            (-112.06466674804688, 0.0),
612
            (-102.67691040039063, 0.0),
613
            (-92.93865966796875, 0.0),
614
            (-78.8035888671875, 15.49993896484375),
615
            (-71.71092224121094, 31.09991455078125),
616
            (-66.50018310546875, 31.09991455078125),
617
            (-53.50018310546875, 0.0),
618
            (-11.8001708984375, 0.0),
619
            (-11.8001708984375, 0.0),
620
            (-73.86737060546875, 0.0),
621
            (-80.65133666992188, 40.5999755859375),
622
            (-72.0006103515625, 40.5999755859375),
623
            (-70.2852783203125, 27.50006103515625),
624
            (-73.50018310546875, 14.30023193359375),
625
            (-73.50018310546875, 13.70037841796875),
626
            (-73.50018310546875, -23.8001708984375),
627
            (-73.50018310546875, -41.50018310546875),
628
            (-66.0003662109375, -47.29998779296875),
629
            (-90.000732421875, -47.29998779296875),
630
            (-93.10113525390625, -47.29998779296875),
631
            (-90.10150146484375, -27.90008544921875),
632
            (-90.10150146484375, -0.89996337890625),
633
            (-90.10150146484375, 19.0),
634
            (-84.10113525390625, 40.5999755859375),
635
            (0.0, 0.0),
636
            (0.0, 0.0),
637
            (0.0, 0.0),
638
            (0.0, 0.0),
639
        ];
640
        assert_eq!(varied.len(), expected_deltas.len());
641
        // Ignore phantom points at end
642
        for (expected, actual) in expected_deltas[..expected_deltas.len() - 4]
643
            .iter()
644
            .copied()
645
            .zip(varied.iter().copied())
646
        {
647
            assert_close!(actual.x(), expected.0, 0.005);
648
            assert_close!(actual.y(), expected.1, 0.005);
649
        }
650

            
651
        Ok(())
652
    }
653

            
654
    #[test]
655
    #[cfg(feature = "prince")]
656
    fn apply_skia_variations_simple_glyph() -> Result<(), ReadWriteError> {
657
        use crate::tables::Fixed;
658

            
659
        let buffer = read_fixture("../../../tests/data/fonts/Skia.subset.ttf");
660
        let scope = ReadScope::new(&buffer);
661
        let font_file = scope.read::<FontData<'_>>()?;
662
        let provider = font_file.table_provider(0)?;
663
        let head = ReadScope::new(&provider.read_table_data(tag::HEAD)?).read::<HeadTable>()?;
664
        let maxp = ReadScope::new(&provider.read_table_data(tag::MAXP)?).read::<MaxpTable>()?;
665
        let loca_data = provider.read_table_data(tag::LOCA)?;
666
        let loca = ReadScope::new(&loca_data)
667
            .read_dep::<LocaTable<'_>>((maxp.num_glyphs, head.index_to_loc_format))?;
668
        let glyf_data = provider.read_table_data(tag::GLYF)?;
669
        let mut glyf = ReadScope::new(&glyf_data).read_dep::<GlyfTable<'_>>(&loca)?;
670
        let fvar_data = provider
671
            .read_table_data(tag::FVAR)
672
            .expect("unable to read fvar table data");
673
        let fvar = ReadScope::new(&fvar_data).read::<FvarTable<'_>>().unwrap();
674
        let avar_data = provider.table_data(tag::AVAR)?;
675
        let avar = avar_data
676
            .as_ref()
677
            .map(|avar_data| ReadScope::new(avar_data).read::<AvarTable<'_>>())
678
            .transpose()?;
679
        let gvar_data = provider.read_table_data(tag::GVAR)?;
680
        let gvar = ReadScope::new(&gvar_data).read::<GvarTable<'_>>().unwrap();
681

            
682
        // Pick a glyph. Glyph 45 is '-', this is chosen to replicate the example in the
683
        // spec: https://learn.microsoft.com/en-us/typography/opentype/spec/otvaroverview#interpolation-example
684
        let glyph_index = 45u16;
685
        let glyph = glyf.get_parsed_glyph(glyph_index)?;
686

            
687
        // (0.2, 0.7) — a slight weight increase and a large width increase. The example
688
        // gives these are normalised values but we need to supply user values
689
        let user_instance = &[Fixed::from(1.44), Fixed::from(1.21)];
690
        let instance = fvar
691
            .normalize(user_instance.iter().copied(), avar.as_ref())
692
            .unwrap();
693

            
694
        let varied = glyph_deltas(glyph, glyph_index, &instance, &gvar)?
695
            .expect("there should be glyph deltas");
696

            
697
        let expected_deltas = &[
698
            (162.3, -28.4),
699
            (8.8, -28.4),
700
            (8.8, 36.4),
701
            (162.3, 36.4),
702
            (0., 0.),
703
            (172.7, 0.),
704
        ];
705
        for (expected, actual) in expected_deltas.iter().copied().zip(varied.iter().copied()) {
706
            assert_close!(actual.x(), expected.0, 0.005);
707
            assert_close!(actual.y(), expected.1, 0.005);
708
        }
709

            
710
        Ok(())
711
    }
712

            
713
    #[test]
714
    #[cfg(feature = "prince")]
715
    fn apply_skia_variations_composite_glyph() -> Result<(), ReadWriteError> {
716
        use crate::tables::Fixed;
717

            
718
        let buffer = read_fixture("../../../tests/data/fonts/Skia.subset.ttf");
719
        let scope = ReadScope::new(&buffer);
720
        let font_file = scope.read::<FontData<'_>>()?;
721
        let provider = font_file.table_provider(0)?;
722
        let head = ReadScope::new(&provider.read_table_data(tag::HEAD)?).read::<HeadTable>()?;
723
        let maxp = ReadScope::new(&provider.read_table_data(tag::MAXP)?).read::<MaxpTable>()?;
724
        let loca_data = provider.read_table_data(tag::LOCA)?;
725
        let loca = ReadScope::new(&loca_data)
726
            .read_dep::<LocaTable<'_>>((maxp.num_glyphs, head.index_to_loc_format))?;
727
        let glyf_data = provider.read_table_data(tag::GLYF)?;
728
        let mut glyf = ReadScope::new(&glyf_data).read_dep::<GlyfTable<'_>>(&loca)?;
729
        let fvar_data = provider
730
            .read_table_data(tag::FVAR)
731
            .expect("unable to read fvar table data");
732
        let fvar = ReadScope::new(&fvar_data).read::<FvarTable<'_>>().unwrap();
733
        let avar_data = provider.table_data(tag::AVAR)?;
734
        let avar = avar_data
735
            .as_ref()
736
            .map(|avar_data| ReadScope::new(avar_data).read::<AvarTable<'_>>())
737
            .transpose()?;
738
        let gvar_data = provider.read_table_data(tag::GVAR)?;
739
        let gvar = ReadScope::new(&gvar_data).read::<GvarTable<'_>>().unwrap();
740

            
741
        // Pick a glyph. Glyph 128 of the Skia font, which is the glyph for “Ä”. The
742
        // glyph entry has two component entries, both with ARGS_ARE_XY_VALUES
743
        // set. https://learn.microsoft.com/en-us/typography/opentype/spec/gvar#point-numbers-and-processing-for-composite-glyphs
744
        let glyph_index = 128u16;
745
        let glyph = glyf.get_parsed_glyph(glyph_index)?;
746

            
747
        // (0.2, 0.7) — a slight weight increase and a large width increase. The example
748
        // gives these are normalised values but we need to supply user values
749
        let user_instance = &[Fixed::from(1.44), Fixed::from(1.21)];
750
        let instance = fvar
751
            .normalize(user_instance.iter().copied(), avar.as_ref())
752
            .unwrap();
753

            
754
        let varied = glyph_deltas(glyph, glyph_index, &instance, &gvar)?
755
            .expect("there should be glyph deltas");
756

            
757
        // The example in the spec appears to be wrong, thus the final values don't
758
        // match. R3 in the example is supposed to correspond to the region
759
        // (weight, width) of (1, 1) however they seem to have used the values
760
        // from the (-1, 1) region. To try to rule out the example
761
        // using a different version of the font I confirmed this with versions of Skia
762
        // from Mac OS 7.6.1 and macOS 11.7.1 (Big Sur) and they were the same.
763
        //
764
        // Tracked by: https://github.com/MicrosoftDocs/typography-issues/issues/1067
765
        let r1_scale = 0.2;
766
        let r2_scale = 0.7;
767
        let r3_scale = 0.14;
768
        let expected_deltas = &[
769
            (0., 0.),
770
            ((r1_scale * 69.) + (r2_scale * 53.) + (r3_scale * -8.), 0.),
771
            ((r1_scale * 58.) + (r2_scale * 38.) + (r3_scale * -30.), 0.),
772
            ((r1_scale * 145.) + (r2_scale * 351.) + (r3_scale * 0.), 0.),
773
        ];
774
        for (expected, actual) in expected_deltas.iter().copied().zip(varied.iter().copied()) {
775
            assert_close!(actual.x(), expected.0, 0.01);
776
            assert_close!(actual.y(), expected.1, 0.01);
777
        }
778

            
779
        Ok(())
780
    }
781

            
782
    #[test]
783
    fn infer_unreferenced_points_test() {
784
        // The data used in this test is extracted from the RobotoFlex font 'j' glyph.
785
        // The inference was not working properly for point 7 of the first contour.
786
        let mut deltas = vec![
787
            vec2f(24.0, -1.0),
788
            vec2f(19.0, -2.0),
789
            vec2f(45.0, 0.0),
790
            vec2f(39.0, 0.0),
791
            vec2f(101.0, 0.0),
792
            vec2f(193.0, 38.0),
793
            vec2f(193.0, 48.0),
794
            vec2f(0.0, 0.0), // This is the one that interpolation was not populating
795
            vec2f(-30.0, 6.0),
796
            vec2f(-30.0, 139.0),
797
            vec2f(-30.0, 135.0),
798
            vec2f(-1.0, 135.0),
799
            vec2f(14.0, 135.0),
800
            vec2f(13.0, 135.0),
801
            vec2f(15.0, 135.0),
802
            vec2f(22.0, 135.0),
803
            vec2f(-36.0, -45.0),
804
            vec2f(0.0, 0.0),
805
            vec2f(0.0, 0.0),
806
            vec2f(81.0, -144.0),
807
            vec2f(0.0, 0.0),
808
            vec2f(0.0, 0.0),
809
            vec2f(198.0, -45.0),
810
            vec2f(0.0, 0.0),
811
            vec2f(0.0, 0.0),
812
            vec2f(82.0, 52.0),
813
            vec2f(0.0, 0.0),
814
            vec2f(0.0, 0.0),
815
            vec2f(0.0, 0.0),
816
            vec2f(135.0, 0.0),
817
            vec2f(0.0, 0.0),
818
            vec2f(0.0, 0.0),
819
        ];
820

            
821
        let region_deltas = [
822
            (0, (24, -1)),
823
            (1, (19, -2)),
824
            (2, (45, 0)),
825
            (3, (39, 0)),
826
            (4, (101, 0)),
827
            (5, (193, 38)),
828
            (6, (193, 48)),
829
            (8, (-30, 6)),
830
            (9, (-30, 139)),
831
            (10, (-30, 135)),
832
            (11, (-1, 135)),
833
            (12, (14, 135)),
834
            (13, (13, 135)),
835
            (14, (15, 135)),
836
            (15, (22, 135)),
837
            (16, (-36, -45)),
838
            (19, (81, -144)),
839
            (22, (198, -45)),
840
            (25, (82, 52)),
841
            (29, (135, 0)),
842
        ];
843
        let explicit_deltas = IntoIterator::into_iter(region_deltas).collect::<BTreeMap<_, _>>();
844

            
845
        // let raw_deltas: &BTreeMap<u32, (i16, i16)> = ;
846
        // let simple_glyph: &SimpleGlyph<'_> = ;
847
        let glyph = SimpleGlyph {
848
            bounding_box: BoundingBox {
849
                x_min: -94,
850
                x_max: 366,
851
                y_min: -436,
852
                y_max: 1481,
853
            },
854
            end_pts_of_contours: vec![15, 27],
855
            instructions: Box::default(),
856
            coordinates: vec![
857
                (
858
                    SimpleGlyphFlag::ON_CURVE_POINT | SimpleGlyphFlag::X_SHORT_VECTOR,
859
                    Point(-94, -410),
860
                ),
861
                (
862
                    SimpleGlyphFlag::X_SHORT_VECTOR
863
                        | SimpleGlyphFlag::Y_SHORT_VECTOR
864
                        | SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR,
865
                    Point(-69, -419),
866
                ),
867
                (
868
                    SimpleGlyphFlag::X_SHORT_VECTOR
869
                        | SimpleGlyphFlag::Y_SHORT_VECTOR
870
                        | SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR,
871
                    Point(30, -436),
872
                ),
873
                (
874
                    SimpleGlyphFlag::ON_CURVE_POINT
875
                        | SimpleGlyphFlag::X_SHORT_VECTOR
876
                        | SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR
877
                        | SimpleGlyphFlag::Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR,
878
                    Point(70, -436),
879
                ),
880
                (
881
                    SimpleGlyphFlag::X_SHORT_VECTOR
882
                        | SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR
883
                        | SimpleGlyphFlag::Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR,
884
                    Point(204, -436),
885
                ),
886
                (
887
                    SimpleGlyphFlag::X_SHORT_VECTOR
888
                        | SimpleGlyphFlag::Y_SHORT_VECTOR
889
                        | SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR
890
                        | SimpleGlyphFlag::Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR,
891
                    Point(343, -270),
892
                ),
893
                (
894
                    SimpleGlyphFlag::ON_CURVE_POINT
895
                        | SimpleGlyphFlag::Y_SHORT_VECTOR
896
                        | SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR
897
                        | SimpleGlyphFlag::Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR,
898
                    Point(343, -90),
899
                ),
900
                (
901
                    SimpleGlyphFlag::ON_CURVE_POINT
902
                        | SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR,
903
                    Point(343, 1052),
904
                ),
905
                (
906
                    SimpleGlyphFlag::ON_CURVE_POINT
907
                        | SimpleGlyphFlag::X_SHORT_VECTOR
908
                        | SimpleGlyphFlag::Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR,
909
                    Point(157, 1052),
910
                ),
911
                (
912
                    SimpleGlyphFlag::ON_CURVE_POINT
913
                        | SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR,
914
                    Point(157, -130),
915
                ),
916
                (
917
                    SimpleGlyphFlag::Y_SHORT_VECTOR
918
                        | SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR,
919
                    Point(157, -210),
920
                ),
921
                (
922
                    SimpleGlyphFlag::X_SHORT_VECTOR | SimpleGlyphFlag::Y_SHORT_VECTOR,
923
                    Point(90, -280),
924
                ),
925
                (
926
                    SimpleGlyphFlag::ON_CURVE_POINT
927
                        | SimpleGlyphFlag::X_SHORT_VECTOR
928
                        | SimpleGlyphFlag::Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR,
929
                    Point(30, -280),
930
                ),
931
                (
932
                    SimpleGlyphFlag::X_SHORT_VECTOR
933
                        | SimpleGlyphFlag::Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR,
934
                    Point(0, -280),
935
                ),
936
                (
937
                    SimpleGlyphFlag::X_SHORT_VECTOR
938
                        | SimpleGlyphFlag::Y_SHORT_VECTOR
939
                        | SimpleGlyphFlag::Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR,
940
                    Point(-74, -266),
941
                ),
942
                (
943
                    SimpleGlyphFlag::ON_CURVE_POINT
944
                        | SimpleGlyphFlag::X_SHORT_VECTOR
945
                        | SimpleGlyphFlag::Y_SHORT_VECTOR
946
                        | SimpleGlyphFlag::Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR,
947
                    Point(-94, -260),
948
                ),
949
                (
950
                    SimpleGlyphFlag::ON_CURVE_POINT
951
                        | SimpleGlyphFlag::X_SHORT_VECTOR
952
                        | SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR,
953
                    Point(134, 1370),
954
                ),
955
                (
956
                    SimpleGlyphFlag::Y_SHORT_VECTOR
957
                        | SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR,
958
                    Point(134, 1323),
959
                ),
960
                (
961
                    SimpleGlyphFlag::X_SHORT_VECTOR
962
                        | SimpleGlyphFlag::Y_SHORT_VECTOR
963
                        | SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR,
964
                    Point(194, 1259),
965
                ),
966
                (
967
                    SimpleGlyphFlag::ON_CURVE_POINT
968
                        | SimpleGlyphFlag::X_SHORT_VECTOR
969
                        | SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR
970
                        | SimpleGlyphFlag::Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR,
971
                    Point(250, 1259),
972
                ),
973
                (
974
                    SimpleGlyphFlag::X_SHORT_VECTOR
975
                        | SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR
976
                        | SimpleGlyphFlag::Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR,
977
                    Point(306, 1259),
978
                ),
979
                (
980
                    SimpleGlyphFlag::X_SHORT_VECTOR
981
                        | SimpleGlyphFlag::Y_SHORT_VECTOR
982
                        | SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR
983
                        | SimpleGlyphFlag::Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR,
984
                    Point(366, 1323),
985
                ),
986
                (
987
                    SimpleGlyphFlag::ON_CURVE_POINT
988
                        | SimpleGlyphFlag::Y_SHORT_VECTOR
989
                        | SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR
990
                        | SimpleGlyphFlag::Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR,
991
                    Point(366, 1370),
992
                ),
993
                (
994
                    SimpleGlyphFlag::Y_SHORT_VECTOR
995
                        | SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR
996
                        | SimpleGlyphFlag::Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR,
997
                    Point(366, 1417),
998
                ),
999
                (
                    SimpleGlyphFlag::X_SHORT_VECTOR
                        | SimpleGlyphFlag::Y_SHORT_VECTOR
                        | SimpleGlyphFlag::Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR,
                    Point(306, 1481),
                ),
                (
                    SimpleGlyphFlag::ON_CURVE_POINT
                        | SimpleGlyphFlag::X_SHORT_VECTOR
                        | SimpleGlyphFlag::Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR,
                    Point(250, 1481),
                ),
                (
                    SimpleGlyphFlag::X_SHORT_VECTOR
                        | SimpleGlyphFlag::Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR,
                    Point(194, 1481),
                ),
                (
                    SimpleGlyphFlag::X_SHORT_VECTOR | SimpleGlyphFlag::Y_SHORT_VECTOR,
                    Point(134, 1417),
                ),
            ],
            phantom_points: None,
        };
        let expected = [
            vec2i(24, -1),
            vec2i(19, -2),
            vec2i(45, 0),
            vec2i(39, 0),
            vec2i(101, 0),
            vec2i(193, 38),
            vec2i(193, 48),
            vec2i(193, 6),
            vec2i(-30, 6),
            vec2i(-30, 139),
            vec2i(-30, 135),
            vec2i(-1, 135),
            vec2i(14, 135),
            vec2i(13, 135),
            vec2i(15, 135),
            vec2i(22, 135),
            vec2i(-36, -45),
            vec2i(-36, -87),
            vec2i(25, -144),
            vec2i(81, -144),
            vec2i(137, -144),
            vec2i(198, -87),
            vec2i(198, -45),
            vec2i(198, -4),
            vec2i(138, 52),
            vec2i(82, 52),
            vec2i(25, 52),
            vec2i(-36, -4),
            vec2i(0, 0),
            vec2i(135, 0),
            vec2i(0, 0),
            vec2i(0, 0),
        ];
        infer_unreferenced_points(&mut deltas, &explicit_deltas, &glyph).unwrap();
        // round actual values for comparison
        let deltas = deltas
            .into_iter()
            .map(|delta| delta.round().to_i32())
            .collect::<Vec<_>>();
        assert_eq!(deltas, expected);
    }
}