Coverage Report

Created: 2026-04-01 07:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fontations/write-fonts/src/tables/variations.rs
Line
Count
Source
1
//! OpenType variations common table formats
2
3
include!("../../generated/generated_variations.rs");
4
5
pub use read_fonts::tables::variations::{DeltaRunType, TupleIndex, TupleVariationCount};
6
7
pub mod common_builder;
8
pub mod ivs_builder;
9
pub mod mivs_builder;
10
11
impl TupleVariationHeader {
12
0
    pub fn new(
13
0
        variation_data_size: u16,
14
0
        shared_tuple_idx: Option<u16>,
15
0
        peak_tuple: Option<Tuple>,
16
0
        intermediate_region: Option<(Tuple, Tuple)>,
17
0
        has_private_points: bool,
18
0
    ) -> Self {
19
0
        assert!(
20
0
            shared_tuple_idx.is_some() != peak_tuple.is_some(),
21
0
            "one and only one of peak_tuple or shared_tuple_idx must be present"
22
        );
23
0
        let mut idx = shared_tuple_idx.unwrap_or_default();
24
0
        if peak_tuple.is_some() {
25
0
            idx |= TupleIndex::EMBEDDED_PEAK_TUPLE;
26
0
        }
27
0
        if intermediate_region.is_some() {
28
0
            idx |= TupleIndex::INTERMEDIATE_REGION;
29
0
        }
30
0
        if has_private_points {
31
0
            idx |= TupleIndex::PRIVATE_POINT_NUMBERS;
32
0
        }
33
0
        let (intermediate_start_tuple, intermediate_end_tuple) = intermediate_region
34
0
            .map(|(start, end)| (start.values, end.values))
35
0
            .unwrap_or_default();
36
37
        TupleVariationHeader {
38
0
            variation_data_size,
39
0
            tuple_index: TupleIndex::from_bits(idx),
40
0
            peak_tuple: peak_tuple.map(|tup| tup.values).unwrap_or_default(),
41
0
            intermediate_start_tuple,
42
0
            intermediate_end_tuple,
43
        }
44
0
    }
45
46
    /// Return the number of bytes required to encode this header
47
0
    pub fn compute_size(&self) -> u16 {
48
0
        let len: usize = 2 + 2 // variationDataSize, tupleIndex
49
0
        + self.peak_tuple.len() * F2Dot14::RAW_BYTE_LEN
50
0
        + self.intermediate_start_tuple.len()  * F2Dot14::RAW_BYTE_LEN
51
0
        + self.intermediate_end_tuple.len()  * F2Dot14::RAW_BYTE_LEN;
52
0
        len.try_into().unwrap()
53
0
    }
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))]
59
pub 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))]
70
pub struct PackedDeltas {
71
    deltas: Vec<i32>,
72
}
73
74
impl Validate for PackedDeltas {
75
0
    fn validate_impl(&self, _ctx: &mut ValidationCtx) {}
76
}
77
78
impl FontWrite for PackedDeltas {
79
0
    fn write_into(&self, writer: &mut TableWriter) {
80
0
        for run in self.iter_runs() {
81
0
            run.write_into(writer)
82
        }
83
0
    }
84
}
85
86
impl PackedDeltas {
87
    /// Construct a `PackedDeltas` from a vector of raw delta values.
88
0
    pub fn new(deltas: Vec<i32>) -> Self {
89
0
        Self { deltas }
90
0
    }
91
92
    /// Compute the number of bytes required to encode these deltas
93
0
    pub(crate) fn compute_size(&self) -> u16 {
94
0
        self.iter_runs().fold(0u16, |acc, run| {
95
0
            acc.checked_add(run.compute_size()).unwrap()
96
0
        })
97
0
    }
98
99
0
    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
0
        fn preferred_run_type(v: i32) -> DeltaRunType {
105
0
            match v {
106
0
                0 => DeltaRunType::Zero,
107
0
                _ if v > i16::MAX as i32 || v < i16::MIN as i32 => DeltaRunType::I32,
108
0
                _ if v > i8::MAX as i32 || v < i8::MIN as i32 => DeltaRunType::I16,
109
0
                _ => DeltaRunType::I8,
110
            }
111
0
        }
112
113
0
        fn count_leading_zeros(slice: &[i32]) -> u8 {
114
0
            slice
115
0
                .iter()
116
0
                .take(MAX_POINTS_PER_RUN)
117
0
                .take_while(|v| **v == 0)
118
0
                .count() as u8
119
0
        }
120
121
        /// compute the number of deltas in the next run, and the value type
122
0
        fn next_run_len(slice: &[i32]) -> (usize, DeltaRunType) {
123
0
            let first = *slice.first().expect("bounds checked before here");
124
0
            debug_assert!(first != 0, "Zeroes are supposed to be handled separately");
125
0
            let run_type = preferred_run_type(first);
126
127
0
            let mut idx = 1;
128
0
            while idx < MAX_POINTS_PER_RUN && idx < slice.len() {
129
0
                let cur = slice[idx];
130
0
                let cur_type = preferred_run_type(cur);
131
0
                let next_type = slice.get(idx + 1).copied().map(preferred_run_type);
132
133
                // Any reason to stop?
134
0
                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
0
                    match cur_type {
139
0
                        DeltaRunType::Zero if next_type == Some(DeltaRunType::Zero) => break,
140
0
                        DeltaRunType::I16 | DeltaRunType::I32 => break,
141
0
                        _ => (),
142
                    }
143
0
                } 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
0
                    match (cur_type, next_type) {
147
0
                        (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
0
                        (DeltaRunType::I8, Some(DeltaRunType::Zero | DeltaRunType::I8)) => break,
152
0
                        _ => (),
153
                    }
154
0
                } else if run_type == DeltaRunType::I32 && cur_type != DeltaRunType::I32 {
155
0
                    break;
156
0
                }
157
158
0
                idx += 1;
159
            }
160
0
            (idx, run_type)
161
0
        }
