Skip to main content

write_fonts/tables/
variations.rs

1//! OpenType variations common table formats
2
3include!("../../generated/generated_variations.rs");
4
5pub use read_fonts::tables::variations::{DeltaRunType, TupleIndex, TupleVariationCount};
6
7pub mod common_builder;
8pub mod ivs_builder;
9pub mod mivs_builder;
10
11impl TupleVariationHeader {
12    pub fn new(
13        variation_data_size: u16,
14        shared_tuple_idx: Option<u16>,
15        peak_tuple: Option<Tuple>,
16        intermediate_region: Option<(Tuple, Tuple)>,
17        has_private_points: bool,
18    ) -> Self {
19        assert!(
20            shared_tuple_idx.is_some() != peak_tuple.is_some(),
21            "one and only one of peak_tuple or shared_tuple_idx must be present"
22        );
23        let mut idx = shared_tuple_idx.unwrap_or_default();
24        if peak_tuple.is_some() {
25            idx |= TupleIndex::EMBEDDED_PEAK_TUPLE;
26        }
27        if intermediate_region.is_some() {
28            idx |= TupleIndex::INTERMEDIATE_REGION;
29        }
30        if has_private_points {
31            idx |= TupleIndex::PRIVATE_POINT_NUMBERS;
32        }
33        let (intermediate_start_tuple, intermediate_end_tuple) = intermediate_region
34            .map(|(start, end)| (start.values, end.values))
35            .unwrap_or_default();
36
37        TupleVariationHeader {
38            variation_data_size,
39            tuple_index: TupleIndex::from_bits(idx),
40            peak_tuple: peak_tuple.map(|tup| tup.values).unwrap_or_default(),
41            intermediate_start_tuple,
42            intermediate_end_tuple,
43        }
44    }
45
46    /// Return the number of bytes required to encode this header
47    pub fn compute_size(&self) -> u16 {
48        let len: usize = 2 + 2 // variationDataSize, tupleIndex
49        + self.peak_tuple.len() * F2Dot14::RAW_BYTE_LEN
50        + self.intermediate_start_tuple.len()  * F2Dot14::RAW_BYTE_LEN
51        + self.intermediate_end_tuple.len()  * F2Dot14::RAW_BYTE_LEN;
52        len.try_into().unwrap()
53    }
54}
55
56/// <https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#packed-point-numbers>
57#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
58#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
59pub enum PackedPointNumbers {
60    /// Contains deltas for all point numbers
61    #[default]
62    All,
63    /// Contains deltas only for these specific point numbers
64    Some(Vec<u16>),
65}
66
67/// <https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#packed-deltas>
68#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
69#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
70pub struct PackedDeltas {
71    deltas: Vec<i32>,
72}
73
74impl Validate for PackedDeltas {
75    fn validate_impl(&self, _ctx: &mut ValidationCtx) {}
76}
77
78impl FontWrite for PackedDeltas {
79    fn write_into(&self, writer: &mut TableWriter) {
80        for run in self.iter_runs() {
81            run.write_into(writer)
82        }
83    }
84}
85
86impl PackedDeltas {
87    /// Construct a `PackedDeltas` from a vector of raw delta values.
88    pub fn new(deltas: Vec<i32>) -> Self {
89        Self { deltas }
90    }
91
92    /// Compute the number of bytes required to encode these deltas
93    pub(crate) fn compute_size(&self) -> u16 {
94        self.iter_runs().fold(0u16, |acc, run| {
95            acc.checked_add(run.compute_size()).unwrap()
96        })
97    }
98
99    fn iter_runs(&self) -> impl Iterator<Item = PackedDeltaRun<'_>> {
100        // 6 bits for length per https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#packed-deltas
101        const MAX_POINTS_PER_RUN: usize = 64;
102
103        // preferred run type for this value
104        fn preferred_run_type(v: i32) -> DeltaRunType {
105            match v {
106                0 => DeltaRunType::Zero,
107                _ if v > i16::MAX as i32 || v < i16::MIN as i32 => DeltaRunType::I32,
108                _ if v > i8::MAX as i32 || v < i8::MIN as i32 => DeltaRunType::I16,
109                _ => DeltaRunType::I8,
110            }
111        }
112
113        fn count_leading_zeros(slice: &[i32]) -> u8 {
114            slice
115                .iter()
116                .take(MAX_POINTS_PER_RUN)
117                .take_while(|v| **v == 0)
118                .count() as u8
119        }
120
121        /// compute the number of deltas in the next run, and the value type
122        fn next_run_len(slice: &[i32]) -> (usize, DeltaRunType) {
123            let first = *slice.first().expect("bounds checked before here");
124            debug_assert!(first != 0, "Zeroes are supposed to be handled separately");
125            let run_type = preferred_run_type(first);
126
127            let mut idx = 1;
128            while idx < MAX_POINTS_PER_RUN && idx < slice.len() {
129                let cur = slice[idx];
130                let cur_type = preferred_run_type(cur);
131                let next_type = slice.get(idx + 1).copied().map(preferred_run_type);
132
133                // Any reason to stop?
134                if run_type == DeltaRunType::I8 {
135                    // a single zero is best stored literally inline, but two or more
136                    // should get a new run:
137                    // https://github.com/fonttools/fonttools/blob/eeaa499981c587/Lib/fontTools/ttLib/tables/TupleVariation.py#L423
138                    match cur_type {
139                        DeltaRunType::Zero if next_type == Some(DeltaRunType::Zero) => break,
140                        DeltaRunType::I16 | DeltaRunType::I32 => break,
141                        _ => (),
142                    }
143                } else if run_type == DeltaRunType::I16 {
144                    // with word deltas, a single zero justifies a new run:
145                    //https://github.com/fonttools/fonttools/blob/eeaa499981c587/Lib/fontTools/ttLib/tables/TupleVariation.py#L457
146                    match (cur_type, next_type) {
147                        (DeltaRunType::Zero | DeltaRunType::I32, _) => break,
148                        // and a single byte-size value should be inlined, if it lets
149                        // us combine two adjoining word-size runs:
150                        // https://github.com/fonttools/fonttools/blob/eeaa499981c587/Lib/fontTools/ttLib/tables/TupleVariation.py#L467
151                        (DeltaRunType::I8, Some(DeltaRunType::Zero | DeltaRunType::I8)) => break,
152                        _ => (),
153                    }
154                } else if run_type == DeltaRunType::I32 && cur_type != DeltaRunType::I32 {
155                    break;
156                }
157
158                idx += 1;
159            }
160            (idx, run_type)
161        }
162
163        let mut deltas = self.deltas.as_slice();
164
165        std::iter::from_fn(move || {
166            let run_start = *deltas.first()?;
167            if run_start == 0 {
168                let n_zeros = count_leading_zeros(deltas);
169                deltas = &deltas[n_zeros as usize..];
170                Some(PackedDeltaRun::Zeros(n_zeros))
171            } else {
172                let (len, value_type) = next_run_len(deltas);
173                let (head, tail) = deltas.split_at(len);
174                deltas = tail;
175                Some(match value_type {
176                    DeltaRunType::I32 => PackedDeltaRun::FourBytes(head),
177                    DeltaRunType::I16 => PackedDeltaRun::TwoBytes(head),
178                    DeltaRunType::I8 => PackedDeltaRun::OneByte(head),
179                    _ => {
180                        unreachable!("We should have taken the other branch for first={run_start}")
181                    }
182                })
183            }
184        })
185    }
186}
187
188#[derive(Clone, Debug, PartialEq, Eq)]
189enum PackedDeltaRun<'a> {
190    Zeros(u8),
191    OneByte(&'a [i32]),
192    TwoBytes(&'a [i32]),
193    FourBytes(&'a [i32]),
194}
195
196impl PackedDeltaRun<'_> {
197    fn compute_flag(&self) -> u8 {
198        /// Flag indicating that this run contains no data,
199        /// and that the deltas for this run are all zero.
200        const DELTAS_ARE_ZERO: u8 = 0x80;
201        /// Flag indicating the data type for delta values in the run.
202        const DELTAS_ARE_WORDS: u8 = 0x40;
203
204        match self {
205            PackedDeltaRun::Zeros(count) => (count - 1) | DELTAS_ARE_ZERO,
206            PackedDeltaRun::OneByte(deltas) => deltas.len() as u8 - 1,
207            PackedDeltaRun::TwoBytes(deltas) => (deltas.len() as u8 - 1) | DELTAS_ARE_WORDS,
208            PackedDeltaRun::FourBytes(deltas) => {
209                (deltas.len() as u8 - 1) | DELTAS_ARE_WORDS | DELTAS_ARE_ZERO
210            }
211        }
212    }
213
214    fn compute_size(&self) -> u16 {
215        match self {
216            PackedDeltaRun::Zeros(_) => 1,
217            PackedDeltaRun::OneByte(vals) => vals.len() as u16 + 1,
218            PackedDeltaRun::TwoBytes(vals) => vals.len() as u16 * 2 + 1,
219            PackedDeltaRun::FourBytes(vals) => vals.len() as u16 * 4 + 1,
220        }
221    }
222}
223
224impl FontWrite for PackedDeltaRun<'_> {
225    fn write_into(&self, writer: &mut TableWriter) {
226        self.compute_flag().write_into(writer);
227        match self {
228            PackedDeltaRun::Zeros(_) => (),
229            PackedDeltaRun::OneByte(deltas) => {
230                deltas.iter().for_each(|v| (*v as i8).write_into(writer))
231            }
232            PackedDeltaRun::TwoBytes(deltas) => {
233                deltas.iter().for_each(|v| (*v as i16).write_into(writer))
234            }
235            PackedDeltaRun::FourBytes(deltas) => deltas.iter().for_each(|v| v.write_into(writer)),
236        }
237    }
238}
239
240impl crate::validate::Validate for PackedPointNumbers {
241    fn validate_impl(&self, ctx: &mut ValidationCtx) {
242        if let PackedPointNumbers::Some(pts) = self {
243            if pts.len() > 0x7FFF {
244                ctx.report("length cannot be stored in 15 bites");
245            }
246        }
247    }
248}
249
250impl FontWrite for PackedPointNumbers {
251    fn write_into(&self, writer: &mut TableWriter) {
252        // compute the actual count:
253        match self.as_slice().len() {
254            len @ 0..=127 => (len as u8).write_into(writer),
255            len => (len as u16 | 0x8000u16).write_into(writer),
256        }
257        for run in self.iter_runs() {
258            run.write_into(writer);
259        }
260    }
261}
262
263impl PackedPointNumbers {
264    /// Compute the number of bytes required to encode these points
265    pub(crate) fn compute_size(&self) -> u16 {
266        let mut count = match self {
267            PackedPointNumbers::All => return 1,
268            PackedPointNumbers::Some(pts) if pts.len() < 128 => 1u16,
269            PackedPointNumbers::Some(_) => 2,
270        };
271        for run in self.iter_runs() {
272            count = count.checked_add(run.compute_size()).unwrap();
273        }
274        count
275    }
276
277    fn as_slice(&self) -> &[u16] {
278        match self {
279            PackedPointNumbers::All => &[],
280            PackedPointNumbers::Some(pts) => pts.as_slice(),
281        }
282    }
283
284    fn iter_runs(&self) -> impl Iterator<Item = PackedPointRun<'_>> {
285        const U8_MAX: u16 = u8::MAX as u16;
286        const MAX_POINTS_PER_RUN: usize = 128;
287
288        let mut points = match self {
289            PackedPointNumbers::Some(pts) => pts.as_slice(),
290            PackedPointNumbers::All => &[],
291        };
292
293        let mut prev_point = 0u16;
294
295        // split a run off the front of points:
296        // - if point is more than 255 away from prev, we're using words
297        std::iter::from_fn(move || {
298            let next = points.first()?;
299            let are_words = (next - prev_point) > U8_MAX;
300            let run_len = points
301                .iter()
302                .take(MAX_POINTS_PER_RUN)
303                .scan(prev_point, |prev, point| {
304                    let take_this = if are_words {
305                        (point - *prev) > U8_MAX
306                    } else {
307                        (point - *prev) <= U8_MAX
308                    };
309                    *prev = *point;
310                    take_this.then_some(point)
311                })
312                .count();
313
314            let (head, tail) = points.split_at(run_len);
315            points = tail;
316            let last_point = prev_point;
317            prev_point = head.last().copied().unwrap();
318
319            Some(PackedPointRun {
320                last_point,
321                are_words,
322                points: head,
323            })
324        })
325    }
326}
327
328#[derive(Debug, PartialEq, Eq)]
329struct PackedPointRun<'a> {
330    last_point: u16,
331    are_words: bool,
332    points: &'a [u16],
333}
334
335impl PackedPointRun<'_> {
336    fn compute_size(&self) -> u16 {
337        const LEN_BYTE: u16 = 1;
338        let per_point_len = if self.are_words { 2 } else { 1 };
339        self.points.len() as u16 * per_point_len + LEN_BYTE
340    }
341}
342
343impl FontWrite for PackedPointRun<'_> {
344    fn write_into(&self, writer: &mut TableWriter) {
345        assert!(!self.points.is_empty() && self.points.len() <= 128);
346        let mut len = self.points.len() as u8 - 1;
347        if self.are_words {
348            len |= 0x80;
349        }
350        len.write_into(writer);
351        let mut last_point = self.last_point;
352        for point in self.points {
353            let delta = point - last_point;
354            last_point = *point;
355            if self.are_words {
356                delta.write_into(writer);
357            } else {
358                debug_assert!(delta <= u8::MAX as u16);
359                (delta as u8).write_into(writer);
360            }
361        }
362    }
363}
364
365impl FontWrite for TupleIndex {
366    fn write_into(&self, writer: &mut TableWriter) {
367        self.bits().write_into(writer)
368    }
369}
370
371//hack: unclear if we're even going to do any codegen for writing, but
372//for the time being this lets us compile
373impl<'a> FromObjRef<Option<read_fonts::tables::variations::Tuple<'a>>> for Vec<F2Dot14> {
374    fn from_obj_ref(
375        from: &Option<read_fonts::tables::variations::Tuple<'a>>,
376        _data: FontData,
377    ) -> Self {
378        from.as_ref()
379            .map(|tup| tup.values.iter().map(BigEndian::get).collect())
380            .unwrap_or_default()
381    }
382}
383
384impl Tuple {
385    pub fn len(&self) -> u16 {
386        self.values.len().try_into().unwrap()
387    }
388
389    pub fn is_empty(&self) -> bool {
390        self.values.is_empty()
391    }
392}
393
394impl DeltaSetIndexMap {
395    /// Return the most compact entry format that can represent this mapping.
396    ///
397    /// EntryFormat is a packed u8 field that describes the compressed representation
398    /// of delta-set indices. For more info, see:
399    /// <https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#associating-target-items-to-variation-data>
400    // This is a direct port from fonttools' DeltaSetMap.getEntryFormat:
401    // https://github.com/fonttools/fonttools/blob/6d531f/Lib/fontTools/ttLib/tables/otTables.py#L644-L666
402    fn get_entry_format(mapping: &[u32]) -> EntryFormat {
403        let ored = mapping.iter().fold(0, |acc, idx| acc | *idx);
404
405        let inner = (ored & 0xFFFF) as u16;
406        let inner_bits = (16 - inner.leading_zeros() as u8).max(1);
407        assert!(inner_bits <= 16);
408
409        let ored = (ored >> (16 - inner_bits)) | (ored & ((1 << inner_bits) - 1));
410        let entry_size = match ored {
411            0..=0xFF => 1,
412            0x100..=0xFFFF => 2,
413            0x10000..=0xFFFFFF => 3,
414            _ => 4,
415        };
416
417        EntryFormat::from_bits(((entry_size - 1) << 4) | (inner_bits - 1)).unwrap()
418    }
419
420    /// Compress u32's into packed data using the most compact entry format.
421    ///
422    /// Returns the computed entry format and the packed data.
423    ///
424    /// For more info, see:
425    /// <https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#associating-target-items-to-variation-data>
426    // Ported from fonttools' VarIdxMapValue.write method:
427    // https://github.com/fonttools/fonttools/blob/6d531fe/Lib/fontTools/ttLib/tables/otConverters.py#L1764-L1781
428    fn pack_map_data(mapping: &[u32]) -> (EntryFormat, Vec<u8>) {
429        let fmt = DeltaSetIndexMap::get_entry_format(mapping);
430        let inner_bits = fmt.bit_count();
431        let inner_mask = (1 << inner_bits as u32) - 1;
432        let outer_shift = 16 - inner_bits;
433        let entry_size = fmt.entry_size();
434        assert!((1..=4).contains(&entry_size));
435
436        // omit trailing entries that are the same as the previous one;
437        // the last entry is assumed when index is >= map_count
438        let mut map_count = mapping.len();
439        while map_count > 1 && mapping[map_count - 1] == mapping[map_count - 2] {
440            map_count -= 1;
441        }
442
443        let mut map_data = Vec::with_capacity(map_count * entry_size as usize);
444        for idx in mapping.iter().take(map_count) {
445            let idx = ((idx & 0xFFFF0000) >> outer_shift) | (idx & inner_mask);
446            // append entry_size bytes to map_data in BigEndian order
447            map_data.extend_from_slice(&idx.to_be_bytes()[4 - entry_size as usize..]);
448        }
449        assert_eq!(map_data.len(), map_count * entry_size as usize);
450        (fmt, map_data)
451    }
452}
453
454impl<I> FromIterator<I> for DeltaSetIndexMap
455where
456    I: Into<u32>,
457{
458    /// Create a DeltaSetIndexMap from an iterator of delta-set indices.
459    fn from_iter<T: IntoIterator<Item = I>>(iter: T) -> Self {
460        let mapping: Vec<u32> = iter.into_iter().map(|v| v.into()).collect();
461        let (fmt, map_data) = DeltaSetIndexMap::pack_map_data(&mapping);
462        let map_count = map_data.len() / fmt.entry_size() as usize;
463        let delta_set_index_map: DeltaSetIndexMap = if map_count <= u16::MAX as usize {
464            DeltaSetIndexMap::format_0(fmt, map_count as u16, map_data)
465        } else {
466            DeltaSetIndexMap::format_1(fmt, map_count as u32, map_data)
467        };
468        delta_set_index_map
469    }
470}
471
472#[cfg(test)]
473mod tests {
474    use super::*;
475    use rstest::rstest;
476
477    #[test]
478    fn point_pack_words() {
479        let thing = PackedPointNumbers::Some(vec![1002, 2002, 8408, 12228]);
480
481        let runs = thing.iter_runs().collect::<Vec<_>>();
482        assert_eq!(runs.len(), 1);
483        assert!(runs[0].are_words);
484        assert_eq!(runs[0].last_point, 0);
485        assert_eq!(runs[0].points, &[1002, 2002, 8408, 12228]);
486    }
487
488    #[test]
489    fn serialize_packed_points() {
490        let thing = PackedPointNumbers::Some(vec![1002, 2002, 8408, 12228]);
491
492        let bytes = crate::dump_table(&thing).unwrap();
493        assert_eq!(thing.compute_size() as usize, bytes.len());
494        let (read, _) = read_fonts::tables::variations::PackedPointNumbers::split_off_front(
495            FontData::new(&bytes),
496        );
497        assert_eq!(thing.as_slice(), read.iter().collect::<Vec<_>>());
498    }
499
500    #[test]
501    fn point_pack_runs() {
502        let thing = PackedPointNumbers::Some(vec![5, 25, 225, 1002, 2002, 2008, 2228]);
503
504        let runs = thing.iter_runs().collect::<Vec<_>>();
505        assert!(!runs[0].are_words);
506        assert_eq!(runs[0].last_point, 0);
507        assert_eq!(runs[0].points, &[5, 25, 225]);
508
509        assert!(runs[1].are_words);
510        assert_eq!(runs[1].last_point, 225);
511        assert_eq!(runs[1].points, &[1002, 2002]);
512
513        assert!(!runs[2].are_words);
514        assert_eq!(runs[2].last_point, 2002);
515        assert_eq!(runs[2].points, &[2008, 2228]);
516
517        assert_eq!(runs.len(), 3);
518    }
519
520    #[test]
521    fn point_pack_long_runs() {
522        let mut numbers = vec![0u16; 130];
523        numbers.extend(1u16..=130u16);
524        let thing = PackedPointNumbers::Some(numbers);
525
526        let runs = thing.iter_runs().collect::<Vec<_>>();
527        assert!(!runs[0].are_words);
528        assert_eq!(runs[0].points.len(), 128);
529        assert_eq!(runs[1].last_point, 0);
530        assert_eq!(runs[1].points.len(), 128);
531        assert_eq!(runs[2].last_point, 126);
532        assert_eq!(runs[2].points, &[127, 128, 129, 130]);
533        assert!(runs.get(3).is_none());
534    }
535
536    #[test]
537    fn point_pack_write_one_byte() {
538        let thing = PackedPointNumbers::Some(vec![5, 25, 225, 1002, 2002, 2008, 2228, 10000]);
539
540        let bytes = crate::dump_table(&thing).unwrap();
541        assert_eq!(thing.compute_size() as usize, bytes.len());
542        let (read, _) = read_fonts::tables::variations::PackedPointNumbers::split_off_front(
543            FontData::new(&bytes),
544        );
545        assert_eq!(thing.as_slice(), read.iter().collect::<Vec<_>>());
546    }
547
548    #[test]
549    fn point_pack_write_two_byte() {
550        let thing = PackedPointNumbers::Some(vec![0; 200]);
551
552        let bytes = crate::dump_table(&thing).unwrap();
553        assert_eq!(thing.compute_size() as usize, bytes.len());
554        let (read, _) = read_fonts::tables::variations::PackedPointNumbers::split_off_front(
555            FontData::new(&bytes),
556        );
557        assert_eq!(thing.as_slice(), read.iter().collect::<Vec<_>>());
558    }
559
560    static PACKED_DELTA_BYTES: &[u8] = &[
561        0x03, 0x0A, 0x97, 0x00, 0xC6, 0x87, 0x41, 0x10, 0x22, 0xFB, 0x34,
562    ];
563
564    // <https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#packed-deltas>
565    #[test]
566    fn packed_deltas_spec_runs() {
567        let deltas = PackedDeltas::new(vec![10, -105, 0, -58, 0, 0, 0, 0, 0, 0, 0, 0, 4130, -1228]);
568        let runs = deltas.iter_runs().collect::<Vec<_>>();
569        assert_eq!(
570            runs,
571            vec![
572                PackedDeltaRun::OneByte(&[10, -105, 0, -58]),
573                PackedDeltaRun::Zeros(8),
574                PackedDeltaRun::TwoBytes(&[4130, -1228]),
575            ]
576        );
577    }
578
579    #[test]
580    fn packed_deltas_spec_write() {
581        let deltas = PackedDeltas::new(vec![10, -105, 0, -58, 0, 0, 0, 0, 0, 0, 0, 0, 4130, -1228]);
582        let bytes = crate::dump_table(&deltas).unwrap();
583        assert_eq!(bytes, PACKED_DELTA_BYTES);
584        let read = read_fonts::tables::variations::PackedDeltas::consume_all(FontData::new(&bytes));
585        let decoded = read.iter().collect::<Vec<_>>();
586        assert_eq!(deltas.deltas.len(), decoded.len());
587        assert_eq!(deltas.deltas, decoded);
588        assert_eq!(bytes, PACKED_DELTA_BYTES);
589    }
590
591    #[test]
592    fn empty_deltas() {
593        let deltas = PackedDeltas::new(vec![]);
594        let bytes = crate::dump_table(&deltas).unwrap();
595        assert!(bytes.is_empty());
596    }
597
598    #[test]
599    fn lots_of_zero() {
600        let num_zeroes = 65;
601        let deltas = PackedDeltas::new(vec![0; num_zeroes]);
602        assert_eq!(
603            vec![PackedDeltaRun::Zeros(64), PackedDeltaRun::Zeros(1)],
604            deltas.iter_runs().collect::<Vec<_>>()
605        );
606    }
607
608    #[test]
609    fn respect_my_run_length_authority() {
610        let mut values = (1..196).collect::<Vec<_>>();
611        values.extend([0, 0, 0]);
612        values.push(i16::MAX as i32 + 1);
613        values.push(i16::MIN as i32 - 1);
614        values.push(i16::MAX as i32 * 2);
615        let deltas = PackedDeltas::new(values);
616        assert_eq!(
617            vec![
618                // 64 entries per run please and thank you
619                PackedDeltaRun::OneByte(&(1..65).collect::<Vec<i32>>()),
620                // 63 entries this time because at 128 we switch to 2 bytes
621                PackedDeltaRun::OneByte(&(65..128).collect::<Vec<i32>>()),
622                // 64 per run again
623                PackedDeltaRun::TwoBytes(&(128..192).collect::<Vec<i32>>()),
624                // tail
625                PackedDeltaRun::TwoBytes(&(192..=195).collect::<Vec<i32>>()),
626                PackedDeltaRun::Zeros(3),
627                PackedDeltaRun::FourBytes(&[
628                    i16::MAX as i32 + 1,
629                    i16::MIN as i32 - 1,
630                    i16::MAX as i32 * 2
631                ]),
632            ],
633            deltas.iter_runs().collect::<Vec<_>>()
634        )
635    }
636
637    #[test]
638    fn inline_single_zeros_with_bytes() {
639        let packed = PackedDeltas::new(vec![1, 2, 0, 3]);
640        assert_eq!(packed.iter_runs().count(), 1)
641    }
642
643    #[test]
644    fn split_two_zeros_in_bytes() {
645        let packed = PackedDeltas::new(vec![1, 2, 0, 0, 3]);
646        assert_eq!(packed.iter_runs().count(), 3)
647    }
648
649    #[test]
650    fn split_single_zero_in_words() {
651        let packed = PackedDeltas::new(vec![150, 200, 0, -300]);
652        assert_eq!(packed.iter_runs().count(), 3)
653    }
654
655    #[test]
656    fn inline_single_byte_in_words() {
657        let packed = PackedDeltas::new(vec![150, 200, 1, -300]);
658        assert_eq!(packed.iter_runs().count(), 1)
659    }
660
661    #[test]
662    fn split_double_byte_in_words() {
663        let packed = PackedDeltas::new(vec![150, 200, 1, 3, -300]);
664        assert_eq!(packed.iter_runs().count(), 3)
665    }
666
667    #[test]
668    fn split_byte_then_zero_after_words() {
669        // without split: 10 = 1 + 2 + 2 + 2 + 1 + 2
670        //    with split:  9 = 1 + 2 + 2 + 1 + 3
671        let packed = PackedDeltas::new(vec![150, 200, 1, 0, 1]);
672        assert_eq!(packed.iter_runs().count(), 2);
673        assert_eq!(packed.compute_size(), 9);
674    }
675
676    #[rstest]
677    // Note how the packed data below is b"\x00\x01" and not b"\x00\x01\x01", for the
678    // repeated trailing values can be omitted
679    #[case::one_byte_one_inner_bit(
680        vec![0, 1, 1], 0b00_0000, 1, 1, b"\x00\x01",
681    )]
682    #[case::one_byte_two_inner_bits(
683        vec![0, 1, 2], 0b00_0001, 1, 2, b"\x00\x01\x02",
684    )]
685    #[case::one_byte_three_inner_bits(
686        vec![0, 1, 4], 0b00_0010, 1, 3, b"\x00\x01\x04",
687    )]
688    #[case::one_byte_four_inner_bits(
689        vec![0, 1, 8], 0b00_0011, 1, 4, b"\x00\x01\x08",
690    )]
691    // 256 needs 2 bytes, of which 9 bits for the inner value
692    #[case::two_bytes_nine_inner_bits(
693        vec![0, 1, 256], 0b01_1000, 2, 9, b"\x00\x00\x00\x01\x01\x00",
694    )]
695    #[case::two_bytes_sixteen_inner_bits(
696        vec![0, 1, 0x8000], 0b01_1111, 2, 16, b"\x00\x00\x00\x01\x80\x00",
697    )]
698    // note this gets packed the same as case 'one_byte_two_inner_bits': [0, 1, 2]
699    // above, but it uses only 1 bit for the inner value, while the other bits are
700    // used for the outer value:
701    // 0x0001_0000 => b"\x02" => 0b00000010 => {outer: 1, inner: 0)
702    #[case::one_byte_one_inner_bit_two_vardatas(
703        vec![0, 1, 0x01_0000], 0b00_0000, 1, 1, b"\x00\x01\x02",
704    )]
705    #[case::three_bytes_sixteen_inner_bits(
706        vec![0, 0xFFFF, 0x01_0000],
707        0b10_1111,
708        3,
709        16,
710        b"\x00\x00\x00\x00\xFF\xFF\x01\x00\x00",
711    )]
712    #[case::four_bytes_sixteen_inner_bits(
713        vec![0, 0xFFFF, 0xFFFF_FFFF],
714        0b11_1111,
715        4,
716        16,
717        b"\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF",
718    )]
719    #[test]
720    fn delta_set_index_map_entry_format_and_packed_data(
721        #[case] mapping: Vec<u32>,
722        #[case] expected_format_bits: u8,
723        #[case] expected_entry_size: u8,
724        #[case] expected_inner_bit_count: u8,
725        #[case] expected_map_data: &[u8],
726    ) {
727        let (format, data) = DeltaSetIndexMap::pack_map_data(&mapping);
728        assert_eq!(format.bits(), expected_format_bits);
729        assert_eq!(format.entry_size(), expected_entry_size);
730        assert_eq!(format.bit_count(), expected_inner_bit_count);
731        assert_eq!(data, expected_map_data);
732
733        let dsim: DeltaSetIndexMap = mapping.iter().copied().collect();
734        // all test mappings have fewer than 65536 entries (for practical reasons)
735        // so we should generate a Format0
736        assert!(matches!(dsim, DeltaSetIndexMap::Format0 { .. }));
737
738        // make sure we get the same mapping back after round-tripping to/from bytes
739        let raw_dsim = crate::dump_table(&dsim).unwrap();
740        let dsim2 =
741            read_fonts::tables::variations::DeltaSetIndexMap::read(FontData::new(&raw_dsim))
742                .unwrap();
743        assert_eq!(
744            (0..mapping.len())
745                .map(|i| {
746                    let index = dsim2.get(i as u32).unwrap();
747                    ((index.outer as u32) << 16) | index.inner as u32
748                })
749                .collect::<Vec<_>>(),
750            mapping
751        );
752    }
753
754    #[test]
755    fn delta_set_index_map_from_variation_index_iterator() {
756        // as returned from VariationStoreBuilder::build() in the VariationIndexRemapping
757        use crate::tables::layout::VariationIndex;
758
759        let mapping = vec![
760            VariationIndex::new(0, 0),
761            VariationIndex::new(0, 1),
762            VariationIndex::new(0, 2),
763            VariationIndex::new(1, 0),
764            VariationIndex::new(1, 1),
765            VariationIndex::new(1, 2),
766        ];
767
768        let dsim: DeltaSetIndexMap = mapping.into_iter().collect();
769        let DeltaSetIndexMap::Format0(dsim) = dsim else {
770            panic!("expected DeltaSetIndexMap::Format0, got {:?}", dsim);
771        };
772        assert_eq!(dsim.map_count, 6);
773        assert_eq!(dsim.entry_format.bits(), 0b000001);
774        assert_eq!(dsim.entry_format.entry_size(), 1); // one byte per entry
775        assert_eq!(dsim.entry_format.bit_count(), 2);
776        // for each entry/byte, the right-most 2 bits are the inner value,
777        // the remaining bits are the outer value
778        assert_eq!(
779            dsim.map_data,
780            vec![
781                0b00_00, // (0, 0)
782                0b00_01, // (0, 1)
783                0b00_10, // (0, 2)
784                0b01_00, // (1, 0)
785                0b01_01, // (1, 1)
786                0b01_10, // (1, 2)
787            ]
788        );
789    }
790
791    #[test]
792    fn huge_mapping_generates_format_1_delta_set_index_map() {
793        // 65536 entries, so we need a Format1 with u32 map_count
794        let mapping = (0..=0xFFFF).collect::<Vec<u32>>();
795        let map_count = mapping.len() as u32;
796        let dsim: DeltaSetIndexMap = mapping.into_iter().collect();
797        let DeltaSetIndexMap::Format1(dsim) = dsim else {
798            panic!("expected DeltaSetIndexMap::Format1, got {:?}", dsim);
799        };
800        assert_eq!(dsim.map_count, map_count);
801    }
802}