1use core::ops::Range;
12
13use super::{GlyphDelta, GlyphVariationData, Gvar};
14use crate::{
15 tables::{
16 glyf::{PointCoord, PointFlags, PointMarker, PHANTOM_POINT_COUNT},
17 variations::TupleVariation,
18 },
19 types::{F2Dot14, Fixed, GlyphId, Point},
20 ReadError,
21};
22
23pub struct DeltaBuffers<'a, D: PointCoord> {
29 pub deltas: &'a mut [Point<D>],
31 pub iup: &'a mut [Point<D>],
34}
35
36impl Gvar<'_> {
37 pub fn simple_deltas<C, D>(
47 &self,
48 glyph_id: GlyphId,
49 coords: &[F2Dot14],
50 points: &[Point<C>],
51 flags: &mut [PointFlags],
52 contours: &[u16],
53 buffers: &mut DeltaBuffers<'_, D>,
54 ) -> Result<bool, ReadError>
55 where
56 C: PointCoord,
57 D: PointCoord + From<C>,
58 {
59 check_simple_buffers(points, flags, buffers)?;
60 let Ok(Some(var_data)) = self.glyph_variation_data(glyph_id) else {
61 zero(buffers.deltas);
63 return Ok(false);
64 };
65 var_data.simple_deltas(coords, points, flags, contours, buffers)?;
66 Ok(true)
67 }
68
69 pub fn composite_deltas<D: PointCoord>(
81 &self,
82 glyph_id: GlyphId,
83 coords: &[F2Dot14],
84 deltas: &mut [Point<D>],
85 ) -> Result<bool, ReadError> {
86 let Ok(Some(var_data)) = self.glyph_variation_data(glyph_id) else {
87 zero(deltas);
88 return Ok(false);
89 };
90 var_data.composite_deltas(coords, deltas)?;
91 Ok(true)
92 }
93}
94
95impl GlyphVariationData<'_> {
96 pub fn simple_deltas<C, D>(
117 &self,
118 coords: &[F2Dot14],
119 points: &[Point<C>],
120 flags: &mut [PointFlags],
121 contours: &[u16],
122 buffers: &mut DeltaBuffers<'_, D>,
123 ) -> Result<(), ReadError>
124 where
125 C: PointCoord,
126 D: PointCoord + From<C>,
127 {
128 check_simple_buffers(points, flags, buffers)?;
129 let DeltaBuffers { deltas, iup } = buffers;
130 self.accumulate_deltas(coords, deltas, |scalar, tuple, deltas| {
131 for (point, iup_point) in points.iter().zip(&mut iup[..]) {
136 *iup_point = point.map(D::from);
137 }
138 for flag in flags.iter_mut() {
139 flag.clear_marker(PointMarker::HAS_DELTA);
140 }
141 tuple.accumulate_sparse_deltas(iup, flags, scalar)?;
142 interpolate_deltas(points, flags, contours, &mut iup[..])
143 .ok_or(ReadError::OutOfBounds)?;
144 for ((delta, point), iup_point) in deltas.iter_mut().zip(points).zip(iup.iter()) {
145 *delta += *iup_point - point.map(D::from);
146 }
147 Ok(())
148 })
149 }
150
151 pub fn composite_deltas<D: PointCoord>(
162 &self,
163 coords: &[F2Dot14],
164 deltas: &mut [Point<D>],
165 ) -> Result<(), ReadError> {
166 self.accumulate_deltas(coords, deltas, |scalar, tuple, deltas| {
167 for tuple_delta in tuple.deltas() {
168 let ix = tuple_delta.position as usize;
169 if let Some(delta) = deltas.get_mut(ix) {
170 *delta += tuple_delta.apply_scalar(scalar);
171 }
172 }
173 Ok(())
174 })
175 }
176
177 fn accumulate_deltas<D: PointCoord>(
183 &self,
184 coords: &[F2Dot14],
185 deltas: &mut [Point<D>],
186 mut apply_sparse_tuple: impl FnMut(
187 Fixed,
188 TupleVariation<GlyphDelta>,
189 &mut [Point<D>],
190 ) -> Result<(), ReadError>,
191 ) -> Result<(), ReadError> {
192 zero(deltas);
194 for (tuple, scalar) in self.active_tuples_at(coords) {
195 if tuple.has_deltas_for_all_points() {
196 tuple.accumulate_dense_deltas(deltas, scalar)?;
199 } else {
200 apply_sparse_tuple(scalar, tuple, deltas)?;
201 }
202 }
203 Ok(())
204 }
205}
206
207fn check_simple_buffers<C: PointCoord, D: PointCoord>(
215 points: &[Point<C>],
216 flags: &[PointFlags],
217 buffers: &DeltaBuffers<'_, D>,
218) -> Result<(), ReadError> {
219 let count = points.len();
220 if count < PHANTOM_POINT_COUNT
221 || flags.len() < count
222 || buffers.deltas.len() < count
223 || buffers.iup.len() < count
224 {
225 return Err(ReadError::InvalidArrayLen);
226 }
227 Ok(())
228}
229
230fn zero<D: PointCoord>(deltas: &mut [Point<D>]) {
231 for delta in deltas.iter_mut() {
232 *delta = Default::default();
233 }
234}
235
236fn interpolate_deltas<C, D>(
245 points: &[Point<C>],
246 flags: &[PointFlags],
247 contours: &[u16],
248 out_points: &mut [Point<D>],
249) -> Option<()>
250where
251 C: PointCoord,
252 D: PointCoord + From<C>,
253{
254 let mut start = 0usize;
255 for &end in contours {
256 let end = end as usize;
257 if end < start {
261 continue;
262 }
263 let range = start..end + 1;
264 start = end + 1;
265 Contour {
268 points: points.get(range.clone())?,
269 flags: flags.get(range.clone())?,
270 out_points: out_points.get_mut(range)?,
271 }
272 .interpolate_untouched();
273 }
274 Some(())
275}
276
277struct Contour<'a, C, D>
283where
284 C: PointCoord,
285 D: PointCoord + From<C>,
286{
287 points: &'a [Point<C>],
288 flags: &'a [PointFlags],
289 out_points: &'a mut [Point<D>],
290}
291
292impl<C, D> Contour<'_, C, D>
293where
294 C: PointCoord,
295 D: PointCoord + From<C>,
296{
297 fn interpolate_untouched(&mut self) {
300 let flags = self.flags;
302 let mut referenced = flags
303 .iter()
304 .enumerate()
305 .filter(|(_, flag)| flag.has_marker(PointMarker::HAS_DELTA))
306 .map(|(ix, _)| ix);
307 let Some(first) = referenced.next() else {
308 return;
311 };
312 let mut last = first;
314 for next in referenced {
315 self.interpolate(last + 1..next, last, next);
316 last = next;
317 }
318 if last == first {
319 self.shift(first);
321 } else {
322 let len = self.out_points.len();
326 self.interpolate(last + 1..len, last, first);
327 self.interpolate(0..first, last, first);
328 }
329 }
330
331 fn shift(&mut self, reference: usize) {
335 let delta = self.out_points[reference] - self.points[reference].map(D::from);
336 if delta.x == D::zeroed() && delta.y == D::zeroed() {
337 return;
338 }
339 let (before, rest) = self.out_points.split_at_mut(reference);
341 for out_point in before {
342 *out_point += delta;
343 }
344 for out_point in &mut rest[1..] {
345 *out_point += delta;
346 }
347 }
348
349 fn interpolate(&mut self, range: Range<usize>, ref1: usize, ref2: usize) {
356 if range.is_empty() {
357 return;
358 }
359 macro_rules! interp_coord {
362 ($coord:ident) => {
363 let (lo, hi) = if self.points[ref1].$coord > self.points[ref2].$coord {
366 (ref2, ref1)
367 } else {
368 (ref1, ref2)
369 };
370 let in1 = D::from(self.points[lo].$coord);
371 let in2 = D::from(self.points[hi].$coord);
372 let out1 = self.out_points[lo].$coord;
373 let out2 = self.out_points[hi].$coord;
374 if in1 != in2 || out1 == out2 {
377 let scale = if in1 != in2 {
378 (out2 - out1) / (in2 - in1)
379 } else {
380 D::zeroed()
381 };
382 let d1 = out1 - in1;
383 let d2 = out2 - in2;
384 for (point, out_point) in self.points[range.clone()]
385 .iter()
386 .zip(&mut self.out_points[range.clone()])
387 {
388 let coord = D::from(point.$coord);
389 out_point.$coord = if coord <= in1 {
392 coord + d1
393 } else if coord >= in2 {
394 coord + d2
395 } else {
396 out1 + (coord - in1) * scale
397 };
398 }
399 }
400 };
401 }
402 interp_coord!(x);
403 interp_coord!(y);
404 }
405}
406
407#[cfg(test)]
408mod tests {
409 use super::*;
410 use crate::{
411 tables::{
412 glyf::{Glyf, Glyph},
413 loca::Loca,
414 },
415 FontRef, TableProvider,
416 };
417 use alloc::{vec, vec::Vec};
418
419 fn make_points(tuples: &[(i32, i32)]) -> Vec<Point<i32>> {
420 tuples.iter().map(|&(x, y)| Point::new(x, y)).collect()
421 }
422
423 fn make_working_points_and_flags(
427 points: &[Point<i32>],
428 deltas: &[Point<i32>],
429 ) -> (Vec<Point<Fixed>>, Vec<PointFlags>) {
430 let working_points = points
431 .iter()
432 .zip(deltas)
433 .map(|(point, delta)| point.map(Fixed::from_i32) + delta.map(Fixed::from_i32))
434 .collect();
435 let flags = deltas
436 .iter()
437 .map(|delta| {
438 let mut flags = PointFlags::default();
439 if delta.x != 0 || delta.y != 0 {
440 flags.set_marker(PointMarker::HAS_DELTA);
441 }
442 flags
443 })
444 .collect();
445 (working_points, flags)
446 }
447
448 fn interpolated_x(
450 points: &[Point<i32>],
451 deltas: &[Point<i32>],
452 contours: &[u16],
453 ) -> Option<Vec<i32>> {
454 let (mut working, flags) = make_working_points_and_flags(points, deltas);
455 interpolate_deltas(points, &flags, contours, &mut working)?;
456 Some(working.iter().map(|p| p.x.to_i32()).collect())
457 }
458
459 #[test]
460 fn shift() {
461 let points = make_points(&[(245, 630), (260, 700), (305, 680)]);
462 let deltas = make_points(&[(20, -10), (0, 0), (0, 0)]);
464 let (mut working_points, flags) = make_working_points_and_flags(&points, &deltas);
465 interpolate_deltas(&points, &flags, &[2], &mut working_points).unwrap();
466 let expected = &[
467 Point::new(265, 620).map(Fixed::from_i32),
468 Point::new(280, 690).map(Fixed::from_i32),
469 Point::new(325, 670).map(Fixed::from_i32),
470 ];
471 assert_eq!(&working_points, expected);
472 }
473
474 #[test]
475 fn interpolate() {
476 let points = make_points(&[(245, 630), (260, 700), (305, 680)]);
480 let deltas = make_points(&[(28, -62), (0, 0), (-42, -57)]);
481 let (mut working_points, flags) = make_working_points_and_flags(&points, &deltas);
482 interpolate_deltas(&points, &flags, &[2], &mut working_points).unwrap();
483 assert_eq!(
484 working_points[1],
485 Point::new(
486 Fixed::from_f64(260.0 + 10.4999237060547),
487 Fixed::from_f64(700.0 - 57.0)
488 )
489 );
490 }
491
492 #[test]
497 fn interpolate_clamps_outside_the_reference_range() {
498 let points = make_points(&[(0, 0), (10, 0), (15, 0), (20, 0), (30, 0)]);
500 let deltas = make_points(&[(0, 0), (4, 0), (0, 0), (14, 0), (0, 0)]);
501 assert_eq!(
502 interpolated_x(&points, &deltas, &[4]).unwrap(),
503 vec![4, 14, 24, 34, 44]
505 );
506 }
507
508 #[test]
510 fn contour_without_deltas_is_untouched() {
511 let points = make_points(&[(0, 0), (10, 0), (20, 0)]);
512 let deltas = make_points(&[(0, 0), (0, 0), (0, 0)]);
513 assert_eq!(
514 interpolated_x(&points, &deltas, &[2]).unwrap(),
515 vec![0, 10, 20]
516 );
517 }
518
519 #[test]
521 fn fully_referenced_contour_is_untouched() {
522 let points = make_points(&[(0, 0), (10, 0), (20, 0)]);
523 let deltas = make_points(&[(1, 0), (2, 0), (3, 0)]);
524 assert_eq!(
525 interpolated_x(&points, &deltas, &[2]).unwrap(),
526 vec![1, 12, 23]
527 );
528 }
529
530 #[test]
532 fn contours_are_independent() {
533 let points = make_points(&[(0, 0), (10, 0), (20, 0), (100, 0), (110, 0), (120, 0)]);
534 let deltas = make_points(&[(5, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0)]);
537 assert_eq!(
538 interpolated_x(&points, &deltas, &[2, 5]).unwrap(),
539 vec![5, 15, 25, 100, 110, 120]
540 );
541 }
542
543 #[test]
546 fn points_before_first_reference_wrap_around() {
547 let points = make_points(&[(0, 0), (10, 0), (20, 0), (30, 0)]);
548 let deltas = make_points(&[(0, 0), (6, 0), (6, 0), (0, 0)]);
551 assert_eq!(
552 interpolated_x(&points, &deltas, &[3]).unwrap(),
553 vec![6, 16, 26, 36]
554 );
555 }
556
557 #[test]
560 fn equal_reference_coords_with_different_deltas_infer_nothing() {
561 let points = make_points(&[(10, 0), (10, 5), (10, 10)]);
562 let deltas = make_points(&[(2, 0), (0, 0), (6, 0)]);
563 let (mut working, flags) = make_working_points_and_flags(&points, &deltas);
564 interpolate_deltas(&points, &flags, &[2], &mut working).unwrap();
565 assert_eq!(working[1], Point::new(10, 5).map(Fixed::from_i32));
568 }
569
570 #[test]
573 fn out_of_order_contour_end_is_skipped() {
574 let points = make_points(&[(0, 0), (10, 0), (20, 0), (30, 0)]);
575 let deltas = make_points(&[(5, 0), (0, 0), (0, 0), (0, 0)]);
576 assert_eq!(
578 interpolated_x(&points, &deltas, &[3, 1]).unwrap(),
579 vec![5, 15, 25, 35]
580 );
581 }
582
583 #[test]
586 fn contour_end_past_last_point_is_rejected() {
587 let points = make_points(&[(0, 0), (10, 0)]);
588 let deltas = make_points(&[(5, 0), (0, 0)]);
589 assert!(interpolated_x(&points, &deltas, &[9]).is_none());
590 }
591
592 const VAR_GID: GlyphId = GlyphId::new(1);
598 const COMPOSITE_GID: GlyphId = GlyphId::new(2);
599 const NO_VAR_GID: GlyphId = GlyphId::new(0);
600
601 struct TestGlyph<'a> {
604 gvar: Gvar<'a>,
605 points: Vec<Point<i32>>,
606 flags: Vec<PointFlags>,
607 contours: Vec<u16>,
608 }
609
610 impl<'a> TestGlyph<'a> {
611 fn new(font: &FontRef<'a>, gid: GlyphId) -> Self {
612 let glyf: Glyf<'a> = font.glyf().unwrap();
613 let loca: Loca<'a> = font.loca(None).unwrap();
614 let Some(Glyph::Simple(simple)) = loca.get_glyf(gid, &glyf).unwrap() else {
615 panic!("expected a simple glyph");
616 };
617 let n = simple.num_points();
618 let total = n + PHANTOM_POINT_COUNT;
619 let mut points = vec![Point::<i32>::default(); total];
620 let mut flags = vec![PointFlags::default(); total];
621 simple
622 .read_points_fast(&mut points[..n], &mut flags[..n])
623 .unwrap();
624 let contours = simple
625 .end_pts_of_contours()
626 .iter()
627 .map(|c| c.get())
628 .collect();
629 Self {
630 gvar: font.gvar().unwrap(),
631 points,
632 flags,
633 contours,
634 }
635 }
636
637 fn deltas(&mut self, gid: GlyphId, coords: &[F2Dot14]) -> (bool, Vec<Point<Fixed>>) {
638 let total = self.points.len();
639 let mut deltas = vec![Point::<Fixed>::default(); total];
640 let mut iup = vec![Point::<Fixed>::default(); total];
641 let mut buffers = DeltaBuffers {
642 deltas: &mut deltas,
643 iup: &mut iup,
644 };
645 let varied = self
646 .gvar
647 .simple_deltas(
648 gid,
649 coords,
650 &self.points,
651 &mut self.flags,
652 &self.contours,
653 &mut buffers,
654 )
655 .unwrap();
656 (varied, deltas)
657 }
658 }
659
660 #[test]
661 fn simple_deltas_at_default_location_are_zero() {
662 let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
663 let mut glyph = TestGlyph::new(&font, VAR_GID);
664 let (varied, deltas) = glyph.deltas(VAR_GID, &[F2Dot14::from_f32(0.0)]);
667 assert!(varied);
668 assert!(deltas.iter().all(|d| *d == Point::default()));
669 }
670
671 #[test]
672 fn simple_deltas_at_extreme_move_points() {
673 let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
674 let mut glyph = TestGlyph::new(&font, VAR_GID);
675 let (varied, deltas) = glyph.deltas(VAR_GID, &[F2Dot14::from_f32(1.0)]);
676 assert!(varied);
677 let outline_deltas = &deltas[..deltas.len() - PHANTOM_POINT_COUNT];
678 assert!(
679 outline_deltas.iter().any(|d| *d != Point::default()),
680 "expected at least one non-zero outline delta"
681 );
682 }
683
684 #[test]
687 fn simple_deltas_scale_along_the_axis() {
688 let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
689 let mut glyph = TestGlyph::new(&font, VAR_GID);
690 let (_, half) = glyph.deltas(VAR_GID, &[F2Dot14::from_f32(0.5)]);
691 let (_, full) = glyph.deltas(VAR_GID, &[F2Dot14::from_f32(1.0)]);
692 let magnitude = |ds: &[Point<Fixed>]| -> f64 {
693 ds.iter()
694 .map(|d| d.x.to_f64().abs() + d.y.to_f64().abs())
695 .sum()
696 };
697 let (half_sum, full_sum) = (magnitude(&half), magnitude(&full));
698 assert!(half_sum > 0.0);
699 assert!(
700 half_sum < full_sum,
701 "half {half_sum} should be less than full {full_sum}"
702 );
703 }
704
705 #[test]
707 fn deltas_do_not_accumulate_across_calls() {
708 let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
709 let mut glyph = TestGlyph::new(&font, VAR_GID);
710 let (_, once) = glyph.deltas(VAR_GID, &[F2Dot14::from_f32(1.0)]);
711 let (_, twice) = glyph.deltas(VAR_GID, &[F2Dot14::from_f32(1.0)]);
712 assert_eq!(once, twice);
713 }
714
715 #[test]
719 fn simple_deltas_without_variation_data_zeroes_buffer() {
720 let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
721 let mut glyph = TestGlyph::new(&font, VAR_GID);
722 let (varied, deltas) = glyph.deltas(NO_VAR_GID, &[F2Dot14::from_f32(1.0)]);
723 assert!(!varied);
724 assert!(deltas.iter().all(|d| *d == Point::default()));
725 }
726
727 #[test]
728 fn simple_deltas_rejects_short_iup_buffer() {
729 let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
730 let gvar = font.gvar().unwrap();
731 let points = [Point::<i32>::default(); 8];
732 let mut flags = [PointFlags::default(); 8];
733 let mut deltas = [Point::<Fixed>::default(); 8];
734 let mut iup = [Point::<Fixed>::default(); 4];
735 let mut buffers = DeltaBuffers {
736 deltas: &mut deltas,
737 iup: &mut iup,
738 };
739 assert!(matches!(
740 gvar.simple_deltas(VAR_GID, &[], &points, &mut flags, &[7], &mut buffers),
741 Err(ReadError::InvalidArrayLen)
742 ));
743 }
744
745 #[test]
746 fn simple_deltas_rejects_missing_phantom_points() {
747 let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
748 let gvar = font.gvar().unwrap();
749 let points = [Point::<i32>::default(); 3];
751 let mut flags = [PointFlags::default(); 3];
752 let mut deltas = [Point::<Fixed>::default(); 3];
753 let mut iup = [Point::<Fixed>::default(); 3];
754 let mut buffers = DeltaBuffers {
755 deltas: &mut deltas,
756 iup: &mut iup,
757 };
758 assert!(matches!(
759 gvar.simple_deltas(VAR_GID, &[], &points, &mut flags, &[2], &mut buffers),
760 Err(ReadError::InvalidArrayLen)
761 ));
762 }
763
764 #[test]
765 fn composite_deltas_without_variation_data_zeroes_buffer() {
766 let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
767 let gvar = font.gvar().unwrap();
768 let coords = [F2Dot14::from_f32(0.5)];
769 let mut deltas = [Point::new(Fixed::from_i32(7), Fixed::from_i32(9)); 8];
770 let varied = gvar
771 .composite_deltas(NO_VAR_GID, &coords, &mut deltas)
772 .unwrap();
773 assert!(!varied);
774 assert!(deltas.iter().all(|d| *d == Point::default()));
775 }
776
777 #[test]
778 fn composite_deltas_with_variation_data_reports_true() {
779 let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
780 let gvar = font.gvar().unwrap();
781 let coords = [F2Dot14::from_f32(1.0)];
782 let mut deltas = [Point::<Fixed>::default(); 8];
783 let varied = gvar
784 .composite_deltas(COMPOSITE_GID, &coords, &mut deltas)
785 .unwrap();
786 assert!(varied);
787 }
788
789 #[test]
793 fn composite_deltas_tolerates_short_buffer() {
794 let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
795 let gvar = font.gvar().unwrap();
796 let coords = [F2Dot14::from_f32(1.0)];
797 let mut deltas = [Point::<Fixed>::default(); 1];
798 assert!(gvar
799 .composite_deltas(COMPOSITE_GID, &coords, &mut deltas)
800 .is_ok());
801 }
802
803 #[test]
806 fn both_layers_agree() {
807 let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
808 let gvar = font.gvar().unwrap();
809 let glyf = font.glyf().unwrap();
810 let loca = font.loca(None).unwrap();
811 let coords = [F2Dot14::from_f32(0.5)];
812 for gid in 0..font.maxp().unwrap().num_glyphs() {
813 let glyph_id = GlyphId::from(gid);
814 let Some(Glyph::Simple(simple)) = loca.get_glyf(glyph_id, &glyf).unwrap() else {
815 continue;
816 };
817 let count = simple.num_points() + PHANTOM_POINT_COUNT;
818 let points = vec![Point::<i32>::default(); count];
819 let contours: Vec<u16> = simple
820 .end_pts_of_contours()
821 .iter()
822 .map(|e| e.get())
823 .collect();
824
825 let mut through_gvar = vec![Point::<Fixed>::default(); count];
826 let mut iup = vec![Point::<Fixed>::default(); count];
827 let mut flags = vec![PointFlags::default(); count];
828 let had_data = gvar
829 .simple_deltas(
830 glyph_id,
831 &coords,
832 &points,
833 &mut flags,
834 &contours,
835 &mut DeltaBuffers {
836 deltas: &mut through_gvar,
837 iup: &mut iup,
838 },
839 )
840 .unwrap();
841
842 let var_data = gvar.glyph_variation_data(glyph_id).unwrap();
843 assert_eq!(had_data, var_data.is_some(), "gid {gid}");
844 let Some(var_data) = var_data else { continue };
845
846 let mut direct = vec![Point::<Fixed>::default(); count];
847 let mut iup = vec![Point::<Fixed>::default(); count];
848 let mut flags = vec![PointFlags::default(); count];
849 var_data
850 .simple_deltas(
851 &coords,
852 &points,
853 &mut flags,
854 &contours,
855 &mut DeltaBuffers {
856 deltas: &mut direct,
857 iup: &mut iup,
858 },
859 )
860 .unwrap();
861 assert_eq!(through_gvar, direct, "gid {gid}");
862 }
863 }
864
865 #[test]
868 fn a_glyph_without_data_still_zeroes() {
869 let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
870 let gvar = font.gvar().unwrap();
871 let missing = GlyphId::new(0xFFFF);
873 let mut deltas = vec![Point::new(Fixed::from_i32(7), Fixed::from_i32(9)); 8];
874 assert!(!gvar.composite_deltas(missing, &[], &mut deltas).unwrap());
875 assert!(deltas.iter().all(|d| *d == Point::default()));
876
877 let points = vec![Point::<i32>::default(); 8];
878 let mut deltas = vec![Point::new(Fixed::from_i32(7), Fixed::from_i32(9)); 8];
879 let mut iup = vec![Point::<Fixed>::default(); 8];
880 let mut flags = vec![PointFlags::default(); 8];
881 assert!(!gvar
882 .simple_deltas(
883 missing,
884 &[],
885 &points,
886 &mut flags,
887 &[7],
888 &mut DeltaBuffers {
889 deltas: &mut deltas,
890 iup: &mut iup,
891 },
892 )
893 .unwrap());
894 assert!(deltas.iter().all(|d| *d == Point::default()));
895 }
896
897 #[test]
900 fn short_buffers_are_rejected_by_both() {
901 let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
902 let gvar = font.gvar().unwrap();
903 let glyph_id = GlyphId::new(2);
904 let var_data = gvar.glyph_variation_data(glyph_id).unwrap().unwrap();
905 const COUNT: usize = 8;
906
907 for short in ["flags", "deltas", "iup", "points"] {
909 let points = vec![Point::<i32>::default(); if short == "points" { 3 } else { COUNT }];
910 let mut flags =
911 vec![PointFlags::default(); if short == "flags" { COUNT - 1 } else { COUNT }];
912 let mut deltas =
913 vec![Point::<Fixed>::default(); if short == "deltas" { COUNT - 1 } else { COUNT }];
914 let mut iup =
915 vec![Point::<Fixed>::default(); if short == "iup" { COUNT - 1 } else { COUNT }];
916 let mut buffers = DeltaBuffers {
917 deltas: &mut deltas,
918 iup: &mut iup,
919 };
920 assert!(
921 matches!(
922 gvar.simple_deltas(glyph_id, &[], &points, &mut flags, &[7], &mut buffers),
923 Err(ReadError::InvalidArrayLen)
924 ),
925 "Gvar accepted a short {short}"
926 );
927 assert!(
928 matches!(
929 var_data.simple_deltas(&[], &points, &mut flags, &[7], &mut buffers),
930 Err(ReadError::InvalidArrayLen)
931 ),
932 "GlyphVariationData accepted a short {short}"
933 );
934 }
935
936 let points = vec![Point::<i32>::default(); COUNT];
938 let mut flags = vec![PointFlags::default(); COUNT];
939 let mut deltas = vec![Point::<Fixed>::default(); COUNT];
940 let mut iup = vec![Point::<Fixed>::default(); COUNT];
941 let mut buffers = DeltaBuffers {
942 deltas: &mut deltas,
943 iup: &mut iup,
944 };
945 assert!(gvar
946 .simple_deltas(glyph_id, &[], &points, &mut flags, &[7], &mut buffers)
947 .is_ok());
948 }
949}