162
163
0
        let mut deltas = self.deltas.as_slice();
164
165
0
        std::iter::from_fn(move || {
166
0
            let run_start = *deltas.first()?;
167
0
            if run_start == 0 {
168
0
                let n_zeros = count_leading_zeros(deltas);
169
0
                deltas = &deltas[n_zeros as usize..];
170
0
                Some(PackedDeltaRun::Zeros(n_zeros))
171
            } else {
172
0
                let (len, value_type) = next_run_len(deltas);
173
0
                let (head, tail) = deltas.split_at(len);
174
0
                deltas = tail;
175
0
                Some(match value_type {
176
0
                    DeltaRunType::I32 => PackedDeltaRun::FourBytes(head),
177
0
                    DeltaRunType::I16 => PackedDeltaRun::TwoBytes(head),
178
0
                    DeltaRunType::I8 => PackedDeltaRun::OneByte(head),
179
                    _ => {
180
0
                        unreachable!("We should have taken the other branch for first={run_start}")
181
                    }
182
                })
183
            }
184
0
        })
185
0
    }
186
}
187
188
#[derive(Clone, Debug, PartialEq, Eq)]
189
enum PackedDeltaRun<'a> {
190
    Zeros(u8),
191
    OneByte(&'a [i32]),
192
    TwoBytes(&'a [i32]),
193
    FourBytes(&'a [i32]),
194
}
195
196
impl PackedDeltaRun<'_> {
197
0
    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
0
        match self {
205
0
            PackedDeltaRun::Zeros(count) => (count - 1) | DELTAS_ARE_ZERO,
206
0
            PackedDeltaRun::OneByte(deltas) => deltas.len() as u8 - 1,
207
0
            PackedDeltaRun::TwoBytes(deltas) => (deltas.len() as u8 - 1) | DELTAS_ARE_WORDS,
208
0
            PackedDeltaRun::FourBytes(deltas) => {
209
0
                (deltas.len() as u8 - 1) | DELTAS_ARE_WORDS | DELTAS_ARE_ZERO
210
            }
211
        }
212
0
    }
213
214
0
    fn compute_size(&self) -> u16 {
215
0
        match self {
216
0
            PackedDeltaRun::Zeros(_) => 1,
217
0
            PackedDeltaRun::OneByte(vals) => vals.len() as u16 + 1,
218
0
            PackedDeltaRun::TwoBytes(vals) => vals.len() as u16 * 2 + 1,
219
0
            PackedDeltaRun::FourBytes(vals) => vals.len() as u16 * 4 + 1,
220
        }
221
0
    }
222
}
223
224
impl FontWrite for PackedDeltaRun<'_> {
225
0
    fn write_into(&self, writer: &mut TableWriter) {
226
0
        self.compute_flag().write_into(writer);
227
0
        match self {
228
0
            PackedDeltaRun::Zeros(_) => (),
229
0
            PackedDeltaRun::OneByte(deltas) => {
230
0
                deltas.iter().for_each(|v| (*v as i8).write_into(writer))
231
            }
232
0
            PackedDeltaRun::TwoBytes(deltas) => {
233
0
                deltas.iter().for_each(|v| (*v as i16).write_into(writer))
234
            }
235
0
            PackedDeltaRun::FourBytes(deltas) => deltas.iter().for_each(|v| v.write_into(writer)),
236
        }
237
0
    }
