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