Skip to main content

read_fonts/tables/
variations.rs

1//! OpenType font variations common tables.
2
3include!("../../generated/generated_variations.rs");
4
5use super::{
6    glyf::{PointCoord, PointFlags, PointMarker},
7    gvar::GlyphDelta,
8};
9
10pub const NO_VARIATION_INDEX: u32 = 0xFFFFFFFF;
11/// Outer and inner indices for reading from an [ItemVariationStore].
12#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
13pub struct DeltaSetIndex {
14    /// Outer delta set index.
15    pub outer: u16,
16    /// Inner delta set index.
17    pub inner: u16,
18}
19
20impl DeltaSetIndex {
21    pub const NO_VARIATION_INDEX: Self = Self {
22        outer: (NO_VARIATION_INDEX >> 16) as u16,
23        inner: (NO_VARIATION_INDEX & 0xFFFF) as u16,
24    };
25}
26
27#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
28#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
29pub struct TupleIndex(u16);
30
31impl TupleIndex {
32    /// Flag indicating that this tuple variation header includes an embedded
33    /// peak tuple record, immediately after the tupleIndex field.
34    ///
35    /// If set, the low 12 bits of the tupleIndex value are ignored.
36    ///
37    /// Note that this must always be set within the 'cvar' table.
38    pub const EMBEDDED_PEAK_TUPLE: u16 = 0x8000;
39
40    /// Flag indicating that this tuple variation table applies to an
41    /// intermediate region within the variation space.
42    ///
43    /// If set, the header includes the two intermediate-region, start and end
44    /// tuple records, immediately after the peak tuple record (if present).
45    pub const INTERMEDIATE_REGION: u16 = 0x4000;
46    /// Flag indicating that the serialized data for this tuple variation table
47    /// includes packed “point” number data.
48    ///
49    /// If set, this tuple variation table uses that number data; if clear,
50    /// this tuple variation table uses shared number data found at the start
51    /// of the serialized data for this glyph variation data or 'cvar' table.
52    pub const PRIVATE_POINT_NUMBERS: u16 = 0x2000;
53    //0x1000	Reserved	Reserved for future use — set to 0.
54    //
55    /// Mask for the low 12 bits to give the shared tuple records index.
56    pub const TUPLE_INDEX_MASK: u16 = 0x0FFF;
57
58    #[inline(always)]
59    fn tuple_len(self, axis_count: u16, flag: usize) -> usize {
60        if flag == 0 {
61            self.embedded_peak_tuple() as usize * axis_count as usize
62        } else {
63            self.intermediate_region() as usize * axis_count as usize
64        }
65    }
66
67    pub fn bits(self) -> u16 {
68        self.0
69    }
70
71    pub fn from_bits(bits: u16) -> Self {
72        TupleIndex(bits)
73    }
74
75    /// `true` if the header includes an embedded peak tuple.
76    pub fn embedded_peak_tuple(self) -> bool {
77        (self.0 & Self::EMBEDDED_PEAK_TUPLE) != 0
78    }
79
80    /// `true` if the header includes the two intermediate region tuple records.
81    pub fn intermediate_region(self) -> bool {
82        (self.0 & Self::INTERMEDIATE_REGION) != 0
83    }
84
85    /// `true` if the data for this table includes packed point number data.
86    pub fn private_point_numbers(self) -> bool {
87        (self.0 & Self::PRIVATE_POINT_NUMBERS) != 0
88    }
89
90    pub fn tuple_records_index(self) -> Option<u16> {
91        (!self.embedded_peak_tuple()).then_some(self.0 & Self::TUPLE_INDEX_MASK)
92    }
93}
94
95impl types::Scalar for TupleIndex {
96    type Raw = <u16 as types::Scalar>::Raw;
97    fn to_raw(self) -> Self::Raw {
98        self.0.to_raw()
99    }
100    fn from_raw(raw: Self::Raw) -> Self {
101        let t = <u16>::from_raw(raw);
102        Self(t)
103    }
104}
105
106/// The 'tupleVariationCount' field of the [Tuple Variation Store Header][header]
107///
108/// The high 4 bits are flags, and the low 12 bits are the number of tuple
109/// variation tables for this glyph. The count can be any number between 1 and 4095.
110///
111/// [header]: https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#tuple-variation-store-header
112#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
113#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
114pub struct TupleVariationCount(u16);
115
116impl TupleVariationCount {
117    /// Flag indicating that some or all tuple variation tables reference a
118    /// shared set of “point” numbers.
119    ///
120    /// These shared numbers are represented as packed point number data at the
121    /// start of the serialized data.
122    pub const SHARED_POINT_NUMBERS: u16 = 0x8000;
123
124    /// Mask for the low 12 bits to give the shared tuple records index.
125    pub const COUNT_MASK: u16 = 0x0FFF;
126
127    pub fn bits(self) -> u16 {
128        self.0
129    }
130
131    pub fn from_bits(bits: u16) -> Self {
132        Self(bits)
133    }
134
135    /// `true` if any tables reference a shared set of point numbers
136    pub fn shared_point_numbers(self) -> bool {
137        (self.0 & Self::SHARED_POINT_NUMBERS) != 0
138    }
139
140    pub fn count(self) -> u16 {
141        self.0 & Self::COUNT_MASK
142    }
143}
144
145impl types::Scalar for TupleVariationCount {
146    type Raw = <u16 as types::Scalar>::Raw;
147    fn to_raw(self) -> Self::Raw {
148        self.0.to_raw()
149    }
150    fn from_raw(raw: Self::Raw) -> Self {
151        let t = <u16>::from_raw(raw);
152        Self(t)
153    }
154}
155
156impl<'a> TupleVariationHeader<'a> {
157    /// Peak tuple record for this tuple variation table — optional,
158    /// determined by flags in the tupleIndex value.  Note that this
159    /// must always be included in the 'cvar' table.
160    #[inline(always)]
161    pub fn peak_tuple(&self) -> Option<Tuple<'a>> {
162        self.tuple_index().embedded_peak_tuple().then(|| {
163            let range = self.peak_tuple_byte_range();
164            Tuple {
165                values: self.offset_data().read_array(range).unwrap(),
166            }
167        })
168    }
169
170    /// Intermediate start tuple record for this tuple variation table
171    /// — optional, determined by flags in the tupleIndex value.
172    #[inline(always)]
173    pub fn intermediate_start_tuple(&self) -> Option<Tuple<'a>> {
174        self.tuple_index().intermediate_region().then(|| {
175            let range = self.intermediate_start_tuple_byte_range();
176            Tuple {
177                values: self.offset_data().read_array(range).unwrap(),
178            }
179        })
180    }
181
182    /// Intermediate end tuple record for this tuple variation table
183    /// — optional, determined by flags in the tupleIndex value.
184    #[inline(always)]
185    pub fn intermediate_end_tuple(&self) -> Option<Tuple<'a>> {
186        self.tuple_index().intermediate_region().then(|| {
187            let range = self.intermediate_end_tuple_byte_range();
188            Tuple {
189                values: self.offset_data().read_array(range).unwrap(),
190            }
191        })
192    }
193
194    /// Intermediate tuple records for this tuple variation table
195    /// — optional, determined by flags in the tupleIndex value.
196    #[inline(always)]
197    pub fn intermediate_tuples(&self) -> Option<(Tuple<'a>, Tuple<'a>)> {
198        self.tuple_index().intermediate_region().then(|| {
199            let start_range = self.intermediate_start_tuple_byte_range();
200            let end_range = self.intermediate_end_tuple_byte_range();
201            (
202                Tuple {
203                    values: self.offset_data().read_array(start_range).unwrap(),
204                },
205                Tuple {
206                    values: self.offset_data().read_array(end_range).unwrap(),
207                },
208            )
209        })
210    }
211
212    /// Compute the actual length of this table in bytes
213    #[inline(always)]
214    fn byte_len(&self, axis_count: u16) -> usize {
215        const FIXED_LEN: usize = u16::RAW_BYTE_LEN + TupleIndex::RAW_BYTE_LEN;
216        let tuple_byte_len = F2Dot14::RAW_BYTE_LEN * axis_count as usize;
217        let index = self.tuple_index();
218        FIXED_LEN
219            + if index.embedded_peak_tuple() {
220                tuple_byte_len
221            } else {
222                Default::default()
223            }
224            + if index.intermediate_region() {
225                tuple_byte_len * 2
226            } else {
227                Default::default()
228            }
229    }
230}
231
232impl Tuple<'_> {
233    pub fn len(&self) -> usize {
234        self.values().len()
235    }
236
237    pub fn is_empty(&self) -> bool {
238        self.values.is_empty()
239    }
240
241    #[inline(always)]
242    pub fn get(&self, idx: usize) -> Option<F2Dot14> {
243        self.values.get(idx).map(BigEndian::get)
244    }
245}
246
247//FIXME: add an #[extra_traits(..)] attribute!
248#[allow(clippy::derivable_impls)]
249impl Default for Tuple<'_> {
250    fn default() -> Self {
251        Self {
252            values: Default::default(),
253        }
254    }
255}
256
257/// [Packed "Point" Numbers](https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#packed-point-numbers)
258#[derive(Clone, Default, Debug)]
259pub struct PackedPointNumbers<'a> {
260    data: FontData<'a>,
261}
262
263impl<'a> PackedPointNumbers<'a> {
264    /// read point numbers off the front of this data, returning the remaining data
265    pub fn split_off_front(data: FontData<'a>) -> (Self, FontData<'a>) {
266        let this = PackedPointNumbers { data };
267        let total_len = this.total_len();
268        let remainder = data.split_off(total_len).unwrap_or_default();
269        (this, remainder)
270    }
271
272    /// The number of points in this set
273    pub fn count(&self) -> u16 {
274        self.count_and_count_bytes().0
275    }
276
277    /// compute the count, and the number of bytes used to store it
278    fn count_and_count_bytes(&self) -> (u16, usize) {
279        match self.data.read_at::<u8>(0).unwrap_or(0) {
280            0 => (0, 1),
281            count @ 1..=127 => (count as u16, 1),
282            _ => {
283                // "If the high bit of the first byte is set, then a second byte is used.
284                // The count is read from interpreting the two bytes as a big-endian
285                // uint16 value with the high-order bit masked out."
286
287                let count = self.data.read_at::<u16>(0).unwrap_or_default() & 0x7FFF;
288                // a weird case where I'm following fonttools: if the 'use words' bit
289                // is set, but the total count is still 0, treat it like 0 first byte
290                if count == 0 {
291                    (0, 2)
292                } else {
293                    (count & 0x7FFF, 2)
294                }
295            }
296        }
297    }
298
299    /// the number of bytes to encode the packed point numbers
300    #[inline(never)]
301    fn total_len(&self) -> usize {
302        let (n_points, mut n_bytes) = self.count_and_count_bytes();
303        if n_points == 0 {
304            return n_bytes;
305        }
306        let mut cursor = self.data.cursor();
307        cursor.advance_by(n_bytes);
308
309        let mut n_seen = 0;
310        while n_seen < n_points {
311            let Some((count, two_bytes)) = read_control_byte(&mut cursor) else {
312                return n_bytes;
313            };
314            let word_size = 1 + usize::from(two_bytes);
315            let run_size = word_size * count as usize;
316            n_bytes += run_size + 1; // plus the control byte;
317            cursor.advance_by(run_size);
318            n_seen += count as u16;
319        }
320
321        n_bytes
322    }
323
324    /// Iterate over the packed points
325    pub fn iter(&self) -> PackedPointNumbersIter<'a> {
326        let (count, n_bytes) = self.count_and_count_bytes();
327        let mut cursor = self.data.cursor();
328        cursor.advance_by(n_bytes);
329        PackedPointNumbersIter::new(count, cursor)
330    }
331}
332
333/// An iterator over the packed point numbers data.
334#[derive(Clone, Debug)]
335pub struct PackedPointNumbersIter<'a> {
336    count: u16,
337    seen: u16,
338    last_val: u16,
339    current_run: PointRunIter<'a>,
340}
341
342impl<'a> PackedPointNumbersIter<'a> {
343    fn new(count: u16, cursor: Cursor<'a>) -> Self {
344        PackedPointNumbersIter {
345            count,
346            seen: 0,
347            last_val: 0,
348            current_run: PointRunIter {
349                remaining: 0,
350                two_bytes: false,
351                cursor,
352            },
353        }
354    }
355}
356
357/// Implements the logic for iterating over the individual runs
358#[derive(Clone, Debug)]
359struct PointRunIter<'a> {
360    remaining: u8,
361    two_bytes: bool,
362    cursor: Cursor<'a>,
363}
364
365impl Iterator for PointRunIter<'_> {
366    type Item = u16;
367
368    fn next(&mut self) -> Option<Self::Item> {
369        // if no items remain in this run, start the next one.
370        while self.remaining == 0 {
371            (self.remaining, self.two_bytes) = read_control_byte(&mut self.cursor)?;
372        }
373
374        self.remaining -= 1;
375        if self.two_bytes {
376            self.cursor.read().ok()
377        } else {
378            self.cursor.read::<u8>().ok().map(|v| v as u16)
379        }
380    }
381}
382
383/// returns the count and the 'uses_two_bytes' flag from the control byte
384fn read_control_byte(cursor: &mut Cursor) -> Option<(u8, bool)> {
385    let control: u8 = cursor.read().ok()?;
386    let two_bytes = (control & 0x80) != 0;
387    let count = (control & 0x7F) + 1;
388    Some((count, two_bytes))
389}
390
391impl Iterator for PackedPointNumbersIter<'_> {
392    type Item = u16;
393
394    fn next(&mut self) -> Option<Self::Item> {
395        // if our count is zero, we keep incrementing forever
396        if self.count == 0 {
397            let result = self.last_val;
398            self.last_val = self.last_val.checked_add(1)?;
399            return Some(result);
400        }
401
402        if self.count == self.seen {
403            return None;
404        }
405        self.seen += 1;
406        self.last_val = self.last_val.checked_add(self.current_run.next()?)?;
407        Some(self.last_val)
408    }
409
410    fn size_hint(&self) -> (usize, Option<usize>) {
411        (self.count as usize, Some(self.count as usize))
412    }
413}
414
415// completely unnecessary?
416impl ExactSizeIterator for PackedPointNumbersIter<'_> {}
417
418/// [Packed Deltas](https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#packed-deltas)
419#[derive(Clone, Debug)]
420pub struct PackedDeltas<'a> {
421    data: FontData<'a>,
422    // How many values we expect
423    count: Option<usize>,
424}
425
426impl<'a> PackedDeltas<'a> {
427    pub(crate) fn new(data: FontData<'a>, count: usize) -> Self {
428        Self {
429            data,
430            count: Some(count),
431        }
432    }
433
434    /// NOTE: this is unbounded, and assumes all of data is deltas.
435    #[doc(hidden)] // used by tests in write-fonts
436    pub fn consume_all(data: FontData<'a>) -> Self {
437        Self { data, count: None }
438    }
439
440    pub fn count(&self) -> Option<usize> {
441        self.count
442    }
443
444    pub fn count_or_compute(&self) -> usize {
445        self.count.unwrap_or_else(|| count_all_deltas(self.data))
446    }
447
448    pub fn iter(&self) -> DeltaRunIter<'a> {
449        DeltaRunIter::new(self.data.cursor(), self.count)
450    }
451
452    pub fn fetcher(&self) -> PackedDeltaFetcher<'a> {
453        PackedDeltaFetcher::new(self.data.as_bytes(), self.count)
454    }
455
456    fn x_deltas(&self) -> DeltaRunIter<'a> {
457        let count = self.count_or_compute() / 2;
458        DeltaRunIter::new(self.data.cursor(), Some(count))
459    }
460
461    fn y_deltas(&self) -> DeltaRunIter<'a> {
462        let count = self.count_or_compute();
463        DeltaRunIter::new(self.data.cursor(), Some(count)).skip_fast(count / 2)
464    }
465}
466
467/// Flag indicating that this run contains no data,
468/// and that the deltas for this run are all zero.
469const DELTAS_ARE_ZERO: u8 = 0x80;
470/// Flag indicating the data type for delta values in the run.
471const DELTAS_ARE_WORDS: u8 = 0x40;
472/// Mask for the low 6 bits to provide the number of delta values in the run, minus one.
473const DELTA_RUN_COUNT_MASK: u8 = 0x3F;
474
475/// The type of values for a given delta run (influences the number of bytes per delta)
476///
477/// The variants are intentionally set to the byte size of the type to allow usage
478/// as a multiplier when computing offsets.
479#[derive(Clone, Copy, Debug, PartialEq)]
480pub enum DeltaRunType {
481    Zero = 0,
482    I8 = 1,
483    I16 = 2,
484    I32 = 4,
485}
486
487impl DeltaRunType {
488    /// The run type for a given control byte
489    pub fn new(control: u8) -> Self {
490        // if the top two bits of the control byte (DELTAS_ARE_ZERO and DELTAS_ARE_WORDS) are both set,
491        // then the following values are 32-bit.
492        // <https://github.com/harfbuzz/boring-expansion-spec/blob/main/VARC.md#tuplevalues>
493        let are_zero = (control & DELTAS_ARE_ZERO) != 0;
494        let are_words = (control & DELTAS_ARE_WORDS) != 0;
495        match (are_zero, are_words) {
496            (false, false) => Self::I8,
497            (false, true) => Self::I16,
498            (true, false) => Self::Zero,
499            (true, true) => Self::I32,
500        }
501    }
502}
503
504/// Implements the logic for iterating over the individual runs
505#[derive(Clone, Debug)]
506pub struct DeltaRunIter<'a> {
507    limit: Option<usize>, // when None, consume all available data
508    remaining_in_run: u8,
509    value_type: DeltaRunType,
510    cursor: Cursor<'a>,
511}
512
513/// A decoding helper that adds packed deltas directly to an output slice.
514pub struct PackedDeltaFetcher<'a> {
515    data: &'a [u8],
516    pos: usize,
517    end: usize,
518    run_count: usize,
519    value_type: DeltaRunType,
520    remaining_total: Option<usize>,
521}
522
523impl<'a> PackedDeltaFetcher<'a> {
524    fn new(data: &'a [u8], count: Option<usize>) -> Self {
525        Self {
526            data,
527            pos: 0,
528            end: data.len(),
529            run_count: 0,
530            value_type: DeltaRunType::I8,
531            remaining_total: count,
532        }
533    }
534
535    #[inline(always)]
536    fn ensure_run(&mut self) -> Result<(), ReadError> {
537        if self.run_count > 0 {
538            return Ok(());
539        }
540        if self.pos >= self.end {
541            return Err(ReadError::OutOfBounds);
542        }
543        let control = self.data[self.pos];
544        self.pos += 1;
545        self.run_count = (control & DELTA_RUN_COUNT_MASK) as usize + 1;
546        self.value_type = DeltaRunType::new(control);
547        let width = self.value_type as usize;
548        let needed = self.run_count * width;
549        if self.pos + needed > self.end {
550            return Err(ReadError::OutOfBounds);
551        }
552        Ok(())
553    }
554
555    pub fn skip(&mut self, mut n: usize) -> Result<(), ReadError> {
556        if let Some(remaining_total) = self.remaining_total {
557            if n > remaining_total {
558                return Err(ReadError::OutOfBounds);
559            }
560            self.remaining_total = Some(remaining_total - n);
561        }
562        while n > 0 {
563            self.ensure_run()?;
564            let take = n.min(self.run_count);
565            let width = self.value_type as usize;
566            self.pos += take * width;
567            self.run_count -= take;
568            n -= take;
569        }
570        Ok(())
571    }
572
573    pub fn add_to_f32_scaled(&mut self, out: &mut [f32], scale: f32) -> Result<(), ReadError> {
574        self.add_to_scaled(out, scale, |delta| delta as f32)
575    }
576
577    /// Adds packed deltas without losing precision from 32-bit integer values.
578    pub fn add_to_f64_scaled(&mut self, out: &mut [f64], scale: f64) -> Result<(), ReadError> {
579        self.add_to_scaled(out, scale, |delta| delta as f64)
580    }
581
582    fn add_to_scaled<T>(
583        &mut self,
584        out: &mut [T],
585        scale: T,
586        convert: impl Fn(i32) -> T,
587    ) -> Result<(), ReadError>
588    where
589        T: Copy + core::ops::AddAssign + core::ops::Mul<Output = T>,
590    {
591        let mut remaining = out.len();
592        if let Some(remaining_total) = self.remaining_total {
593            if remaining > remaining_total {
594                return Err(ReadError::OutOfBounds);
595            }
596            self.remaining_total = Some(remaining_total - remaining);
597        }
598        let mut idx = 0usize;
599        while remaining > 0 {
600            self.ensure_run()?;
601            let take = remaining.min(self.run_count);
602            match self.value_type {
603                DeltaRunType::Zero => {
604                    // nothing to add
605                    idx += take;
606                }
607                DeltaRunType::I8 => {
608                    let bytes = &self.data[self.pos..self.pos + take];
609                    for &b in bytes {
610                        out[idx] += convert(b as i8 as i32) * scale;
611                        idx += 1;
612                    }
613                    self.pos += take;
614                }
615                DeltaRunType::I16 => {
616                    let bytes = &self.data[self.pos..self.pos + take * 2];
617                    for chunk in bytes.chunks_exact(2) {
618                        let delta = i16::from_be_bytes([chunk[0], chunk[1]]) as i32;
619                        out[idx] += convert(delta) * scale;
620                        idx += 1;
621                    }
622                    self.pos += take * 2;
623                }
624                DeltaRunType::I32 => {
625                    let bytes = &self.data[self.pos..self.pos + take * 4];
626                    for chunk in bytes.chunks_exact(4) {
627                        let delta = i32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
628                        out[idx] += convert(delta) * scale;
629                        idx += 1;
630                    }
631                    self.pos += take * 4;
632                }
633            }
634            self.run_count -= take;
635            remaining -= take;
636        }
637        Ok(())
638    }
639}
640
641/// Counts the number of deltas available in the given data, avoiding
642/// excessive reads.
643fn count_all_deltas(data: FontData) -> usize {
644    let mut count = 0;
645    let mut offset = 0;
646    while let Ok(control) = data.read_at::<u8>(offset) {
647        let run_count = (control & DELTA_RUN_COUNT_MASK) as usize + 1;
648        count += run_count;
649        offset += run_count * DeltaRunType::new(control) as usize + 1;
650    }
651    count
652}
653
654impl<'a> DeltaRunIter<'a> {
655    fn new(cursor: Cursor<'a>, limit: Option<usize>) -> Self {
656        DeltaRunIter {
657            limit,
658            remaining_in_run: 0,
659            value_type: DeltaRunType::I8,
660            cursor,
661        }
662    }
663
664    pub(crate) fn end(mut self) -> Cursor<'a> {
665        if let Some(limit) = self.limit {
666            return self.skip_fast(limit).cursor;
667        }
668        // No limit: jump over runs without decoding values.
669        if self.remaining_in_run != 0 {
670            if self.value_type != DeltaRunType::Zero {
671                self.cursor
672                    .advance_by(self.remaining_in_run as usize * self.value_type as usize);
673            }
674            self.remaining_in_run = 0;
675        }
676        while self.read_next_control().is_some() {
677            if self.value_type != DeltaRunType::Zero {
678                self.cursor
679                    .advance_by(self.remaining_in_run as usize * self.value_type as usize);
680            }
681            self.remaining_in_run = 0;
682        }
683        self.cursor
684    }
685
686    /// Skips `n` deltas without reading the actual delta values.
687    #[inline(always)]
688    pub fn skip_fast(mut self, n: usize) -> Self {
689        let mut wanted = n;
690        let mut remaining = self.remaining_in_run as usize;
691        let mut value_type = self.value_type;
692        loop {
693            if wanted > remaining {
694                // Consume the rest of this run and move to the next.
695                self.cursor.advance_by(remaining * value_type as usize);
696                wanted -= remaining;
697                if self.read_next_control().is_none() {
698                    self.limit = Some(0);
699                    break;
700                }
701                remaining = self.remaining_in_run as usize;
702                value_type = self.value_type;
703                continue;
704            }
705            let consumed = wanted;
706            self.remaining_in_run -= consumed as u8;
707            self.cursor.advance_by(consumed * value_type as usize);
708            if let Some(limit) = self.limit.as_mut() {
709                *limit = limit.saturating_sub(n);
710            }
711            break;
712        }
713        self
714    }
715
716    #[inline(always)]
717    fn read_next_control(&mut self) -> Option<()> {
718        self.remaining_in_run = 0;
719        let control: u8 = self.cursor.read().ok()?;
720        self.value_type = DeltaRunType::new(control);
721        self.remaining_in_run = (control & DELTA_RUN_COUNT_MASK) + 1;
722        Some(())
723    }
724}
725
726impl Iterator for DeltaRunIter<'_> {
727    type Item = i32;
728
729    #[inline(always)]
730    fn next(&mut self) -> Option<Self::Item> {
731        if let Some(limit) = self.limit {
732            if limit == 0 {
733                return None;
734            }
735            self.limit = Some(limit - 1);
736        }
737        if self.remaining_in_run == 0 {
738            self.read_next_control()?;
739        }
740        self.remaining_in_run -= 1;
741        match self.value_type {
742            DeltaRunType::Zero => Some(0),
743            DeltaRunType::I8 => self.cursor.read::<i8>().ok().map(|v| v as i32),
744            DeltaRunType::I16 => self.cursor.read::<i16>().ok().map(|v| v as i32),
745            DeltaRunType::I32 => self.cursor.read::<i32>().ok(),
746        }
747    }
748}
749
750/// A helper type for iterating over [`TupleVariationHeader`]s.
751pub struct TupleVariationHeaderIter<'a> {
752    data: FontData<'a>,
753    n_headers: usize,
754    current: usize,
755    axis_count: u16,
756}
757
758impl<'a> TupleVariationHeaderIter<'a> {
759    pub(crate) fn new(data: FontData<'a>, n_headers: usize, axis_count: u16) -> Self {
760        Self {
761            data,
762            n_headers,
763            current: 0,
764            axis_count,
765        }
766    }
767}
768
769impl<'a> Iterator for TupleVariationHeaderIter<'a> {
770    type Item = Result<TupleVariationHeader<'a>, ReadError>;
771
772    #[inline(always)]
773    fn next(&mut self) -> Option<Self::Item> {
774        if self.current == self.n_headers {
775            return None;
776        }
777        self.current += 1;
778        let next = TupleVariationHeader::read(self.data, self.axis_count);
779
780        let next_len = next
781            .as_ref()
782            .map(|table| table.byte_len(self.axis_count))
783            .unwrap_or(0);
784        self.data = self.data.split_off(next_len)?;
785        Some(next)
786    }
787}
788
789#[derive(Clone)]
790pub struct TupleVariationData<'a, T> {
791    pub(crate) axis_count: u16,
792    pub(crate) shared_tuples: Option<ComputedArray<'a, Tuple<'a>>>,
793    pub(crate) shared_point_numbers: Option<PackedPointNumbers<'a>>,
794    pub(crate) tuple_count: TupleVariationCount,
795    // the data for all the tuple variation headers
796    pub(crate) header_data: FontData<'a>,
797    // the data for all the tuple bodies
798    pub(crate) serialized_data: FontData<'a>,
799    pub(crate) _marker: std::marker::PhantomData<fn() -> T>,
800}
801
802impl<'a, T> TupleVariationData<'a, T>
803where
804    T: TupleDelta,
805{
806    pub fn tuples(&self) -> TupleVariationIter<'a, T> {
807        TupleVariationIter {
808            current: 0,
809            parent: self.clone(),
810            header_iter: TupleVariationHeaderIter::new(
811                self.header_data,
812                self.tuple_count.count() as usize,
813                self.axis_count,
814            ),
815            serialized_data: self.serialized_data,
816            _marker: std::marker::PhantomData,
817        }
818    }
819
820    /// Returns an iterator over all of the pairs of (variation tuple, scalar)
821    /// for this glyph that are active for the given set of normalized
822    /// coordinates.
823    pub fn active_tuples_at<'b>(
824        &self,
825        coords: &'b [F2Dot14],
826    ) -> impl Iterator<Item = (TupleVariation<'a, T>, Fixed)> + 'b
827    where
828        'a: 'b,
829    {
830        self.active_tuples_at_with_scalars(coords, &[])
831    }
832
833    /// Returns an iterator over the tuples that apply at `coords`, reusing
834    /// `scalars`.
835    ///
836    /// `scalars` holds one entry per shared tuple, from
837    /// [`Gvar::compute_scalars`]. A tuple that names a shared peak and no
838    /// range of its own has the same scalar everywhere, so it is read from
839    /// there. A tuple with its own range, or one `scalars` does not reach, is
840    /// computed here.
841    ///
842    /// [`Gvar::compute_scalars`]: crate::tables::gvar::Gvar::compute_scalars
843    pub fn active_tuples_at_with_scalars<'b>(
844        &self,
845        coords: &'b [F2Dot14],
846        scalars: &'b [Fixed],
847    ) -> impl Iterator<Item = (TupleVariation<'a, T>, Fixed)> + 'b
848    where
849        'a: 'b,
850    {
851        ActiveTupleVariationIter {
852            coords,
853            scalars,
854            parent: self.clone(),
855            header_iter: TupleVariationHeaderIter::new(
856                self.header_data,
857                self.tuple_count.count() as usize,
858                self.axis_count,
859            ),
860            serialized_data: self.serialized_data,
861            data_offset: 0,
862            _marker: std::marker::PhantomData,
863        }
864    }
865
866    pub(crate) fn tuple_count(&self) -> usize {
867        self.tuple_count.count() as usize
868    }
869}
870
871/// An iterator over the [`TupleVariation`]s for a specific glyph.
872pub struct TupleVariationIter<'a, T> {
873    current: usize,
874    parent: TupleVariationData<'a, T>,
875    header_iter: TupleVariationHeaderIter<'a>,
876    serialized_data: FontData<'a>,
877    _marker: std::marker::PhantomData<fn() -> T>,
878}
879
880impl<'a, T> TupleVariationIter<'a, T>
881where
882    T: TupleDelta,
883{
884    #[inline(always)]
885    fn next_tuple(&mut self) -> Option<TupleVariation<'a, T>> {
886        if self.parent.tuple_count() == self.current {
887            return None;
888        }
889        self.current += 1;
890
891        // FIXME: is it okay to discard an error here?
892        let header = self.header_iter.next()?.ok()?;
893        let data_len = header.variation_data_size() as usize;
894        let var_data = self.serialized_data.take_up_to(data_len)?;
895
896        Some(TupleVariation {
897            axis_count: self.parent.axis_count,
898            header,
899            shared_tuples: self.parent.shared_tuples.clone(),
900            serialized_data: var_data,
901            shared_point_numbers: self.parent.shared_point_numbers.clone(),
902            _marker: std::marker::PhantomData,
903        })
904    }
905}
906
907impl<'a, T> Iterator for TupleVariationIter<'a, T>
908where
909    T: TupleDelta,
910{
911    type Item = TupleVariation<'a, T>;
912
913    #[inline(always)]
914    fn next(&mut self) -> Option<Self::Item> {
915        self.next_tuple()
916    }
917}
918
919/// An iterator over the active [`TupleVariation`]s for a specific glyph
920/// for a given set of coordinates.
921struct ActiveTupleVariationIter<'a, 'b, T> {
922    coords: &'b [F2Dot14],
923    scalars: &'b [Fixed],
924    parent: TupleVariationData<'a, T>,
925    header_iter: TupleVariationHeaderIter<'a>,
926    serialized_data: FontData<'a>,
927    data_offset: usize,
928    _marker: std::marker::PhantomData<fn() -> T>,
929}
930
931impl<'a, T> Iterator for ActiveTupleVariationIter<'a, '_, T>
932where
933    T: TupleDelta,
934{
935    type Item = (TupleVariation<'a, T>, Fixed);
936
937    #[inline(always)]
938    fn next(&mut self) -> Option<Self::Item> {
939        loop {
940            let header = self.header_iter.next()?.ok()?;
941            let data_len = header.variation_data_size() as usize;
942            let data_start = self.data_offset;
943            let data_end = data_start.checked_add(data_len)?;
944            self.data_offset = data_end;
945            if let Some(scalar) = compute_scalar(
946                &header,
947                self.parent.axis_count as usize,
948                &self.parent.shared_tuples,
949                self.coords,
950                self.scalars,
951            ) {
952                let var_data = self.serialized_data.slice(data_start..data_end)?;
953                return Some((
954                    TupleVariation {
955                        axis_count: self.parent.axis_count,
956                        header,
957                        shared_tuples: self.parent.shared_tuples.clone(),
958                        serialized_data: var_data,
959                        shared_point_numbers: self.parent.shared_point_numbers.clone(),
960                        _marker: std::marker::PhantomData,
961                    },
962                    scalar,
963                ));
964            }
965        }
966    }
967}
968
969/// Marks a shared tuple scalar that was never computed.
970///
971/// A glyph names shared tuples by index and need not name them all, so a
972/// slice of scalars can carry gaps. Variation store regions are dense and
973/// need no such mark.
974///
975/// No computed scalar can equal this: a scalar is a product of ratios, each
976/// held between zero and one.
977pub const NOT_COMPUTED: Fixed = Fixed::MAX;
978
979/// A single set of tuple variation data
980#[derive(Clone)]
981pub struct TupleVariation<'a, T> {
982    axis_count: u16,
983    header: TupleVariationHeader<'a>,
984    shared_tuples: Option<ComputedArray<'a, Tuple<'a>>>,
985    serialized_data: FontData<'a>,
986    shared_point_numbers: Option<PackedPointNumbers<'a>>,
987    _marker: std::marker::PhantomData<fn() -> T>,
988}
989
990impl<'a, T> TupleVariation<'a, T>
991where
992    T: TupleDelta,
993{
994    /// Returns true if this tuple provides deltas for all points in a glyph.
995    pub fn has_deltas_for_all_points(&self) -> bool {
996        if self.header.tuple_index().private_point_numbers() {
997            PackedPointNumbers {
998                data: self.serialized_data,
999            }
1000            .count()
1001                == 0
1002        } else if let Some(shared) = &self.shared_point_numbers {
1003            shared.count() == 0
1004        } else {
1005            false
1006        }
1007    }
1008
1009    pub fn point_numbers(&self) -> PackedPointNumbersIter<'a> {
1010        let (point_numbers, _) = self.point_numbers_and_packed_deltas();
1011        point_numbers.iter()
1012    }
1013
1014    /// Returns the 'peak' tuple for this variation
1015    pub fn peak(&self) -> Tuple<'a> {
1016        self.header
1017            .tuple_index()
1018            .tuple_records_index()
1019            .and_then(|idx| self.shared_tuples.as_ref()?.get(idx as usize).ok())
1020            .or_else(|| self.header.peak_tuple())
1021            .unwrap_or_default()
1022    }
1023
1024    pub fn intermediate_start(&self) -> Option<Tuple<'a>> {
1025        self.header.intermediate_start_tuple()
1026    }
1027
1028    pub fn intermediate_end(&self) -> Option<Tuple<'a>> {
1029        self.header.intermediate_end_tuple()
1030    }
1031
1032    /// Compute the fixed point scalar for this tuple at the given location in
1033    /// variation space.
1034    ///
1035    /// The `coords` slice must be of lesser or equal length to the number of
1036    /// axes. If it is less, missing (trailing) axes will be assumed to have
1037    /// zero values.
1038    ///
1039    /// Returns `None` if this tuple is not applicable at the provided
1040    /// coordinates (e.g. if the resulting scalar is zero).
1041    pub fn compute_scalar(&self, coords: &[F2Dot14]) -> Option<Fixed> {
1042        compute_scalar(
1043            &self.header,
1044            self.axis_count as usize,
1045            &self.shared_tuples,
1046            coords,
1047            &[],
1048        )
1049    }
1050
1051    /// Compute the floating point scalar for this tuple at the given location
1052    /// in variation space.
1053    ///
1054    /// The `coords` slice must be of lesser or equal length to the number of
1055    /// axes. If it is less, missing (trailing) axes will be assumed to have
1056    /// zero values.
1057    ///
1058    /// Returns `None` if this tuple is not applicable at the provided
1059    /// coordinates (e.g. if the resulting scalar is zero).
1060    pub fn compute_scalar_f32(&self, coords: &[F2Dot14]) -> Option<f32> {
1061        let mut scalar = 1.0;
1062        let peak = self.peak();
1063        let inter_start = self.header.intermediate_start_tuple();
1064        let inter_end = self.header.intermediate_end_tuple();
1065        if peak.len() != self.axis_count as usize {
1066            return None;
1067        }
1068        for i in 0..self.axis_count {
1069            let i = i as usize;
1070            let coord = coords.get(i).copied().unwrap_or_default().to_bits() as i32;
1071            let peak = peak.get(i).unwrap_or_default().to_bits() as i32;
1072            if peak == 0 || peak == coord {
1073                continue;
1074            }
1075            if coord == 0 {
1076                return None;
1077            }
1078            if let (Some(inter_start), Some(inter_end)) = (&inter_start, &inter_end) {
1079                let start = inter_start.get(i).unwrap_or_default().to_bits() as i32;
1080                let end = inter_end.get(i).unwrap_or_default().to_bits() as i32;
1081                if start > peak || peak > end || (start < 0 && end > 0 && peak != 0) {
1082                    continue;
1083                }
1084                if coord < start || coord > end {
1085                    return None;
1086                }
1087                if coord < peak {
1088                    if peak != start {
1089                        scalar *= (coord - start) as f32 / (peak - start) as f32;
1090                    }
1091                } else if peak != end {
1092                    scalar *= (end - coord) as f32 / (end - peak) as f32;
1093                }
1094            } else {
1095                if coord < peak.min(0) || coord > peak.max(0) {
1096                    return None;
1097                }
1098                scalar *= coord as f32 / peak as f32;
1099            }
1100        }
1101        Some(scalar)
1102    }
1103
1104    /// Iterate over the deltas for this tuple.
1105    ///
1106    /// This does not account for scaling. Returns only explicitly encoded
1107    /// deltas, e.g. an omission by IUP will not be present.
1108    pub fn deltas(&self) -> TupleDeltaIter<'a, T> {
1109        let (point_numbers, packed_deltas) = self.point_numbers_and_packed_deltas();
1110        let count = point_numbers.count() as usize;
1111        let packed_deltas = if count == 0 {
1112            PackedDeltas::consume_all(packed_deltas)
1113        } else {
1114            PackedDeltas::new(packed_deltas, if T::is_point() { count * 2 } else { count })
1115        };
1116        TupleDeltaIter::new(&point_numbers, packed_deltas)
1117    }
1118
1119    fn point_numbers_and_packed_deltas(&self) -> (PackedPointNumbers<'a>, FontData<'a>) {
1120        if self.header.tuple_index().private_point_numbers() {
1121            PackedPointNumbers::split_off_front(self.serialized_data)
1122        } else {
1123            (
1124                self.shared_point_numbers.clone().unwrap_or_default(),
1125                self.serialized_data,
1126            )
1127        }
1128    }
1129}
1130
1131impl TupleVariation<'_, GlyphDelta> {
1132    /// Reads the set of deltas from this tuple variation.
1133    ///
1134    /// This is significantly faster than using the [`Self::deltas`]
1135    /// method but requires preallocated memory to store deltas and
1136    /// flags.
1137    ///
1138    /// This method should only be used when the tuple variation is dense,
1139    /// that is, [`Self::has_deltas_for_all_points`] returns true.
1140    ///
1141    /// The size of `deltas` must be the same as the target value set to
1142    /// which the variation is applied. For simple outlines, this is
1143    /// `num_points + 4` and for composites it is `num_components + 4`
1144    /// (where the `+ 4` is to accommodate phantom points).
1145    ///
1146    /// The `deltas` slice will not be zeroed before accumulation and each
1147    /// delta will be multiplied by the given `scalar`.
1148    pub fn accumulate_dense_deltas<D: PointCoord>(
1149        &self,
1150        deltas: &mut [Point<D>],
1151        scalar: Fixed,
1152    ) -> Result<(), ReadError> {
1153        let (_, packed_deltas) = self.point_numbers_and_packed_deltas();
1154        let mut cursor = packed_deltas.cursor();
1155        if scalar == Fixed::ONE {
1156            // scalar of 1.0 is common so avoid the costly conversions and
1157            // multiplications per coord
1158            read_dense_deltas(&mut cursor, deltas, |delta, new_delta| {
1159                delta.x += D::from_i32(new_delta);
1160            })?;
1161            read_dense_deltas(&mut cursor, deltas, |delta, new_delta| {
1162                delta.y += D::from_i32(new_delta);
1163            })?;
1164        } else {
1165            read_dense_deltas(&mut cursor, deltas, |delta, new_delta| {
1166                delta.x += D::from_fixed(Fixed::from_i32(new_delta) * scalar);
1167            })?;
1168            read_dense_deltas(&mut cursor, deltas, |delta, new_delta| {
1169                delta.y += D::from_fixed(Fixed::from_i32(new_delta) * scalar);
1170            })?;
1171        }
1172        Ok(())
1173    }
1174
1175    /// Reads the set of deltas from this tuple variation.
1176    ///
1177    /// This is significantly faster than using the [`Self::deltas`]
1178    /// method but requires preallocated memory to store deltas and
1179    /// flags.
1180    ///
1181    /// This method should only be used when the tuple variation is sparse,
1182    /// that is, [`Self::has_deltas_for_all_points`] returns false.
1183    ///
1184    /// The size of `deltas` must be the same as the target value set to
1185    /// which the variation is applied. For simple outlines, this is
1186    /// `num_points + 4` and for composites it is `num_components + 4`
1187    /// (where the `+ 4` is to accommodate phantom points).
1188    ///
1189    /// The `deltas` and `flags` slices must be the same size. Modifications
1190    /// to `deltas` will be sparse and for each entry that is modified, the
1191    /// [PointMarker::HAS_DELTA] marker will be set for the corresponding
1192    /// entry in the `flags` slice.
1193    ///
1194    /// The `deltas` slice will not be zeroed before accumulation and each
1195    /// delta will be multiplied by the given `scalar`.
1196    pub fn accumulate_sparse_deltas<D: PointCoord>(
1197        &self,
1198        deltas: &mut [Point<D>],
1199        flags: &mut [PointFlags],
1200        scalar: Fixed,
1201    ) -> Result<(), ReadError> {
1202        let (point_numbers, packed_deltas) = self.point_numbers_and_packed_deltas();
1203        let mut cursor = packed_deltas.cursor();
1204        let count = point_numbers.count() as usize;
1205        if scalar == Fixed::ONE {
1206            // scalar of 1.0 is common so avoid the costly conversions and
1207            // multiplications per coord
1208            read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| {
1209                if let Some((delta, flag)) = deltas.get_mut(ix).zip(flags.get_mut(ix)) {
1210                    delta.x += D::from_i32(new_delta);
1211                    flag.set_marker(PointMarker::HAS_DELTA);
1212                }
1213            })?;
1214            read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| {
1215                if let Some(delta) = deltas.get_mut(ix) {
1216                    delta.y += D::from_i32(new_delta);
1217                }
1218            })?;
1219        } else {
1220            read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| {
1221                if let Some((delta, flag)) = deltas.get_mut(ix).zip(flags.get_mut(ix)) {
1222                    delta.x += D::from_fixed(Fixed::from_i32(new_delta) * scalar);
1223                    flag.set_marker(PointMarker::HAS_DELTA);
1224                }
1225            })?;
1226            read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| {
1227                if let Some(delta) = deltas.get_mut(ix) {
1228                    delta.y += D::from_fixed(Fixed::from_i32(new_delta) * scalar);
1229                }
1230            })?;
1231        }
1232        Ok(())
1233    }
1234}
1235
1236/// This is basically a manually applied loop unswitching optimization
1237/// for reading deltas. It reads each typed run into a slice for processing
1238/// instead of handling each delta individually with all the necessary
1239/// branching that implies.
1240fn read_dense_deltas<T>(
1241    cursor: &mut Cursor,
1242    deltas: &mut [T],
1243    mut f: impl FnMut(&mut T, i32),
1244) -> Result<(), ReadError> {
1245    let count = deltas.len();
1246    let mut cur = 0;
1247    while cur < count {
1248        let control: u8 = cursor.read()?;
1249        let value_type = DeltaRunType::new(control);
1250        let run_count = ((control & DELTA_RUN_COUNT_MASK) + 1) as usize;
1251        let dest = deltas
1252            .get_mut(cur..cur + run_count)
1253            .ok_or(ReadError::OutOfBounds)?;
1254        match value_type {
1255            DeltaRunType::Zero => {}
1256            DeltaRunType::I8 => {
1257                let packed_deltas = cursor.read_array::<i8>(run_count)?;
1258                for (delta, new_delta) in dest.iter_mut().zip(packed_deltas) {
1259                    f(delta, *new_delta as i32);
1260                }
1261            }
1262            DeltaRunType::I16 => {
1263                let packed_deltas = cursor.read_array::<BigEndian<i16>>(run_count)?;
1264                for (delta, new_delta) in dest.iter_mut().zip(packed_deltas) {
1265                    f(delta, new_delta.get() as i32);
1266                }
1267            }
1268            DeltaRunType::I32 => {
1269                let packed_deltas = cursor.read_array::<BigEndian<i32>>(run_count)?;
1270                for (delta, new_delta) in dest.iter_mut().zip(packed_deltas) {
1271                    f(delta, new_delta.get());
1272                }
1273            }
1274        }
1275        cur += run_count;
1276    }
1277    Ok(())
1278}
1279
1280/// See [read_dense_deltas] docs.
1281fn read_sparse_deltas(
1282    cursor: &mut Cursor,
1283    point_numbers: &PackedPointNumbers,
1284    count: usize,
1285    mut f: impl FnMut(usize, i32),
1286) -> Result<(), ReadError> {
1287    let mut cur = 0;
1288    let mut points_iter = point_numbers.iter().map(|ix| ix as usize);
1289    while cur < count {
1290        let control: u8 = cursor.read()?;
1291        let value_type = DeltaRunType::new(control);
1292        let run_count = ((control & DELTA_RUN_COUNT_MASK) + 1) as usize;
1293        match value_type {
1294            DeltaRunType::Zero => {
1295                for _ in 0..run_count {
1296                    let point_ix = points_iter.next().ok_or(ReadError::OutOfBounds)?;
1297                    f(point_ix, 0);
1298                }
1299            }
1300            DeltaRunType::I8 => {
1301                let packed_deltas = cursor.read_array::<i8>(run_count)?;
1302                for (new_delta, point_ix) in packed_deltas.iter().zip(points_iter.by_ref()) {
1303                    f(point_ix, *new_delta as i32);
1304                }
1305            }
1306            DeltaRunType::I16 => {
1307                let packed_deltas = cursor.read_array::<BigEndian<i16>>(run_count)?;
1308                for (new_delta, point_ix) in packed_deltas.iter().zip(points_iter.by_ref()) {
1309                    f(point_ix, new_delta.get() as i32);
1310                }
1311            }
1312            DeltaRunType::I32 => {
1313                let packed_deltas = cursor.read_array::<BigEndian<i32>>(run_count)?;
1314                for (new_delta, point_ix) in packed_deltas.iter().zip(points_iter.by_ref()) {
1315                    f(point_ix, new_delta.get());
1316                }
1317            }
1318        }
1319        cur += run_count;
1320    }
1321    Ok(())
1322}
1323
1324/// Compute the fixed point scalar for this tuple at the given location in
1325/// variation space.
1326///
1327/// The `coords` slice must be of lesser or equal length to the number of
1328/// axes. If it is less, missing (trailing) axes will be assumed to have
1329/// zero values.
1330///
1331/// Returns `None` if this tuple is not applicable at the provided
1332/// coordinates (e.g. if the resulting scalar is zero).
1333#[inline(always)]
1334fn compute_scalar<'a>(
1335    header: &TupleVariationHeader,
1336    axis_count: usize,
1337    shared_tuples: &Option<ComputedArray<'a, Tuple<'a>>>,
1338    coords: &[F2Dot14],
1339    scalars: &[Fixed],
1340) -> Option<Fixed> {
1341    let tuple_idx = header.tuple_index();
1342    let intermediate = header.intermediate_tuples();
1343    let peak = if let Some(shared_index) = tuple_idx.tuple_records_index() {
1344        // A shared peak with no range of its own has the same scalar
1345        // wherever it is named, so a cached value applies.
1346        if intermediate.is_none() {
1347            match scalars.get(shared_index as usize) {
1348                Some(scalar) if *scalar != NOT_COMPUTED => {
1349                    return (*scalar != Fixed::ZERO).then_some(*scalar);
1350                }
1351                _ => {}
1352            }
1353        }
1354        shared_tuples.as_ref()?.get(shared_index as usize).ok()?
1355    } else {
1356        header.peak_tuple()?
1357    };
1358    if peak.len() != axis_count {
1359        return None;
1360    }
1361    scalar_for(&peak, intermediate, coords)
1362}
1363
1364/// Computes the scalar for a peak at `coords`, interpolating over the tuple's
1365/// range if it states one.
1366///
1367/// `None` where the tuple does not apply, which a scalar of zero also
1368/// expresses.
1369pub(crate) fn scalar_for(
1370    peak: &Tuple,
1371    intermediate: Option<(Tuple, Tuple)>,
1372    coords: &[F2Dot14],
1373) -> Option<Fixed> {
1374    let mut scalar = Fixed::ONE;
1375    for (i, peak) in peak
1376        .values
1377        .iter()
1378        .enumerate()
1379        .filter(|(_, peak)| peak.get() != F2Dot14::ZERO)
1380    {
1381        let coord = coords.get(i).copied().unwrap_or_default();
1382        if coord == F2Dot14::ZERO {
1383            return None;
1384        }
1385        let peak = peak.get();
1386        if peak == coord {
1387            continue;
1388        }
1389        if let Some((inter_start, inter_end)) = &intermediate {
1390            let start = inter_start.get(i).unwrap_or_default();
1391            let end = inter_end.get(i).unwrap_or_default();
1392            // A region that does not enclose its own peak, or that straddles
1393            // the default, is invalid and contributes a factor of one. This
1394            // mirrors the OpenType algorithm and the sibling scalar
1395            // computations (`compute_scalar_f32`, `VariationRegion::compute_scalar`);
1396            // without it a malformed region interpolates where it should be
1397            // skipped.
1398            if start > peak || peak > end || (start < F2Dot14::ZERO && end > F2Dot14::ZERO) {
1399                continue;
1400            }
1401            if coord <= start || coord >= end {
1402                return None;
1403            }
1404            let coord = coord.to_fixed();
1405            let peak = peak.to_fixed();
1406            if coord < peak {
1407                let start = start.to_fixed();
1408                scalar = scalar.mul_div(coord - start, peak - start);
1409            } else {
1410                let end = end.to_fixed();
1411                scalar = scalar.mul_div(end - coord, end - peak);
1412            }
1413        } else {
1414            if coord < peak.min(F2Dot14::ZERO) || coord > peak.max(F2Dot14::ZERO) {
1415                return None;
1416            }
1417            let coord = coord.to_fixed();
1418            let peak = peak.to_fixed();
1419            scalar = scalar.mul_div(coord, peak);
1420        }
1421    }
1422    (scalar != Fixed::ZERO).then_some(scalar)
1423}
1424
1425#[derive(Clone, Debug)]
1426enum TupleDeltaValues<'a> {
1427    // Point deltas have separate runs for x and y coordinates.
1428    Points(DeltaRunIter<'a>, DeltaRunIter<'a>),
1429    Scalars(DeltaRunIter<'a>),
1430}
1431
1432/// An iterator over the deltas for a glyph.
1433#[derive(Clone, Debug)]
1434pub struct TupleDeltaIter<'a, T> {
1435    pub cur: usize,
1436    // if None all points get deltas, if Some specifies subset of points that do
1437    points: Option<PackedPointNumbersIter<'a>>,
1438    next_point: usize,
1439    values: TupleDeltaValues<'a>,
1440    _marker: std::marker::PhantomData<fn() -> T>,
1441}
1442
1443impl<'a, T> TupleDeltaIter<'a, T>
1444where
1445    T: TupleDelta,
1446{
1447    fn new(points: &PackedPointNumbers<'a>, deltas: PackedDeltas<'a>) -> TupleDeltaIter<'a, T> {
1448        let mut points = points.iter();
1449        let next_point = points.next();
1450        let values = if T::is_point() {
1451            TupleDeltaValues::Points(deltas.x_deltas(), deltas.y_deltas())
1452        } else {
1453            TupleDeltaValues::Scalars(deltas.iter())
1454        };
1455        TupleDeltaIter {
1456            cur: 0,
1457            points: next_point.map(|_| points),
1458            next_point: next_point.unwrap_or_default() as usize,
1459            values,
1460            _marker: std::marker::PhantomData,
1461        }
1462    }
1463}
1464
1465/// Trait for deltas that are computed in a tuple variation store.
1466pub trait TupleDelta: Sized + Copy + 'static {
1467    /// Returns true if the delta is a point and requires reading two values
1468    /// from the packed delta stream.
1469    fn is_point() -> bool;
1470
1471    /// Creates a new delta for the given position and coordinates. If
1472    /// the delta is not a point, the y value will always be zero.
1473    fn new(position: u16, x: i32, y: i32) -> Self;
1474}
1475
1476impl<T> Iterator for TupleDeltaIter<'_, T>
1477where
1478    T: TupleDelta,
1479{
1480    type Item = T;
1481
1482    fn next(&mut self) -> Option<Self::Item> {
1483        let (position, dx, dy) = loop {
1484            let position = if let Some(points) = &mut self.points {
1485                // if we have points then result is sparse; only some points have deltas
1486                if self.cur > self.next_point {
1487                    self.next_point = points.next()? as usize;
1488                }
1489                self.next_point
1490            } else {
1491                // no points, every point has a delta. Just take the next one.
1492                self.cur
1493            };
1494            if position == self.cur {
1495                let (dx, dy) = match &mut self.values {
1496                    TupleDeltaValues::Points(x, y) => (x.next()?, y.next()?),
1497                    TupleDeltaValues::Scalars(scalars) => (scalars.next()?, 0),
1498                };
1499                break (position, dx, dy);
1500            }
1501            self.cur += 1;
1502        };
1503        self.cur += 1;
1504        Some(T::new(position as u16, dx, dy))
1505    }
1506}
1507
1508impl EntryFormat {
1509    pub fn entry_size(self) -> u8 {
1510        ((self.bits() & Self::MAP_ENTRY_SIZE_MASK.bits()) >> 4) + 1
1511    }
1512
1513    pub fn bit_count(self) -> u8 {
1514        (self.bits() & Self::INNER_INDEX_BIT_COUNT_MASK.bits()) + 1
1515    }
1516
1517    // called from codegen
1518    pub(crate) fn map_size(self, map_count: impl Into<u32>) -> usize {
1519        self.entry_size() as usize * map_count.into() as usize
1520    }
1521}
1522
1523impl DeltaSetIndexMap<'_> {
1524    /// Returns the delta set index for the specified value.
1525    pub fn get(&self, index: u32) -> Result<DeltaSetIndex, ReadError> {
1526        let (entry_format, map_count, data) = match self {
1527            Self::Format0(fmt) => (fmt.entry_format(), fmt.map_count() as u32, fmt.map_data()),
1528            Self::Format1(fmt) => (fmt.entry_format(), fmt.map_count(), fmt.map_data()),
1529        };
1530        if map_count == 0 {
1531            return Ok(DeltaSetIndex {
1532                outer: (index >> 16) as u16,
1533                inner: index as u16,
1534            });
1535        }
1536        let entry_size = entry_format.entry_size();
1537        let data = FontData::new(data);
1538        // "if an index into the mapping array is used that is greater than or equal to
1539        // mapCount, then the last logical entry of the mapping array is used."
1540        // https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats
1541        // #associating-target-items-to-variation-data
1542        let index = index.min(map_count.saturating_sub(1));
1543        let offset = index as usize * entry_size as usize;
1544        let entry = match entry_size {
1545            1 => data.read_at::<u8>(offset)? as u32,
1546            2 => data.read_at::<u16>(offset)? as u32,
1547            3 => data.read_at::<Uint24>(offset)?.into(),
1548            4 => data.read_at::<u32>(offset)?,
1549            _ => {
1550                return Err(ReadError::MalformedData(
1551                    "invalid entry size in DeltaSetIndexMap",
1552                ))
1553            }
1554        };
1555        let bit_count = entry_format.bit_count();
1556        Ok(DeltaSetIndex {
1557            outer: (entry >> bit_count) as u16,
1558            inner: (entry & ((1 << bit_count) - 1)) as u16,
1559        })
1560    }
1561}
1562
1563impl ItemVariationStore<'_> {
1564    /// Computes the delta value for the specified index and set of
1565    /// normalized variation coordinates.
1566    ///
1567    /// Each region's scalar is computed in 16.16 and multiplied by its raw
1568    /// delta, and the products are summed exactly -- 48.16 holds the largest
1569    /// possible sum with room to spare. Use [`F48Dot16::to_i32`] for the
1570    /// classic integer delta, or apply the value unrounded to targets that
1571    /// take fractional deltas.
1572    pub fn compute_delta(&self, index: DeltaSetIndex, coords: &[F2Dot14]) -> Option<F48Dot16> {
1573        self.compute_delta_with_scalars(index, coords, &[])
1574    }
1575
1576    /// Computes the delta for `index` at `coords`, reusing `scalars`.
1577    ///
1578    /// `scalars` holds one entry per variation region, from
1579    /// [`compute_scalars`](Self::compute_scalars). It may be empty or cover
1580    /// only some of them; the rest are computed here.
1581    pub fn compute_delta_with_scalars(
1582        &self,
1583        index: DeltaSetIndex,
1584        coords: &[F2Dot14],
1585        scalars: &[Fixed],
1586    ) -> Option<F48Dot16> {
1587        if coords.is_empty() || index == DeltaSetIndex::NO_VARIATION_INDEX {
1588            return Some(F48Dot16::ZERO);
1589        }
1590        let data = match self.item_variation_data().get(index.outer as usize) {
1591            Some(data) => data.ok()?,
1592            None => return Some(F48Dot16::ZERO),
1593        };
1594        let region_indices = data.region_indexes();
1595        // Read only when a scalar has to be computed. A run that finds all
1596        // of them in `scalars` never touches the region list.
1597        let mut regions = None;
1598        // Compute deltas with 64-bit precision.
1599        // See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/7ab541a2/src/truetype/ttgxvar.c#L1094>
1600        let mut accum = F48Dot16::ZERO;
1601        // The deltas and the region indices are parallel arrays sized by the
1602        // same header field, so they are walked together.
1603        for (region_index, region_delta) in region_indices.iter().zip(data.delta_set(index.inner)) {
1604            let region_index = region_index.get() as usize;
1605            let scalar = match scalars.get(region_index) {
1606                Some(scalar) => *scalar,
1607                None => {
1608                    let regions = match &regions {
1609                        Some(regions) => regions,
1610                        None => {
1611                            regions.insert(self.variation_region_list().ok()?.variation_regions())
1612                        }
1613                    };
1614                    let region = regions.get(region_index).ok()?;
1615                    // A zero delta cannot contribute at any location. Still
1616                    // resolve its region so malformed indices fail as before.
1617                    if region_delta == 0 {
1618                        continue;
1619                    }
1620                    region.compute_scalar(coords)
1621                }
1622            };
1623            // The sum cannot overflow, even for hostile data: a scalar is a
1624            // product of ratios that the range guards keep at most one, so
1625            // each term is under 2^47, and at most 2^16 - 1 regions bounds
1626            // the total below 2^63.
1627            accum += scalar.mul_i32(region_delta);
1628        }
1629        Some(accum)
1630    }
1631
1632    /// Computes the scalar for each variation region at `coords`, in store
1633    /// order, and returns how many were written.
1634    ///
1635    /// A region that does not apply at `coords` is written as zero, which is
1636    /// the value it contributes.
1637    ///
1638    /// `out` may be shorter than the store has regions, and an unreadable
1639    /// store writes nothing.
1640    /// [`compute_delta_with_scalars`](Self::compute_delta_with_scalars)
1641    /// computes whatever is missing.
1642    ///
1643    /// Pass back only the entries this wrote. Regions are dense, so the
1644    /// length of the slice is what identifies them.
1645    pub fn compute_scalars(&self, coords: &[F2Dot14], out: &mut [Fixed]) -> usize {
1646        let Ok(list) = self.variation_region_list() else {
1647            return 0;
1648        };
1649        let regions = list.variation_regions();
1650        let count = out.len().min(regions.len());
1651        for (i, out) in out[..count].iter_mut().enumerate() {
1652            // An unreadable region ends the run. Entries already written
1653            // stay valid; the caller computes the rest.
1654            let Ok(region) = regions.get(i) else {
1655                return i;
1656            };
1657            *out = region.compute_scalar(coords);
1658        }
1659        count
1660    }
1661}
1662
1663impl<'a> VariationRegion<'a> {
1664    /// Computes a scalar value for this region and the specified
1665    /// normalized variation coordinates.
1666    pub fn compute_scalar(&self, coords: &[F2Dot14]) -> Fixed {
1667        const ZERO: Fixed = Fixed::ZERO;
1668        let mut scalar = Fixed::ONE;
1669        for (i, peak, axis_coords) in self.active_region_axes() {
1670            // A coordinate pinned to the peak contributes a factor of one,
1671            // whatever the rest of the axis says: if the axis is invalid or
1672            // the peak sits at an edge, the outcome is `continue` on every
1673            // path below. Checking it first, in raw 2.14, skips the start
1674            // and end reads and their conversions for the common case of an
1675            // instance sitting on a region's corner.
1676            let raw_coord = coords.get(i).copied().unwrap_or_default();
1677            if raw_coord == peak {
1678                continue;
1679            }
1680            let peak = peak.to_fixed();
1681            let start = axis_coords.start_coord.get().to_fixed();
1682            let end = axis_coords.end_coord.get().to_fixed();
1683            if start > peak || peak > end || start < ZERO && end > ZERO {
1684                continue;
1685            }
1686            let coord = raw_coord.to_fixed();
1687            if coord < start || coord > end {
1688                return ZERO;
1689            } else if coord < peak {
1690                scalar = scalar.mul_div(coord - start, peak - start);
1691            } else {
1692                scalar = scalar.mul_div(end - coord, end - peak);
1693            }
1694        }
1695        scalar
1696    }
1697
1698    fn active_region_axes(
1699        &self,
1700    ) -> impl Iterator<Item = (usize, F2Dot14, &'a RegionAxisCoordinates)> {
1701        self.region_axes()
1702            .iter()
1703            .enumerate()
1704            .filter_map(|(i, axis_coords)| {
1705                let peak = axis_coords.peak_coord();
1706                if peak != F2Dot14::ZERO {
1707                    Some((i, peak, axis_coords))
1708                } else {
1709                    None
1710                }
1711            })
1712    }
1713}
1714
1715impl<'a> ItemVariationData<'a> {
1716    /// Returns an iterator over the per-region delta values for the specified
1717    /// inner index.
1718    pub fn delta_set(&self, inner_index: u16) -> impl Iterator<Item = i32> + 'a + Clone {
1719        let word_delta_count = self.word_delta_count();
1720        let region_count = self.region_index_count();
1721        let bytes_per_row = Self::delta_row_len(word_delta_count, region_count);
1722        let long_words = word_delta_count & 0x8000 != 0;
1723        let word_delta_count = word_delta_count & 0x7FFF;
1724
1725        let offset = bytes_per_row * inner_index as usize;
1726        ItemDeltas {
1727            bytes: self.delta_sets().get(offset..).unwrap_or_default().iter(),
1728            word_delta_count,
1729            long_words,
1730            len: region_count,
1731            pos: 0,
1732        }
1733    }
1734
1735    pub fn get_delta_row_len(&self) -> usize {
1736        let word_delta_count = self.word_delta_count();
1737        let region_count = self.region_index_count();
1738        Self::delta_row_len(word_delta_count, region_count)
1739    }
1740
1741    /// the length of one delta set
1742    pub fn delta_row_len(word_delta_count: u16, region_index_count: u16) -> usize {
1743        let region_count = region_index_count as usize;
1744        let long_words = word_delta_count & 0x8000 != 0;
1745        let (word_size, small_size) = if long_words { (4, 2) } else { (2, 1) };
1746        let long_delta_count = (word_delta_count & 0x7FFF) as usize;
1747        let short_delta_count = region_count.saturating_sub(long_delta_count);
1748        long_delta_count * word_size + short_delta_count * small_size
1749    }
1750
1751    // called from generated code: compute the length in bytes of the delta_sets data
1752    pub fn delta_sets_len(
1753        item_count: u16,
1754        word_delta_count: u16,
1755        region_index_count: u16,
1756    ) -> usize {
1757        let bytes_per_row = Self::delta_row_len(word_delta_count, region_index_count);
1758        bytes_per_row * item_count as usize
1759    }
1760}
1761
1762#[derive(Clone)]
1763struct ItemDeltas<'a> {
1764    bytes: core::slice::Iter<'a, u8>,
1765    word_delta_count: u16,
1766    long_words: bool,
1767    len: u16,
1768    pos: u16,
1769}
1770
1771impl Iterator for ItemDeltas<'_> {
1772    type Item = i32;
1773
1774    fn next(&mut self) -> Option<Self::Item> {
1775        if self.pos >= self.len {
1776            return None;
1777        }
1778        let pos = self.pos;
1779        self.pos += 1;
1780        let mut byte = || self.bytes.next().copied();
1781        let value = match (pos >= self.word_delta_count, self.long_words) {
1782            (true, true) | (false, false) => i16::from_be_bytes([byte()?, byte()?]) as i32,
1783            (true, false) => byte()? as i8 as i32,
1784            (false, true) => i32::from_be_bytes([byte()?, byte()?, byte()?, byte()?]),
1785        };
1786        Some(value)
1787    }
1788}
1789
1790/// The delta for a glyph's advance, reusing `scalars`.
1791///
1792/// Keeps every bit the variation store computed. Rounding it to a whole
1793/// design unit is left to a caller, and implementations differ on how.
1794pub(crate) fn advance_delta_with_scalars(
1795    dsim: Option<Result<DeltaSetIndexMap, ReadError>>,
1796    ivs: Result<ItemVariationStore, ReadError>,
1797    glyph_id: GlyphId,
1798    coords: &[F2Dot14],
1799    scalars: &[Fixed],
1800) -> Option<F48Dot16> {
1801    if coords.is_empty() {
1802        return Some(F48Dot16::ZERO);
1803    }
1804    let gid = glyph_id.to_u32();
1805    let ix = match dsim {
1806        Some(Ok(dsim)) => dsim.get(gid).ok()?,
1807        // Implicit mappings use outer index zero; an oversized glyph ID
1808        // cannot address an inner row and must not wrap to a different glyph.
1809        _ if gid > u16::MAX as u32 => return Some(F48Dot16::ZERO),
1810        _ => DeltaSetIndex {
1811            outer: 0,
1812            inner: gid as _,
1813        },
1814    };
1815    ivs.ok()?.compute_delta_with_scalars(ix, coords, scalars)
1816}
1817
1818/// The delta for an item, reusing `scalars`.
1819///
1820/// As [`advance_delta_with_scalars`], but for mappings that require an index
1821/// map instead of falling back to the glyph id.
1822pub(crate) fn item_delta_with_scalars(
1823    dsim: Option<Result<DeltaSetIndexMap, ReadError>>,
1824    ivs: Result<ItemVariationStore, ReadError>,
1825    glyph_id: GlyphId,
1826    coords: &[F2Dot14],
1827    scalars: &[Fixed],
1828) -> Option<F48Dot16> {
1829    if coords.is_empty() {
1830        return Some(F48Dot16::ZERO);
1831    }
1832    let gid = glyph_id.to_u32();
1833    let ix = match dsim {
1834        Some(Ok(dsim)) => dsim.get(gid).ok()?,
1835        _ => return None,
1836    };
1837    ivs.ok()?.compute_delta_with_scalars(ix, coords, scalars)
1838}
1839
1840#[cfg(test)]
1841mod tests {
1842    use font_test_data::bebuffer::BeBuffer;
1843
1844    use super::*;
1845    use crate::{FontRef, TableProvider};
1846
1847    #[test]
1848    fn zero_deltas_still_validate_their_regions() {
1849        let store_bytes = |indices: [u16; 2]| {
1850            BeBuffer::new()
1851                .push(1u16) // ItemVariationStore format
1852                .push(12u32) // region list offset
1853                .push(1u16) // variation data count
1854                .push(28u32) // variation data offset
1855                .push(1u16) // axis count
1856                .push(2u16) // region count
1857                .extend([F2Dot14::ZERO, F2Dot14::ONE, F2Dot14::ONE])
1858                .extend([F2Dot14::NEG_ONE, F2Dot14::NEG_ONE, F2Dot14::ZERO])
1859                .push(2u16) // item count
1860                .push(2u16) // word delta count
1861                .push(2u16) // region index count
1862                .extend(indices)
1863                .extend([0i16, 120, -40, 0])
1864        };
1865        let bytes = store_bytes([0, 1]);
1866        let store = ItemVariationStore::read(FontData::new(&bytes)).unwrap();
1867        for coord in [-1.0, -0.25, 0.0, 0.25, 1.0] {
1868            let coords = [F2Dot14::from_f32(coord)];
1869            let mut scalars = [Fixed::ZERO; 2];
1870            assert_eq!(store.compute_scalars(&coords, &mut scalars), 2);
1871            for (inner, expected) in [120.0 * (-coord).max(0.0), -40.0 * coord.max(0.0)]
1872                .into_iter()
1873                .enumerate()
1874            {
1875                let index = DeltaSetIndex {
1876                    outer: 0,
1877                    inner: inner as u16,
1878                };
1879                let expected = Some(F48Dot16::from_f64(expected as f64));
1880                assert_eq!(store.compute_delta(index, &coords), expected);
1881                for count in 0..=2 {
1882                    assert_eq!(
1883                        store.compute_delta_with_scalars(index, &coords, &scalars[..count]),
1884                        expected
1885                    );
1886                }
1887            }
1888        }
1889        // Both the leading and trailing zero must reject an invalid region.
1890        for (indices, inner) in [([2, 1], 0), ([0, 2], 1)] {
1891            let bytes = store_bytes(indices);
1892            let store = ItemVariationStore::read(FontData::new(&bytes)).unwrap();
1893            assert_eq!(
1894                store.compute_delta(DeltaSetIndex { outer: 0, inner }, &[F2Dot14::ONE]),
1895                None
1896            );
1897        }
1898    }
1899
1900    #[test]
1901    fn implicit_advance_indices_do_not_truncate_glyph_ids() {
1902        let bytes = BeBuffer::new()
1903            .push(1u16) // ItemVariationStore format
1904            .push(12u32) // region list offset
1905            .push(1u16) // variation data count
1906            .push(22u32) // variation data offset
1907            .push(1u16) // axis count
1908            .push(1u16) // region count
1909            .extend([F2Dot14::ZERO, F2Dot14::ONE, F2Dot14::ONE])
1910            .push(2u16) // item count
1911            .push(1u16) // word delta count
1912            .push(1u16) // region index count
1913            .push(0u16) // region index
1914            .extend([10i16, 100i16]);
1915        let store = ItemVariationStore::read(FontData::new(&bytes)).unwrap();
1916        let mapping = DeltaSetIndexMap::read(FontData::new(&[0, 0, 0, 1, 0])).unwrap();
1917        let coords = [F2Dot14::from_f32(0.75)];
1918        let mut scalars = [Fixed::ZERO; 32];
1919        let scalar_count = store.compute_scalars(&coords, &mut scalars);
1920        for scalars in [&[][..], &scalars[..scalar_count]] {
1921            let delta = advance_delta_with_scalars(
1922                None,
1923                Ok(store.clone()),
1924                GlyphId::new(1),
1925                &coords,
1926                scalars,
1927            );
1928            assert!(delta.is_some_and(|delta| delta != F48Dot16::ZERO));
1929            for gid in [0xFFFF, 0x10000, 0x10001, 0xFFFF0001] {
1930                assert_eq!(
1931                    advance_delta_with_scalars(
1932                        None,
1933                        Ok(store.clone()),
1934                        GlyphId::new(gid),
1935                        &coords,
1936                        scalars,
1937                    ),
1938                    Some(F48Dot16::ZERO),
1939                    "implicit advance mapping wrapped glyph ID {gid}"
1940                );
1941            }
1942            // Explicit mappings keep their last-entry repetition for high IDs.
1943            for gid in [0x10000, 0x10001] {
1944                assert_eq!(
1945                    advance_delta_with_scalars(
1946                        Some(Ok(mapping.clone())),
1947                        Ok(store.clone()),
1948                        GlyphId::new(gid),
1949                        &coords,
1950                        scalars,
1951                    ),
1952                    Some(F48Dot16::from_f64(7.5))
1953                );
1954            }
1955        }
1956    }
1957
1958    #[test]
1959    fn ivs_regions() {
1960        let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
1961        let hvar = font.hvar().expect("missing HVAR table");
1962        let ivs = hvar
1963            .item_variation_store()
1964            .expect("missing item variation store in HVAR");
1965        let region_list = ivs.variation_region_list().expect("missing region list!");
1966        let regions = region_list.variation_regions();
1967        let expected = &[
1968            // start_coord, peak_coord, end_coord
1969            vec![[-1.0f32, -1.0, 0.0]],
1970            vec![[0.0, 1.0, 1.0]],
1971        ][..];
1972        let region_coords = regions
1973            .iter()
1974            .map(|region| {
1975                region
1976                    .unwrap()
1977                    .region_axes()
1978                    .iter()
1979                    .map(|coords| {
1980                        [
1981                            coords.start_coord().to_f32(),
1982                            coords.peak_coord().to_f32(),
1983                            coords.end_coord().to_f32(),
1984                        ]
1985                    })
1986                    .collect::<Vec<_>>()
1987            })
1988            .collect::<Vec<_>>();
1989        assert_eq!(expected, &region_coords);
1990    }
1991
1992    // A gvar/cvar intermediate region that does not enclose its own peak, or
1993    // that straddles the default, must be ignored (contribute a factor of one)
1994    // rather than interpolated. This matches the OpenType algorithm and the
1995    // sibling `compute_scalar_f32` / `VariationRegion::compute_scalar`, which
1996    // already apply the guard.
1997    #[test]
1998    fn scalar_for_ignores_invalid_intermediate_region() {
1999        fn tuple(values: &[BigEndian<F2Dot14>]) -> Tuple<'_> {
2000            Tuple { values }
2001        }
2002        let f = F2Dot14::from_f32;
2003
2004        // start > peak: the coord sits inside (start, end) but the region does
2005        // not contain the peak, so the axis is skipped. Before the guard this
2006        // interpolated to (end - coord) / (end - peak) = 0.2.
2007        let peak: [BigEndian<F2Dot14>; 1] = [f(0.5).into()];
2008        let start: [BigEndian<F2Dot14>; 1] = [f(0.8).into()];
2009        let end: [BigEndian<F2Dot14>; 1] = [f(1.0).into()];
2010        let coords = [f(0.9)];
2011        assert_eq!(
2012            scalar_for(&tuple(&peak), Some((tuple(&start), tuple(&end))), &coords),
2013            Some(Fixed::ONE)
2014        );
2015
2016        // peak > end.
2017        let peak2: [BigEndian<F2Dot14>; 1] = [f(1.0).into()];
2018        let start2: [BigEndian<F2Dot14>; 1] = [f(0.0).into()];
2019        let end2: [BigEndian<F2Dot14>; 1] = [f(0.5).into()];
2020        let coords2 = [f(0.25)];
2021        assert_eq!(
2022            scalar_for(
2023                &tuple(&peak2),
2024                Some((tuple(&start2), tuple(&end2))),
2025                &coords2
2026            ),
2027            Some(Fixed::ONE)
2028        );
2029
2030        // Region straddling the default (start < 0 < end).
2031        let start3: [BigEndian<F2Dot14>; 1] = [f(-0.5).into()];
2032        let end3: [BigEndian<F2Dot14>; 1] = [f(1.0).into()];
2033        let coords3 = [f(0.25)];
2034        assert_eq!(
2035            scalar_for(
2036                &tuple(&peak),
2037                Some((tuple(&start3), tuple(&end3))),
2038                &coords3
2039            ),
2040            Some(Fixed::ONE)
2041        );
2042
2043        // A well-formed region (start <= peak <= end) still interpolates.
2044        let start4: [BigEndian<F2Dot14>; 1] = [f(0.0).into()];
2045        let end4: [BigEndian<F2Dot14>; 1] = [f(1.0).into()];
2046        let coords4 = [f(0.25)];
2047        let scalar = scalar_for(
2048            &tuple(&peak),
2049            Some((tuple(&start4), tuple(&end4))),
2050            &coords4,
2051        )
2052        .unwrap();
2053        assert!((scalar.to_f32() - 0.5).abs() < 1e-4);
2054    }
2055
2056    // adapted from https://github.com/fonttools/fonttools/blob/f73220816264fc383b8a75f2146e8d69e455d398/Tests/ttLib/tables/TupleVariation_test.py#L492
2057    #[test]
2058    fn packed_points() {
2059        fn decode_points(bytes: &[u8]) -> Option<Vec<u16>> {
2060            let data = FontData::new(bytes);
2061            let packed = PackedPointNumbers { data };
2062            if packed.count() == 0 {
2063                None
2064            } else {
2065                Some(packed.iter().collect())
2066            }
2067        }
2068
2069        assert_eq!(decode_points(&[0]), None);
2070        // all points in glyph (in overly verbose encoding, not explicitly prohibited by spec)
2071        assert_eq!(decode_points(&[0x80, 0]), None);
2072        // 2 points; first run: [9, 9+6]
2073        assert_eq!(decode_points(&[0x02, 0x01, 0x09, 0x06]), Some(vec![9, 15]));
2074        // 2 points; first run: [0xBEEF, 0xCAFE]. (0x0C0F = 0xCAFE - 0xBEEF)
2075        assert_eq!(
2076            decode_points(&[0x02, 0x81, 0xbe, 0xef, 0x0c, 0x0f]),
2077            Some(vec![0xbeef, 0xcafe])
2078        );
2079        // 1 point; first run: [7]
2080        assert_eq!(decode_points(&[0x01, 0, 0x07]), Some(vec![7]));
2081        // 1 point; first run: [7] in overly verbose encoding
2082        assert_eq!(decode_points(&[0x01, 0x80, 0, 0x07]), Some(vec![7]));
2083        // 1 point; first run: [65535]; requires words to be treated as unsigned numbers
2084        assert_eq!(decode_points(&[0x01, 0x80, 0xff, 0xff]), Some(vec![65535]));
2085        // 4 points; first run: [7, 8]; second run: [255, 257]. 257 is stored in delta-encoded bytes (0xFF + 2).
2086        assert_eq!(
2087            decode_points(&[0x04, 1, 7, 1, 1, 0xff, 2]),
2088            Some(vec![7, 8, 263, 265])
2089        );
2090    }
2091
2092    #[test]
2093    fn packed_point_byte_len() {
2094        fn count_bytes(bytes: &[u8]) -> usize {
2095            let packed = PackedPointNumbers {
2096                data: FontData::new(bytes),
2097            };
2098            packed.total_len()
2099        }
2100
2101        static CASES: &[&[u8]] = &[
2102            &[0],
2103            &[0x80, 0],
2104            &[0x02, 0x01, 0x09, 0x06],
2105            &[0x02, 0x81, 0xbe, 0xef, 0x0c, 0x0f],
2106            &[0x01, 0, 0x07],
2107            &[0x01, 0x80, 0, 0x07],
2108            &[0x01, 0x80, 0xff, 0xff],
2109            &[0x04, 1, 7, 1, 1, 0xff, 2],
2110        ];
2111
2112        for case in CASES {
2113            assert_eq!(count_bytes(case), case.len(), "{case:?}");
2114        }
2115    }
2116
2117    // https://github.com/fonttools/fonttools/blob/c30a6355ffdf7f09d31e7719975b4b59bac410af/Tests/ttLib/tables/TupleVariation_test.py#L670
2118    #[test]
2119    fn packed_deltas() {
2120        static INPUT: FontData = FontData::new(&[0x83, 0x40, 0x01, 0x02, 0x01, 0x81, 0x80]);
2121
2122        let deltas = PackedDeltas::consume_all(INPUT);
2123        assert_eq!(deltas.count_or_compute(), 7);
2124        assert_eq!(
2125            deltas.iter().collect::<Vec<_>>(),
2126            &[0, 0, 0, 0, 258, -127, -128]
2127        );
2128
2129        assert_eq!(
2130            PackedDeltas::consume_all(FontData::new(&[0x81]))
2131                .iter()
2132                .collect::<Vec<_>>(),
2133            &[0, 0,]
2134        );
2135    }
2136
2137    // https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#packed-deltas
2138    #[test]
2139    fn packed_deltas_spec() {
2140        static INPUT: FontData = FontData::new(&[
2141            0x03, 0x0A, 0x97, 0x00, 0xC6, 0x87, 0x41, 0x10, 0x22, 0xFB, 0x34,
2142        ]);
2143        static EXPECTED: &[i32] = &[10, -105, 0, -58, 0, 0, 0, 0, 0, 0, 0, 0, 4130, -1228];
2144
2145        let deltas = PackedDeltas::consume_all(INPUT);
2146        assert_eq!(deltas.count_or_compute(), EXPECTED.len());
2147        assert_eq!(deltas.iter().collect::<Vec<_>>(), EXPECTED);
2148    }
2149
2150    #[test]
2151    fn packed_delta_fetcher_skip_matches_iterator_suffix() {
2152        static INPUT: FontData = FontData::new(&[
2153            0x03, 0x0A, 0x97, 0x00, 0xC6, 0x87, 0x41, 0x10, 0x22, 0xFB, 0x34,
2154        ]);
2155        let deltas = PackedDeltas::consume_all(INPUT);
2156        let expected = deltas.iter().collect::<Vec<_>>();
2157
2158        for skip in 0..=expected.len() {
2159            let mut fetcher = deltas.fetcher();
2160            fetcher.skip(skip).unwrap();
2161            let mut out = vec![0.0; expected.len() - skip];
2162            fetcher.add_to_f32_scaled(&mut out, 1.0).unwrap();
2163            let got = out.into_iter().map(|v| v as i32).collect::<Vec<_>>();
2164            assert_eq!(&got[..], &expected[skip..], "skip={skip}");
2165        }
2166
2167        let mut fetcher = deltas.fetcher();
2168        assert!(matches!(
2169            fetcher.skip(expected.len() + 1),
2170            Err(ReadError::OutOfBounds)
2171        ));
2172    }
2173
2174    #[test]
2175    fn packed_delta_fetcher_scaled_add_and_exhaustion() {
2176        static INPUT: FontData = FontData::new(&[
2177            0x03, 0x0A, 0x97, 0x00, 0xC6, 0x87, 0x41, 0x10, 0x22, 0xFB, 0x34,
2178        ]);
2179        // First four deltas are [10, -105, 0, -58].
2180        let deltas = PackedDeltas::new(INPUT, 4);
2181        let mut fetcher = deltas.fetcher();
2182        let mut out = [1.0f32; 4];
2183        fetcher.add_to_f32_scaled(&mut out, 0.5).unwrap();
2184        assert_eq!(out, [6.0, -51.5, 1.0, -28.0]);
2185
2186        // Bounded fetcher should now be exhausted.
2187        let mut extra = [0.0f32; 1];
2188        assert!(matches!(
2189            fetcher.add_to_f32_scaled(&mut extra, 1.0),
2190            Err(ReadError::OutOfBounds)
2191        ));
2192    }
2193
2194    #[test]
2195    fn packed_delta_fetcher_f64_preserves_i32_residuals() {
2196        static INPUT: FontData =
2197            FontData::new(&[0xC1, 0x01, 0x00, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x00]);
2198        let mut fetcher = PackedDeltas::new(INPUT, 2).fetcher();
2199        let mut out = [0.0f64];
2200        fetcher.add_to_f64_scaled(&mut out, 0.5).unwrap();
2201        fetcher.add_to_f64_scaled(&mut out, 0.5).unwrap();
2202        assert_eq!(out, [0.5]);
2203        assert!(matches!(
2204            fetcher.add_to_f64_scaled(&mut out, 1.0),
2205            Err(ReadError::OutOfBounds)
2206        ));
2207    }
2208
2209    #[test]
2210    fn packed_delta_fetcher_skip_then_add_bounded() {
2211        static INPUT: FontData = FontData::new(&[0x83, 0x40, 0x01, 0x02, 0x01, 0x81, 0x80]);
2212        // Full decoded stream: [0, 0, 0, 0, 258, -127, -128]
2213        let deltas = PackedDeltas::new(INPUT, 7);
2214        let mut fetcher = deltas.fetcher();
2215        fetcher.skip(3).unwrap();
2216        let mut out = [0.0f32; 4];
2217        fetcher.add_to_f32_scaled(&mut out, 1.0).unwrap();
2218        assert_eq!(out, [0.0, 258.0, -127.0, -128.0]);
2219    }
2220
2221    #[test]
2222    fn delta_run_iter_end_exhausts_unbounded_data() {
2223        static INPUT: FontData = FontData::new(&[0x83, 0x40, 0x01, 0x02, 0x01, 0x81, 0x80]);
2224        let deltas = PackedDeltas::consume_all(INPUT);
2225        let end = deltas.iter().end();
2226        assert_eq!(end.remaining_bytes(), 0);
2227    }
2228
2229    #[test]
2230    fn delta_run_iter_end_respects_bounded_count() {
2231        static INPUT: FontData = FontData::new(&[0x83, 0x40, 0x01, 0x02, 0x01, 0x81, 0x80]);
2232        // Count is exactly the first run only (4 zeros), so end() should not consume past
2233        // the run header byte.
2234        let deltas = PackedDeltas::new(INPUT, 4);
2235        let end = deltas.iter().end();
2236        assert_eq!(end.remaining_bytes(), INPUT.len() - 1);
2237
2238        let end_via_skip = deltas.iter().skip_fast(4).cursor;
2239        assert_eq!(end_via_skip.remaining_bytes(), INPUT.len() - 1);
2240    }
2241
2242    #[test]
2243    fn delta_run_iter_end_matches_manual_iteration_for_bounded_data() {
2244        static INPUT: FontData = FontData::new(&[
2245            0x03, 0x0A, 0x97, 0x00, 0xC6, 0x87, 0x41, 0x10, 0x22, 0xFB, 0x34,
2246        ]);
2247        let deltas = PackedDeltas::new(INPUT, 6);
2248
2249        let iter_collected = deltas.iter().collect::<Vec<_>>();
2250        assert_eq!(iter_collected.len(), 6);
2251
2252        let end = deltas.iter().end();
2253        let end_via_skip = deltas.iter().skip_fast(6).cursor;
2254        assert_eq!(end.remaining_bytes(), end_via_skip.remaining_bytes());
2255    }
2256
2257    fn lcg_next(state: &mut u32) -> u32 {
2258        *state = state.wrapping_mul(1664525).wrapping_add(1013904223);
2259        *state
2260    }
2261
2262    fn generated_delta_stream(seed: u32) -> (Vec<u8>, Vec<i32>) {
2263        let mut state = seed;
2264        let mut bytes = Vec::new();
2265        let mut expected = Vec::new();
2266        let run_count = (lcg_next(&mut state) % 6 + 1) as usize;
2267        for _ in 0..run_count {
2268            let run_type = (lcg_next(&mut state) % 4) as usize;
2269            let len = (lcg_next(&mut state) % 8 + 1) as usize;
2270            let control = match run_type {
2271                0 => (len - 1) as u8,        // i8
2272                1 => 0x40 | (len - 1) as u8, // i16
2273                2 => 0x80 | (len - 1) as u8, // zero
2274                _ => 0xC0 | (len - 1) as u8, // i32
2275            };
2276            bytes.push(control);
2277            match run_type {
2278                0 => {
2279                    for _ in 0..len {
2280                        let v = ((lcg_next(&mut state) % 255) as i32 - 127) as i8;
2281                        bytes.push(v as u8);
2282                        expected.push(v as i32);
2283                    }
2284                }
2285                1 => {
2286                    for _ in 0..len {
2287                        let v = ((lcg_next(&mut state) % 65535) as i32 - 32767) as i16;
2288                        bytes.extend(v.to_be_bytes());
2289                        expected.push(v as i32);
2290                    }
2291                }
2292                2 => {
2293                    expected.resize(expected.len() + len, 0);
2294                }
2295                _ => {
2296                    for _ in 0..len {
2297                        let v = (lcg_next(&mut state) % 2_000_001) as i32 - 1_000_000;
2298                        bytes.extend(v.to_be_bytes());
2299                        expected.push(v);
2300                    }
2301                }
2302            }
2303        }
2304        (bytes, expected)
2305    }
2306
2307    #[test]
2308    fn generated_packed_deltas_iter_matches_expected() {
2309        for seed in 1..=64 {
2310            let (bytes, expected) = generated_delta_stream(seed);
2311            let data = FontData::new(&bytes);
2312            let deltas = PackedDeltas::consume_all(data);
2313            assert_eq!(deltas.count_or_compute(), expected.len(), "seed={seed}");
2314            assert_eq!(deltas.iter().collect::<Vec<_>>(), expected, "seed={seed}");
2315        }
2316    }
2317
2318    #[test]
2319    fn generated_fetcher_skip_scaled_matches_expected() {
2320        for seed in 1..=64 {
2321            let (bytes, expected) = generated_delta_stream(seed);
2322            let data = FontData::new(&bytes);
2323            let deltas = PackedDeltas::new(data, expected.len());
2324            let mut fetcher = deltas.fetcher();
2325            let skip = (seed as usize * 7) % (expected.len() + 1);
2326            fetcher.skip(skip).unwrap();
2327
2328            let scale = if seed % 2 == 0 { 0.25 } else { -0.5 };
2329            let mut out = vec![10.0f32; expected.len() - skip];
2330            fetcher.add_to_f32_scaled(&mut out, scale).unwrap();
2331            for (i, got) in out.iter().copied().enumerate() {
2332                let want = 10.0 + expected[skip + i] as f32 * scale;
2333                assert!(
2334                    (got - want).abs() <= 1e-6,
2335                    "seed={seed} i={i} got={got} want={want}"
2336                );
2337            }
2338
2339            // Bounded fetcher should be exhausted after consuming all remaining entries.
2340            let mut extra = [0.0f32; 1];
2341            assert!(matches!(
2342                fetcher.add_to_f32_scaled(&mut extra, 1.0),
2343                Err(ReadError::OutOfBounds)
2344            ));
2345        }
2346    }
2347
2348    #[test]
2349    fn packed_point_split() {
2350        static INPUT: FontData =
2351            FontData::new(&[2, 1, 1, 2, 1, 205, 143, 1, 8, 0, 1, 202, 59, 1, 255, 0]);
2352        let (points, data) = PackedPointNumbers::split_off_front(INPUT);
2353        assert_eq!(points.count(), 2);
2354        assert_eq!(points.iter().collect::<Vec<_>>(), &[1, 3]);
2355        assert_eq!(points.total_len(), 4);
2356        assert_eq!(data.len(), INPUT.len() - 4);
2357    }
2358
2359    #[test]
2360    fn packed_points_dont_panic() {
2361        // a single '0' byte means that there are deltas for all points
2362        static ALL_POINTS: FontData = FontData::new(&[0]);
2363        let (all_points, _) = PackedPointNumbers::split_off_front(ALL_POINTS);
2364        // in which case the iterator just keeps incrementing until u16::MAX
2365        assert_eq!(all_points.iter().count(), u16::MAX as usize);
2366    }
2367
2368    /// Test that we split properly when the coordinate boundary doesn't align
2369    /// with a packed run boundary
2370    #[test]
2371    fn packed_delta_run_crosses_coord_boundary() {
2372        // 8 deltas with values 0..=7 with a run broken after the first 6; the
2373        // coordinate boundary occurs after the first 4
2374        static INPUT: FontData = FontData::new(&[
2375            // first run: 6 deltas as bytes
2376            5,
2377            0,
2378            1,
2379            2,
2380            3,
2381            // coordinate boundary is here
2382            4,
2383            5,
2384            // second run: 2 deltas as words
2385            1 | DELTAS_ARE_WORDS,
2386            0,
2387            6,
2388            0,
2389            7,
2390        ]);
2391        let deltas = PackedDeltas::consume_all(INPUT);
2392        assert_eq!(deltas.count_or_compute(), 8);
2393        let x_deltas = deltas.x_deltas().collect::<Vec<_>>();
2394        let y_deltas = deltas.y_deltas().collect::<Vec<_>>();
2395        assert_eq!(x_deltas, [0, 1, 2, 3]);
2396        assert_eq!(y_deltas, [4, 5, 6, 7]);
2397    }
2398
2399    /// We don't have a reference for our float delta computation, so this is
2400    /// a sanity test to ensure that floating point deltas are within a
2401    /// reasonable margin of the same in fixed point.
2402    #[test]
2403    fn ivs_float_deltas_nearly_match_fixed_deltas() {
2404        let font = FontRef::new(font_test_data::COLRV0V1_VARIABLE).unwrap();
2405        let axis_count = font.fvar().unwrap().axis_count() as usize;
2406        let colr = font.colr().unwrap();
2407        let ivs = colr.item_variation_store().unwrap().unwrap();
2408        // Generate a set of coords from -1 to 1 in 0.1 increments
2409        for coord in (0..=20).map(|x| F2Dot14::from_f32((x as f32) / 10.0 - 1.0)) {
2410            // For testing purposes, just splat the coord to all axes
2411            let coords = vec![coord; axis_count];
2412            for (outer_ix, data) in ivs.item_variation_data().iter().enumerate() {
2413                let outer_ix = outer_ix as u16;
2414                let Some(Ok(data)) = data else {
2415                    continue;
2416                };
2417                for inner_ix in 0..data.item_count() {
2418                    let delta_ix = DeltaSetIndex {
2419                        outer: outer_ix,
2420                        inner: inner_ix,
2421                    };
2422                    // Check the deltas against all possible target values
2423                    let delta = ivs.compute_delta(delta_ix, &coords).unwrap();
2424                    let orig_delta = delta.to_i32();
2425                    let float_delta = delta.to_f64();
2426
2427                    // For font unit types, we need to accept both rounding and
2428                    // truncation to account for the additional accumulation of
2429                    // fractional bits in floating point
2430                    assert!(
2431                        orig_delta == float_delta.round() as i32
2432                            || orig_delta == float_delta.trunc() as i32
2433                    );
2434                    // For the fixed point types, check with an epsilon
2435                    const EPSILON: f32 = 1e12;
2436                    let fixed_delta = Fixed::ZERO.apply_delta(delta);
2437                    assert!((Fixed::from_bits(orig_delta).to_f32() - fixed_delta).abs() < EPSILON);
2438                    let f2dot14_delta = F2Dot14::ZERO.apply_delta(delta);
2439                    assert!(
2440                        (F2Dot14::from_bits(orig_delta as i16).to_f32() - f2dot14_delta).abs()
2441                            < EPSILON
2442                    );
2443                }
2444            }
2445        }
2446    }
2447
2448    #[test]
2449    fn ivs_data_len_short() {
2450        let data = BeBuffer::new()
2451            .push(2u16) // item_count
2452            .push(3u16) // word_delta_count
2453            .push(5u16) // region_index_count
2454            .extend([0u16, 1, 2, 3, 4]) // region_indices
2455            .extend([1u8; 128]); // this is much more data than we need!
2456
2457        let ivs = ItemVariationData::read(data.data().into()).unwrap();
2458        let row_len = (3 * u16::RAW_BYTE_LEN) + (2 * u8::RAW_BYTE_LEN); // 3 word deltas, 2 byte deltas
2459        let expected_len = 2 * row_len;
2460        assert_eq!(ivs.delta_sets().len(), expected_len);
2461    }
2462
2463    #[test]
2464    fn ivs_data_len_long() {
2465        let data = BeBuffer::new()
2466            .push(2u16) // item_count
2467            .push(2u16 | 0x8000) // word_delta_count, long deltas
2468            .push(4u16) // region_index_count
2469            .extend([0u16, 1, 2]) // region_indices
2470            .extend([1u8; 128]); // this is much more data than we need!
2471
2472        let ivs = ItemVariationData::read(data.data().into()).unwrap();
2473        let row_len = (2 * u32::RAW_BYTE_LEN) + (2 * u16::RAW_BYTE_LEN); // 1 word (4-byte) delta, 2 short (2-byte)
2474        let expected_len = 2 * row_len;
2475        assert_eq!(ivs.delta_sets().len(), expected_len);
2476    }
2477
2478    // Add with overflow when accumulating packed point numbers
2479    // https://issues.oss-fuzz.com/issues/378159154
2480    #[test]
2481    fn packed_point_numbers_avoid_overflow() {
2482        // Lots of 1 bits triggers the behavior quite nicely
2483        let buf = vec![0xFF; 0xFFFF];
2484        let iter = PackedPointNumbersIter::new(0xFFFF, FontData::new(&buf).cursor());
2485        // Don't panic!
2486        let _ = iter.count();
2487    }
2488
2489    // Dense accumulator should match iterator
2490    #[test]
2491    fn accumulate_dense() {
2492        let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
2493        let gvar = font.gvar().unwrap();
2494        let gvar_data = gvar.glyph_variation_data(GlyphId::new(1)).unwrap().unwrap();
2495        let mut count = 0;
2496        for tuple in gvar_data.tuples() {
2497            if !tuple.has_deltas_for_all_points() {
2498                continue;
2499            }
2500            let iter_deltas = tuple
2501                .deltas()
2502                .map(|delta| (delta.x_delta, delta.y_delta))
2503                .collect::<Vec<_>>();
2504            let mut delta_buf = vec![Point::broadcast(Fixed::ZERO); iter_deltas.len()];
2505            tuple
2506                .accumulate_dense_deltas(&mut delta_buf, Fixed::ONE)
2507                .unwrap();
2508            let accum_deltas = delta_buf
2509                .iter()
2510                .map(|delta| (delta.x.to_i32(), delta.y.to_i32()))
2511                .collect::<Vec<_>>();
2512            assert_eq!(iter_deltas, accum_deltas);
2513            count += iter_deltas.len();
2514        }
2515        assert!(count != 0);
2516    }
2517
2518    // Sparse accumulator should match iterator
2519    #[test]
2520    fn accumulate_sparse() {
2521        let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
2522        let gvar = font.gvar().unwrap();
2523        let gvar_data = gvar.glyph_variation_data(GlyphId::new(2)).unwrap().unwrap();
2524        let mut count = 0;
2525        for tuple in gvar_data.tuples() {
2526            if tuple.has_deltas_for_all_points() {
2527                continue;
2528            }
2529            let iter_deltas = tuple.deltas().collect::<Vec<_>>();
2530            let max_modified_point = iter_deltas
2531                .iter()
2532                .max_by_key(|delta| delta.position)
2533                .unwrap()
2534                .position as usize;
2535            let mut delta_buf = vec![Point::broadcast(Fixed::ZERO); max_modified_point + 1];
2536            let mut flags = vec![PointFlags::default(); delta_buf.len()];
2537            tuple
2538                .accumulate_sparse_deltas(&mut delta_buf, &mut flags, Fixed::ONE)
2539                .unwrap();
2540            let mut accum_deltas = vec![];
2541            for (i, (delta, flag)) in delta_buf.iter().zip(flags).enumerate() {
2542                if flag.has_marker(PointMarker::HAS_DELTA) {
2543                    accum_deltas.push(GlyphDelta::new(
2544                        i as u16,
2545                        delta.x.to_i32(),
2546                        delta.y.to_i32(),
2547                    ));
2548                }
2549            }
2550            assert_eq!(iter_deltas, accum_deltas);
2551            count += iter_deltas.len();
2552        }
2553        assert!(count != 0);
2554    }
2555
2556    #[test]
2557    fn delta_set_index_map_empty_is_identity() {
2558        let data = BeBuffer::new()
2559            .push(0u8) // format 0
2560            .push(EntryFormat::empty())
2561            .push(0u16); // map_count
2562        let map = DeltaSetIndexMap::read(data.data().into()).unwrap();
2563        assert_eq!(
2564            map.get(0x0001_0002).unwrap(),
2565            DeltaSetIndex { outer: 1, inner: 2 }
2566        );
2567    }
2568}