238
}
239
240
impl crate::validate::Validate for PackedPointNumbers {
241
0
    fn validate_impl(&self, ctx: &mut ValidationCtx) {
242
0
        if let PackedPointNumbers::Some(pts) = self {
243
0
            if pts.len() > 0x7FFF {
244
0
                ctx.report("length cannot be stored in 15 bites");
245
0
            }
246
0
        }
247
0
    }
248
}
249
250
impl FontWrite for PackedPointNumbers {
251
0
    fn write_into(&self, writer: &mut TableWriter) {
252
        // compute the actual count:
253
0
        match self.as_slice().len() {
254
0
            len @ 0..=127 => (len as u8).write_into(writer),
255
0
            len => (len as u16 | 0x8000u16).write_into(writer),
256
        }
257
0
        for run in self.iter_runs() {
258
0
            run.write_into(writer);
259
0
        }
260
0
    }
261
}
262
263
impl PackedPointNumbers {
264
    /// Compute the number of bytes required to encode these points
265
0
    pub(crate) fn compute_size(&self) -> u16 {
266
0
        let mut count = match self {
267
0
            PackedPointNumbers::All => return 1,
268
0
            PackedPointNumbers::Some(pts) if pts.len() < 128 => 1u16,
269
0
            PackedPointNumbers::Some(_) => 2,
270
        };
271
0
        for run in self.iter_runs() {
272
0
            count = count.checked_add(run.compute_size()).unwrap();
273
0
        }
274
0
        count
275
0
    }
276
277
0
    fn as_slice(&self) -> &[u16] {
278
0
        match self {
279
0
            PackedPointNumbers::All => &[],
280
0
            PackedPointNumbers::Some(pts) => pts.as_slice(),
281
        }
282
0
    }
283
284
0
    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
0
        let mut points = match self {
289
0
            PackedPointNumbers::Some(pts) => pts.as_slice(),
290
0
            PackedPointNumbers::All => &[],
291
        };
292
293
0
        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
0
        std::iter::from_fn(move || {
298
0
            let next = points.first()?;
299
0
            let are_words = (next - prev_point) > U8_MAX;
300
0
            let run_len = points
301
0
                .iter()
302
0
                .take(MAX_POINTS_PER_RUN)
303
0
                .scan(prev_point, |prev, point| {
304
0
                    let take_this = if are_words {
305
0
                        (point - *prev) > U8_MAX
306
                    } else {
307
0
                        (point - *prev) <= U8_MAX
308
                    };
309
0
                    *prev = *point;
310
0
                    take_this.then_some(point)
311
0
                })
312
0
                .count();
313
314
0
            let (head, tail) = points.split_at(run_len);
315
0
            points = tail;
316
0
            let last_point = prev_point;
317
0
            prev_point = head.last().copied().unwrap();
318
319
0
            Some(PackedPointRun {
320
0
                last_point,
321
0
                are_words,
322
0
                points: head,
323
0
            })
324
0
        })
325
0
    }
326
}
327
328
#[derive(Debug, PartialEq, Eq)]
329
struct PackedPointRun<'a> {
330
    last_point: u16,
331
    are_words: bool,
332
    points: &'a [u16],
333
}
334
335
impl PackedPointRun<'_> {
336
0
    fn compute_size(&self) -> u16 {
337
        const LEN_BYTE: u16 = 1;
338
0
        let per_point_len = if self.are_words { 2 } else { 1 };
339
0
        self.points.len() as u16 * per_point_len + LEN_BYTE
340
0
    }
341
}
342
343
impl FontWrite for PackedPointRun<'_> {
344
0
    fn write_into(&self, writer: &mut TableWriter) {
345
0
        assert!(!self.points.is_empty() && self.points.len() <= 128);
346
0
        let mut len = self.points.len() as u8 - 1;
347
0
        if self.are_words {
348
0
            len |= 0x80;
349
0
        }
350
0
        len.write_into(writer);
351
0
        let mut last_point = self.last_point;
352
0
        for point in self.points {
353
0
            let delta = point - last_point;
354
0
            last_point = *point;
355
0
            if self.are_words {
356
0
                delta.write_into(writer);
357
0
            } else {
358
0
                debug_assert!(delta <= u8::MAX as u16);
359
0
                (delta as u8).write_into(writer);
360
            }
361
        }
362
0
    }
363
}
364
365
impl FontWrite for TupleIndex {
366
0
    fn write_into(&self, writer: &mut TableWriter) {
367
0
        self.bits().write_into(writer)
368
0
    }
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
373
impl<'a> FromObjRef<Option<read_fonts::tables::variations::Tuple<'a>>> for Vec<F2Dot14> {
374
0
    fn from_obj_ref(
375
0
        from: &Option<read_fonts::tables::variations::Tuple<'a>>,
376
0
        _data: FontData,
377
0
    ) -> Self {
378
0
        from.as_ref()
379
0
            .map(|tup| tup.values.iter().map(BigEndian::get).collect())
380
0
            .unwrap_or_default()
381
0
    }
382
}
383
384
impl Tuple {
385
0
    pub fn len(&self) -> u16 {
386
0
        self.values.len().try_into().unwrap()
387
0
    }
388
389
0
    pub fn is_empty(&self) -> bool {
390
0
        self.values.is_empty()
391
0
    }
392
}
393
394
impl 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
0
    fn get_entry_format(mapping: &[u32]) -> EntryFormat {
403
0
        let ored = mapping.iter().fold(0, |acc, idx| acc | *idx);
404
405
0
        let inner = (ored & 0xFFFF) as u16;
406
0
        let inner_bits = (16 - inner.leading_zeros() as u8).max(1);
407
0
        assert!(inner_bits <= 16);
408
409
0
        let ored = (ored >> (16 - inner_bits)) | (ored & ((1 << inner_bits) - 1));
410
0
        let entry_size = match ored {
411
0
            0..=0xFF => 1,
412
0
            0x100..=0xFFFF => 2,
413
0
            0x10000..=0xFFFFFF => 3,
414
0
            _ => 4,
415
        };
416
417
0
        EntryFormat::from_bits(((entry_size - 1) << 4) | (inner_bits - 1)).unwrap()
418
0
    }
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
0
    fn pack_map_data(mapping: &[u32]) -> (EntryFormat, Vec<u8>) {
429
0
        let fmt = DeltaSetIndexMap::get_entry_format(mapping);
430
0
        let inner_bits = fmt.bit_count();
431
0
        let inner_mask = (1 << inner_bits as u32) - 1;
432
0
        let outer_shift = 16 - inner_bits;
433
0
        let entry_size = fmt.entry_size();
434
0
        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
0
        let mut map_count = mapping.len();
439
0
        while map_count > 1 && mapping[map_count - 1] == mapping[map_count - 2] {
440
0
            map_count -= 1;
441
0
        }
442
443
0
        let mut map_data = Vec::with_capacity(map_count * entry_size as usize);
444
0
        for idx in mapping.iter().take(map_count) {
445
0
            let idx = ((idx & 0xFFFF0000) >> outer_shift) | (idx & inner_mask);
446
0
            // append entry_size bytes to map_data in BigEndian order
447
0
            map_data.extend_from_slice(&idx.to_be_bytes()[4 - entry_size as usize..]);
448
0
        }
449
0
        assert_eq!(map_data.len(), map_count * entry_size as usize);
450
0
        (fmt, map_data)
451
0
    }
452
}
453
454
impl<I> FromIterator<I> for DeltaSetIndexMap
455
where
456
    I: Into<u32>,
457
{
458
    /// Create a DeltaSetIndexMap from an iterator of delta-set indices.
459
0
    fn from_iter<T: IntoIterator<Item = I>>(iter: T) -> Self {
460
0
        let mapping: Vec<u32> = iter.into_iter().map(|v| v.into()).collect();
461
0
        let (fmt, map_data) = DeltaSetIndexMap::pack_map_data(&mapping);
462
0
        let map_count = map_data.len() / fmt.entry_size() as usize;
463
0
        let delta_set_index_map: DeltaSetIndexMap = if map_count <= u16::MAX as usize {
464
0
            DeltaSetIndexMap::format_0(fmt, map_count as u16, map_data)
465
        } else {
466
0
            DeltaSetIndexMap::format_1(fmt, map_count as u32, map_data)
467
        };
468
0
        delta_set_index_map
469
0
    }
470
}
471
472
#[cfg(test)]
473
mod 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
}