Skip to main content

write_fonts/tables/
variations.rs

1//! OpenType variations common table formats
2
3include!("../../generated/generated_variations.rs");
4
5use indexmap::IndexMap;
6
7pub use read_fonts::tables::variations::{DeltaRunType, TupleIndex, TupleVariationCount};
8
9pub mod common_builder;
10pub mod ivs_builder;
11pub mod mivs_builder;
12
13/// The influence of a single axis on a variation region.
14///
15/// The values here end up serialized in peak/start/end tuples in
16/// [`TupleVariationHeader`].
17///
18/// The name "Tent" is taken from HarfBuzz.
19#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
21pub struct Tent {
22    peak: F2Dot14,
23    min: F2Dot14,
24    max: F2Dot14,
25}
26
27impl Tent {
28    /// Construct a new tent from a peak value and optional intermediate values.
29    ///
30    /// If the intermediate values are `None`, they will be inferred from the
31    /// peak value.
32    pub fn new(peak: F2Dot14, intermediate: Option<(F2Dot14, F2Dot14)>) -> Self {
33        let (min, max) = intermediate.unwrap_or_else(|| Tent::implied_intermediates_for_peak(peak));
34        Self { peak, min, max }
35    }
36
37    pub(crate) fn peak(&self) -> F2Dot14 {
38        self.peak
39    }
40
41    pub(crate) fn bounds(&self) -> (F2Dot14, F2Dot14) {
42        (self.min, self.max)
43    }
44
45    pub(crate) fn requires_intermediate(&self) -> bool {
46        (self.min, self.max) != Self::implied_intermediates_for_peak(self.peak)
47    }
48
49    pub(crate) fn implied_intermediates_for_peak(peak: F2Dot14) -> (F2Dot14, F2Dot14) {
50        (peak.min(F2Dot14::ZERO), peak.max(F2Dot14::ZERO))
51    }
52}
53
54/// Like [Iterator::max_by_key][1] but returns the first instead of last in case of a tie.
55///
56/// Intended to match Python's [max()][2] behavior.
57///
58/// [1]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.max_by_key
59/// [2]: https://docs.python.org/3/library/functions.html#max
60pub(crate) fn max_by_first_key<I, B, F>(iter: I, mut key: F) -> Option<I::Item>
61where
62    I: Iterator,
63    B: Ord,
64    F: FnMut(&I::Item) -> B,
65{
66    iter.fold(None, |max_elem: Option<(_, _)>, item| {
67        let item_key = key(&item);
68        match &max_elem {
69            // current item's key not greater than max key so far, keep max unchanged
70            Some((_, max_key)) if item_key <= *max_key => max_elem,
71            // either no max yet, or a new max found, update max
72            _ => Some((item, item_key)),
73        }
74    })
75    .map(|(item, _)| item)
76}
77
78#[derive(Clone, Debug)]
79pub struct Deltas<T> {
80    pub peak_tuple: Tuple,
81    // start and end tuples of optional intermediate region
82    pub intermediate_region: Option<(Tuple, Tuple)>,
83    // delta values for points in the variation, in the same order as the point numbers in `points`
84    pub deltas: Vec<T>,
85    pub best_point_packing: PackedPointNumbers,
86}
87
88/// Determine if we should use 'shared point numbers'
89///
90/// If multiple tuple variations for a given glyph use the same point numbers,
91/// it is possible to store this in the glyph table, avoiding duplicating
92/// data.
93///
94/// This implementation is currently based on the one in fonttools, where it
95/// is part of the compileTupleVariationStore method:
96/// <https://github.com/fonttools/fonttools/blob/0a3360e52727cdefce2e9b28286b074faf99033c/Lib/fontTools/ttLib/tables/TupleVariation.py#L641>
97///
98/// # Note
99///
100/// There is likely room for some optimization here, depending on the
101/// structure of the point numbers. If it is common for point numbers to only
102/// vary by an item or two, it may be worth picking a set of shared points
103/// that is a subset of multiple different tuples; this would mean you could
104/// make some tuples include deltas that they might otherwise omit, but let
105/// them omit their explicit point numbers.
106///
107/// For fonts with a large number of variations, this could produce reasonable
108/// savings, at the cost of a significantly more complicated algorithm.
109///
110/// (issue <https://github.com/googlefonts/fontations/issues/634>)
111///
112/// If multiple tuple variations use the same point-number encoding, sharing can
113/// avoid duplicate serialized data.
114///
115/// The scoring and tie-breaking behavior matches fonttools:
116/// - only point sets with more than one occurrence are candidates
117/// - candidate score is `(count - 1) * encoded_size`
118/// - ties pick the first occurrence in iteration order
119pub(crate) fn compute_shared_points<T>(variations: &[Deltas<T>]) -> Option<PackedPointNumbers> {
120    let mut point_number_counts = IndexMap::new();
121    // count how often each set of numbers occurs
122    for deltas in variations {
123        // for each set points, get compiled size + number of occurrences
124        let (_, count) = point_number_counts
125            .entry(&deltas.best_point_packing)
126            .or_insert_with(|| {
127                let size = deltas.best_point_packing.compute_size();
128                (size as usize, 0usize)
129            });
130        *count += 1;
131    }
132
133    let (pts, _) = max_by_first_key(
134        point_number_counts
135            .into_iter()
136            // no use sharing points if they only occur once
137            .filter(|(_, (_, count))| *count > 1),
138        |(_, (size, count))| (*count - 1) * *size,
139    )?;
140
141    Some(pts.to_owned())
142}
143
144/// Compute tupleVariationCount bits from header count and shared-point usage.
145pub(crate) fn compute_tuple_variation_count(
146    n_headers: usize,
147    has_shared_points: bool,
148) -> TupleVariationCount {
149    assert!(n_headers <= 4095);
150    let mut bits = n_headers as u16;
151    if has_shared_points {
152        bits |= TupleVariationCount::SHARED_POINT_NUMBERS;
153    }
154    TupleVariationCount::from_bits(bits)
155}
156
157/// Compute data offset for a tuple variation store.
158///
159/// `header_prefix_len` is the number of bytes before `tupleVariationHeaders`.
160pub(crate) fn compute_tuple_variation_data_offset<T: TryFrom<usize>>(
161    headers: &[TupleVariationHeader],
162    header_prefix_len: usize,
163) -> T {
164    let header_len = headers.iter().fold(0usize, |acc, header| {
165        acc.checked_add(header.compute_size_wide()).unwrap()
166    });
167    (header_prefix_len + header_len).try_into().ok().unwrap()
168}
169
170impl TupleVariationHeader {
171    pub fn new(
172        variation_data_size: u16,
173        shared_tuple_idx: Option<u16>,
174        peak_tuple: Option<Tuple>,
175        intermediate_region: Option<(Tuple, Tuple)>,
176        has_private_points: bool,
177    ) -> Self {
178        assert!(
179            shared_tuple_idx.is_some() != peak_tuple.is_some(),
180            "one and only one of peak_tuple or shared_tuple_idx must be present"
181        );
182        let mut idx = shared_tuple_idx.unwrap_or_default();
183        if peak_tuple.is_some() {
184            idx |= TupleIndex::EMBEDDED_PEAK_TUPLE;
185        }
186        if intermediate_region.is_some() {
187            idx |= TupleIndex::INTERMEDIATE_REGION;
188        }
189        if has_private_points {
190            idx |= TupleIndex::PRIVATE_POINT_NUMBERS;
191        }
192        let (intermediate_start_tuple, intermediate_end_tuple) = intermediate_region
193            .map(|(start, end)| (start.values, end.values))
194            .unwrap_or_default();
195
196        TupleVariationHeader {
197            variation_data_size,
198            tuple_index: TupleIndex::from_bits(idx),
199            peak_tuple: peak_tuple.map(|tup| tup.values).unwrap_or_default(),
200            intermediate_start_tuple,
201            intermediate_end_tuple,
202        }
203    }
204
205    /// Return the number of bytes required to encode this header
206    pub fn compute_size(&self) -> u16 {
207        self.compute_size_wide().try_into().unwrap()
208    }
209
210    pub(crate) fn compute_size_wide(&self) -> usize {
211        2 + 2 // variationDataSize, tupleIndex
212        + self.peak_tuple.len() * F2Dot14::RAW_BYTE_LEN
213        + self.intermediate_start_tuple.len()  * F2Dot14::RAW_BYTE_LEN
214        + self.intermediate_end_tuple.len()  * F2Dot14::RAW_BYTE_LEN
215    }
216}
217
218/// <https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#packed-point-numbers>
219#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
220#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
221pub enum PackedPointNumbers {
222    /// Contains deltas for all point numbers
223    #[default]
224    All,
225    /// Contains deltas only for these specific point numbers
226    Some(Vec<u16>),
227}
228
229/// <https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#packed-deltas>
230#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
231#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
232pub struct PackedDeltas {
233    deltas: Vec<i32>,
234}
235
236impl Validate for PackedDeltas {
237    fn validate_impl(&self, _ctx: &mut ValidationCtx) {}
238}
239
240impl FontWrite for PackedDeltas {
241    fn write_into(&self, writer: &mut TableWriter) {
242        for run in self.iter_runs() {
243            run.write_into(writer)
244        }
245    }
246}
247
248impl PackedDeltas {
249    /// Construct a `PackedDeltas` from a vector of raw delta values.
250    pub fn new(deltas: Vec<i32>) -> Self {
251        Self { deltas }
252    }
253
254    /// Compute the number of bytes required to encode these deltas
255    pub(crate) fn compute_size(&self) -> u16 {
256        self.iter_runs().fold(0u16, |acc, run| {
257            acc.checked_add(run.compute_size()).unwrap()
258        })
259    }
260
261    fn iter_runs(&self) -> impl Iterator<Item = PackedDeltaRun<'_>> {
262        // 6 bits for length per https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#packed-deltas
263        const MAX_POINTS_PER_RUN: usize = 64;
264
265        // preferred run type for this value
266        fn preferred_run_type(v: i32) -> DeltaRunType {
267            match v {
268                0 => DeltaRunType::Zero,
269                _ if v > i16::MAX as i32 || v < i16::MIN as i32 => DeltaRunType::I32,
270                _ if v > i8::MAX as i32 || v < i8::MIN as i32 => DeltaRunType::I16,
271                _ => DeltaRunType::I8,
272            }
273        }
274
275        fn count_leading_zeros(slice: &[i32]) -> u8 {
276            slice
277                .iter()
278                .take(MAX_POINTS_PER_RUN)
279                .take_while(|v| **v == 0)
280                .count() as u8
281        }
282
283        /// compute the number of deltas in the next run, and the value type
284        fn next_run_len(slice: &[i32]) -> (usize, DeltaRunType) {
285            let first = *slice.first().expect("bounds checked before here");
286            debug_assert!(first != 0, "Zeroes are supposed to be handled separately");
287            let run_type = preferred_run_type(first);
288
289            let mut idx = 1;
290            while idx < MAX_POINTS_PER_RUN && idx < slice.len() {
291                let cur = slice[idx];
292                let cur_type = preferred_run_type(cur);
293                let next_type = slice.get(idx + 1).copied().map(preferred_run_type);
294
295                // Any reason to stop?
296                if run_type == DeltaRunType::I8 {
297                    // a single zero is best stored literally inline, but two or more
298                    // should get a new run:
299                    // https://github.com/fonttools/fonttools/blob/eeaa499981c587/Lib/fontTools/ttLib/tables/TupleVariation.py#L423
300                    match cur_type {
301                        DeltaRunType::Zero if next_type == Some(DeltaRunType::Zero) => break,
302                        DeltaRunType::I16 | DeltaRunType::I32 => break,
303                        _ => (),
304                    }
305                } else if run_type == DeltaRunType::I16 {
306                    // with word deltas, a single zero justifies a new run:
307                    //https://github.com/fonttools/fonttools/blob/eeaa499981c587/Lib/fontTools/ttLib/tables/TupleVariation.py#L457
308                    match (cur_type, next_type) {
309                        (DeltaRunType::Zero | DeltaRunType::I32, _) => break,
310                        // and a single byte-size value should be inlined, if it lets
311                        // us combine two adjoining word-size runs:
312                        // https://github.com/fonttools/fonttools/blob/eeaa499981c587/Lib/fontTools/ttLib/tables/TupleVariation.py#L467
313                        (DeltaRunType::I8, Some(DeltaRunType::Zero | DeltaRunType::I8)) => break,
314                        _ => (),
315                    }
316                } else if run_type == DeltaRunType::I32 && cur_type != DeltaRunType::I32 {
317                    break;
318                }
319
320                idx += 1;
321            }
322            (idx, run_type)
323        }
324
325        let mut deltas = self.deltas.as_slice();
326
327        std::iter::from_fn(move || {
328            let run_start = *deltas.first()?;
329            if run_start == 0 {
330                let n_zeros = count_leading_zeros(deltas);
331                deltas = &deltas[n_zeros as usize..];
332                Some(PackedDeltaRun::Zeros(n_zeros))
333            } else {
334                let (len, value_type) = next_run_len(deltas);
335                let (head, tail) = deltas.split_at(len);
336                deltas = tail;
337                Some(match value_type {
338                    DeltaRunType::I32 => PackedDeltaRun::FourBytes(head),
339                    DeltaRunType::I16 => PackedDeltaRun::TwoBytes(head),
340                    DeltaRunType::I8 => PackedDeltaRun::OneByte(head),
341                    _ => {
342                        unreachable!("We should have taken the other branch for first={run_start}")
343                    }
344                })
345            }
346        })
347    }
348}
349
350#[derive(Clone, Debug, PartialEq, Eq)]
351enum PackedDeltaRun<'a> {
352    Zeros(u8),
353    OneByte(&'a [i32]),
354    TwoBytes(&'a [i32]),
355    FourBytes(&'a [i32]),
356}
357
358impl PackedDeltaRun<'_> {
359    fn compute_flag(&self) -> u8 {
360        /// Flag indicating that this run contains no data,
361        /// and that the deltas for this run are all zero.
362        const DELTAS_ARE_ZERO: u8 = 0x80;
363        /// Flag indicating the data type for delta values in the run.
364        const DELTAS_ARE_WORDS: u8 = 0x40;
365
366        match self {
367            PackedDeltaRun::Zeros(count) => (count - 1) | DELTAS_ARE_ZERO,
368            PackedDeltaRun::OneByte(deltas) => deltas.len() as u8 - 1,
369            PackedDeltaRun::TwoBytes(deltas) => (deltas.len() as u8 - 1) | DELTAS_ARE_WORDS,
370            PackedDeltaRun::FourBytes(deltas) => {
371                (deltas.len() as u8 - 1) | DELTAS_ARE_WORDS | DELTAS_ARE_ZERO
372            }
373        }
374    }
375
376    fn compute_size(&self) -> u16 {
377        match self {
378            PackedDeltaRun::Zeros(_) => 1,
379            PackedDeltaRun::OneByte(vals) => vals.len() as u16 + 1,
380            PackedDeltaRun::TwoBytes(vals) => vals.len() as u16 * 2 + 1,
381            PackedDeltaRun::FourBytes(vals) => vals.len() as u16 * 4 + 1,
382        }
383    }
384}
385
386impl FontWrite for PackedDeltaRun<'_> {
387    fn write_into(&self, writer: &mut TableWriter) {
388        self.compute_flag().write_into(writer);
389        match self {
390            PackedDeltaRun::Zeros(_) => (),
391            PackedDeltaRun::OneByte(deltas) => {
392                deltas.iter().for_each(|v| (*v as i8).write_into(writer))
393            }
394            PackedDeltaRun::TwoBytes(deltas) => {
395                deltas.iter().for_each(|v| (*v as i16).write_into(writer))
396            }
397            PackedDeltaRun::FourBytes(deltas) => deltas.iter().for_each(|v| v.write_into(writer)),
398        }
399    }
400}
401
402impl crate::validate::Validate for PackedPointNumbers {
403    fn validate_impl(&self, ctx: &mut ValidationCtx) {
404        if let PackedPointNumbers::Some(pts) = self {
405            if pts.len() > 0x7FFF {
406                ctx.report("length cannot be stored in 15 bites");
407            }
408        }
409    }
410}
411
412impl FontWrite for PackedPointNumbers {
413    fn write_into(&self, writer: &mut TableWriter) {
414        // compute the actual count:
415        match self.as_slice().len() {
416            len @ 0..=127 => (len as u8).write_into(writer),
417            len => (len as u16 | 0x8000u16).write_into(writer),
418        }
419        for run in self.iter_runs() {
420            run.write_into(writer);
421        }
422    }
423}
424
425impl PackedPointNumbers {
426    /// Compute the number of bytes required to encode these points
427    pub(crate) fn compute_size(&self) -> u16 {
428        let mut count = match self {
429            PackedPointNumbers::All => return 1,
430            PackedPointNumbers::Some(pts) if pts.len() < 128 => 1u16,
431            PackedPointNumbers::Some(_) => 2,
432        };
433        for run in self.iter_runs() {
434            count = count.checked_add(run.compute_size()).unwrap();
435        }
436        count
437    }
438
439    fn as_slice(&self) -> &[u16] {
440        match self {
441            PackedPointNumbers::All => &[],
442            PackedPointNumbers::Some(pts) => pts.as_slice(),
443        }
444    }
445
446    fn iter_runs(&self) -> impl Iterator<Item = PackedPointRun<'_>> {
447        const U8_MAX: u16 = u8::MAX as u16;
448        const MAX_POINTS_PER_RUN: usize = 128;
449
450        let mut points = match self {
451            PackedPointNumbers::Some(pts) => pts.as_slice(),
452            PackedPointNumbers::All => &[],
453        };
454
455        let mut prev_point = 0u16;
456
457        // split a run off the front of points:
458        // - if point is more than 255 away from prev, we're using words
459        std::iter::from_fn(move || {
460            let next = points.first()?;
461            let are_words = (next - prev_point) > U8_MAX;
462            let run_len = points
463                .iter()
464                .take(MAX_POINTS_PER_RUN)
465                .scan(prev_point, |prev, point| {
466                    let take_this = if are_words {
467                        (point - *prev) > U8_MAX
468                    } else {
469                        (point - *prev) <= U8_MAX
470                    };
471                    *prev = *point;
472                    take_this.then_some(point)
473                })
474                .count();
475
476            let (head, tail) = points.split_at(run_len);
477            points = tail;
478            let last_point = prev_point;
479            prev_point = head.last().copied().unwrap();
480
481            Some(PackedPointRun {
482                last_point,
483                are_words,
484                points: head,
485            })
486        })
487    }
488}
489
490#[derive(Debug, PartialEq, Eq)]
491struct PackedPointRun<'a> {
492    last_point: u16,
493    are_words: bool,
494    points: &'a [u16],
495}
496
497impl PackedPointRun<'_> {
498    fn compute_size(&self) -> u16 {
499        const LEN_BYTE: u16 = 1;
500        let per_point_len = if self.are_words { 2 } else { 1 };
501        self.points.len() as u16 * per_point_len + LEN_BYTE
502    }
503}
504
505impl FontWrite for PackedPointRun<'_> {
506    fn write_into(&self, writer: &mut TableWriter) {
507        assert!(!self.points.is_empty() && self.points.len() <= 128);
508        let mut len = self.points.len() as u8 - 1;
509        if self.are_words {
510            len |= 0x80;
511        }
512        len.write_into(writer);
513        let mut last_point = self.last_point;
514        for point in self.points {
515            let delta = point - last_point;
516            last_point = *point;
517            if self.are_words {
518                delta.write_into(writer);
519            } else {
520                debug_assert!(delta <= u8::MAX as u16);
521                (delta as u8).write_into(writer);
522            }
523        }
524    }
525}
526
527impl FontWrite for TupleIndex {
528    fn write_into(&self, writer: &mut TableWriter) {
529        self.bits().write_into(writer)
530    }
531}
532
533//hack: unclear if we're even going to do any codegen for writing, but
534//for the time being this lets us compile
535impl<'a> FromObjRef<Option<read_fonts::tables::variations::Tuple<'a>>> for Vec<F2Dot14> {
536    fn from_obj_ref(
537        from: &Option<read_fonts::tables::variations::Tuple<'a>>,
538        _data: FontData,
539    ) -> Self {
540        from.as_ref()
541            .map(|tup| tup.values.iter().map(BigEndian::get).collect())
542            .unwrap_or_default()
543    }
544}
545
546impl Tuple {
547    pub fn len(&self) -> u16 {
548        self.values.len().try_into().unwrap()
549    }
550
551    pub fn is_empty(&self) -> bool {
552        self.values.is_empty()
553    }
554}
555
556impl DeltaSetIndexMap {
557    /// Return the most compact entry format that can represent this mapping.
558    ///
559    /// EntryFormat is a packed u8 field that describes the compressed representation
560    /// of delta-set indices. For more info, see:
561    /// <https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#associating-target-items-to-variation-data>
562    // This is a direct port from fonttools' DeltaSetMap.getEntryFormat:
563    // https://github.com/fonttools/fonttools/blob/6d531f/Lib/fontTools/ttLib/tables/otTables.py#L644-L666
564    fn get_entry_format(mapping: &[u32]) -> EntryFormat {
565        let ored = mapping.iter().fold(0, |acc, idx| acc | *idx);
566
567        let inner = (ored & 0xFFFF) as u16;
568        let inner_bits = (16 - inner.leading_zeros() as u8).max(1);
569        assert!(inner_bits <= 16);
570
571        let ored = (ored >> (16 - inner_bits)) | (ored & ((1 << inner_bits) - 1));
572        let entry_size = match ored {
573            0..=0xFF => 1,
574            0x100..=0xFFFF => 2,
575            0x10000..=0xFFFFFF => 3,
576            _ => 4,
577        };
578
579        EntryFormat::from_bits(((entry_size - 1) << 4) | (inner_bits - 1)).unwrap()
580    }
581
582    /// Compress u32's into packed data using the most compact entry format.
583    ///
584    /// Returns the computed entry format and the packed data.
585    ///
586    /// For more info, see:
587    /// <https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#associating-target-items-to-variation-data>
588    // Ported from fonttools' VarIdxMapValue.write method:
589    // https://github.com/fonttools/fonttools/blob/6d531fe/Lib/fontTools/ttLib/tables/otConverters.py#L1764-L1781
590    fn pack_map_data(mapping: &[u32]) -> (EntryFormat, Vec<u8>) {
591        let fmt = DeltaSetIndexMap::get_entry_format(mapping);
592        let inner_bits = fmt.bit_count();
593        let inner_mask = (1 << inner_bits as u32) - 1;
594        let outer_shift = 16 - inner_bits;
595        let entry_size = fmt.entry_size();
596        assert!((1..=4).contains(&entry_size));
597
598        // omit trailing entries that are the same as the previous one;
599        // the last entry is assumed when index is >= map_count
600        let mut map_count = mapping.len();
601        while map_count > 1 && mapping[map_count - 1] == mapping[map_count - 2] {
602            map_count -= 1;
603        }
604
605        let mut map_data = Vec::with_capacity(map_count * entry_size as usize);
606        for idx in mapping.iter().take(map_count) {
607            let idx = ((idx & 0xFFFF0000) >> outer_shift) | (idx & inner_mask);
608            // append entry_size bytes to map_data in BigEndian order
609            map_data.extend_from_slice(&idx.to_be_bytes()[4 - entry_size as usize..]);
610        }
611        assert_eq!(map_data.len(), map_count * entry_size as usize);
612        (fmt, map_data)
613    }
614}
615
616impl<I> FromIterator<I> for DeltaSetIndexMap
617where
618    I: Into<u32>,
619{
620    /// Create a DeltaSetIndexMap from an iterator of delta-set indices.
621    fn from_iter<T: IntoIterator<Item = I>>(iter: T) -> Self {
622        let mapping: Vec<u32> = iter.into_iter().map(|v| v.into()).collect();
623        let (fmt, map_data) = DeltaSetIndexMap::pack_map_data(&mapping);
624        let map_count = map_data.len() / fmt.entry_size() as usize;
625        let delta_set_index_map: DeltaSetIndexMap = if map_count <= u16::MAX as usize {
626            DeltaSetIndexMap::format_0(fmt, map_count as u16, map_data)
627        } else {
628            DeltaSetIndexMap::format_1(fmt, map_count as u32, map_data)
629        };
630        delta_set_index_map
631    }
632}
633
634/// An error representing invalid input when building a tuple variation store
635#[derive(Clone, Debug)]
636pub enum TupleVariationStoreInputError<T> {
637    /// Glyph variations do not have the expected axis count
638    UnexpectedAxisCount {
639        index: T,
640        expected: u16,
641        actual: u16,
642    },
643    /// A single entry contains variations with inconsistent axis counts
644    InconsistentAxisCount(T),
645    /// A single entry contains variations with different delta counts
646    InconsistentDeltaLength(T),
647    /// A variation in this entry contains an intermediate region with a
648    /// different length than the peak.
649    InconsistentTupleLengths(T),
650}
651
652impl<T: std::fmt::Display> std::fmt::Display for TupleVariationStoreInputError<T> {
653    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
654        match self {
655            TupleVariationStoreInputError::UnexpectedAxisCount {
656                index,
657                expected,
658                actual,
659            } => {
660                write!(
661                    f,
662                    "Expected {} axes for entry {}, got {}",
663                    expected, index, actual
664                )
665            }
666            TupleVariationStoreInputError::InconsistentAxisCount(gid) => write!(
667                f,
668                "Entry {gid} contains variations with inconsistent axis counts"
669            ),
670            TupleVariationStoreInputError::InconsistentDeltaLength(gid) => write!(
671                f,
672                "Entry {gid} contains variations with inconsistent delta counts"
673            ),
674            TupleVariationStoreInputError::InconsistentTupleLengths(gid) => write!(
675                f,
676                "Entry {gid} contains variations with inconsistent intermediate region sizes"
677            ),
678        }
679    }
680}
681
682impl<T: std::fmt::Display + std::fmt::Debug> std::error::Error
683    for TupleVariationStoreInputError<T>
684{
685}
686
687#[cfg(test)]
688mod tests {
689    use super::*;
690    use rstest::rstest;
691
692    #[test]
693    fn point_pack_words() {
694        let thing = PackedPointNumbers::Some(vec![1002, 2002, 8408, 12228]);
695
696        let runs = thing.iter_runs().collect::<Vec<_>>();
697        assert_eq!(runs.len(), 1);
698        assert!(runs[0].are_words);
699        assert_eq!(runs[0].last_point, 0);
700        assert_eq!(runs[0].points, &[1002, 2002, 8408, 12228]);
701    }
702
703    #[test]
704    fn serialize_packed_points() {
705        let thing = PackedPointNumbers::Some(vec![1002, 2002, 8408, 12228]);
706
707        let bytes = crate::dump_table(&thing).unwrap();
708        assert_eq!(thing.compute_size() as usize, bytes.len());
709        let (read, _) = read_fonts::tables::variations::PackedPointNumbers::split_off_front(
710            FontData::new(&bytes),
711        );
712        assert_eq!(thing.as_slice(), read.iter().collect::<Vec<_>>());
713    }
714
715    #[test]
716    fn point_pack_runs() {
717        let thing = PackedPointNumbers::Some(vec![5, 25, 225, 1002, 2002, 2008, 2228]);
718
719        let runs = thing.iter_runs().collect::<Vec<_>>();
720        assert!(!runs[0].are_words);
721        assert_eq!(runs[0].last_point, 0);
722        assert_eq!(runs[0].points, &[5, 25, 225]);
723
724        assert!(runs[1].are_words);
725        assert_eq!(runs[1].last_point, 225);
726        assert_eq!(runs[1].points, &[1002, 2002]);
727
728        assert!(!runs[2].are_words);
729        assert_eq!(runs[2].last_point, 2002);
730        assert_eq!(runs[2].points, &[2008, 2228]);
731
732        assert_eq!(runs.len(), 3);
733    }
734
735    #[test]
736    fn point_pack_long_runs() {
737        let mut numbers = vec![0u16; 130];
738        numbers.extend(1u16..=130u16);
739        let thing = PackedPointNumbers::Some(numbers);
740
741        let runs = thing.iter_runs().collect::<Vec<_>>();
742        assert!(!runs[0].are_words);
743        assert_eq!(runs[0].points.len(), 128);
744        assert_eq!(runs[1].last_point, 0);
745        assert_eq!(runs[1].points.len(), 128);
746        assert_eq!(runs[2].last_point, 126);
747        assert_eq!(runs[2].points, &[127, 128, 129, 130]);
748        assert!(runs.get(3).is_none());
749    }
750
751    #[test]
752    fn point_pack_write_one_byte() {
753        let thing = PackedPointNumbers::Some(vec![5, 25, 225, 1002, 2002, 2008, 2228, 10000]);
754
755        let bytes = crate::dump_table(&thing).unwrap();
756        assert_eq!(thing.compute_size() as usize, bytes.len());
757        let (read, _) = read_fonts::tables::variations::PackedPointNumbers::split_off_front(
758            FontData::new(&bytes),
759        );
760        assert_eq!(thing.as_slice(), read.iter().collect::<Vec<_>>());
761    }
762
763    #[test]
764    fn point_pack_write_two_byte() {
765        let thing = PackedPointNumbers::Some(vec![0; 200]);
766
767        let bytes = crate::dump_table(&thing).unwrap();
768        assert_eq!(thing.compute_size() as usize, bytes.len());
769        let (read, _) = read_fonts::tables::variations::PackedPointNumbers::split_off_front(
770            FontData::new(&bytes),
771        );
772        assert_eq!(thing.as_slice(), read.iter().collect::<Vec<_>>());
773    }
774
775    static PACKED_DELTA_BYTES: &[u8] = &[
776        0x03, 0x0A, 0x97, 0x00, 0xC6, 0x87, 0x41, 0x10, 0x22, 0xFB, 0x34,
777    ];
778
779    // <https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#packed-deltas>
780    #[test]
781    fn packed_deltas_spec_runs() {
782        let deltas = PackedDeltas::new(vec![10, -105, 0, -58, 0, 0, 0, 0, 0, 0, 0, 0, 4130, -1228]);
783        let runs = deltas.iter_runs().collect::<Vec<_>>();
784        assert_eq!(
785            runs,
786            vec![
787                PackedDeltaRun::OneByte(&[10, -105, 0, -58]),
788                PackedDeltaRun::Zeros(8),
789                PackedDeltaRun::TwoBytes(&[4130, -1228]),
790            ]
791        );
792    }
793
794    #[test]
795    fn packed_deltas_spec_write() {
796        let deltas = PackedDeltas::new(vec![10, -105, 0, -58, 0, 0, 0, 0, 0, 0, 0, 0, 4130, -1228]);
797        let bytes = crate::dump_table(&deltas).unwrap();
798        assert_eq!(bytes, PACKED_DELTA_BYTES);
799        let read = read_fonts::tables::variations::PackedDeltas::consume_all(FontData::new(&bytes));
800        let decoded = read.iter().collect::<Vec<_>>();
801        assert_eq!(deltas.deltas.len(), decoded.len());
802        assert_eq!(deltas.deltas, decoded);
803        assert_eq!(bytes, PACKED_DELTA_BYTES);
804    }
805
806    #[test]
807    fn empty_deltas() {
808        let deltas = PackedDeltas::new(vec![]);
809        let bytes = crate::dump_table(&deltas).unwrap();
810        assert!(bytes.is_empty());
811    }
812
813    #[test]
814    fn lots_of_zero() {
815        let num_zeroes = 65;
816        let deltas = PackedDeltas::new(vec![0; num_zeroes]);
817        assert_eq!(
818            vec![PackedDeltaRun::Zeros(64), PackedDeltaRun::Zeros(1)],
819            deltas.iter_runs().collect::<Vec<_>>()
820        );
821    }
822
823    #[test]
824    fn respect_my_run_length_authority() {
825        let mut values = (1..196).collect::<Vec<_>>();
826        values.extend([0, 0, 0]);
827        values.push(i16::MAX as i32 + 1);
828        values.push(i16::MIN as i32 - 1);
829        values.push(i16::MAX as i32 * 2);
830        let deltas = PackedDeltas::new(values);
831        assert_eq!(
832            vec![
833                // 64 entries per run please and thank you
834                PackedDeltaRun::OneByte(&(1..65).collect::<Vec<i32>>()),
835                // 63 entries this time because at 128 we switch to 2 bytes
836                PackedDeltaRun::OneByte(&(65..128).collect::<Vec<i32>>()),
837                // 64 per run again
838                PackedDeltaRun::TwoBytes(&(128..192).collect::<Vec<i32>>()),
839                // tail
840                PackedDeltaRun::TwoBytes(&(192..=195).collect::<Vec<i32>>()),
841                PackedDeltaRun::Zeros(3),
842                PackedDeltaRun::FourBytes(&[
843                    i16::MAX as i32 + 1,
844                    i16::MIN as i32 - 1,
845                    i16::MAX as i32 * 2
846                ]),
847            ],
848            deltas.iter_runs().collect::<Vec<_>>()
849        )
850    }
851
852    #[test]
853    fn inline_single_zeros_with_bytes() {
854        let packed = PackedDeltas::new(vec![1, 2, 0, 3]);
855        assert_eq!(packed.iter_runs().count(), 1)
856    }
857
858    #[test]
859    fn split_two_zeros_in_bytes() {
860        let packed = PackedDeltas::new(vec![1, 2, 0, 0, 3]);
861        assert_eq!(packed.iter_runs().count(), 3)
862    }
863
864    #[test]
865    fn split_single_zero_in_words() {
866        let packed = PackedDeltas::new(vec![150, 200, 0, -300]);
867        assert_eq!(packed.iter_runs().count(), 3)
868    }
869
870    #[test]
871    fn inline_single_byte_in_words() {
872        let packed = PackedDeltas::new(vec![150, 200, 1, -300]);
873        assert_eq!(packed.iter_runs().count(), 1)
874    }
875
876    #[test]
877    fn split_double_byte_in_words() {
878        let packed = PackedDeltas::new(vec![150, 200, 1, 3, -300]);
879        assert_eq!(packed.iter_runs().count(), 3)
880    }
881
882    #[test]
883    fn split_byte_then_zero_after_words() {
884        // without split: 10 = 1 + 2 + 2 + 2 + 1 + 2
885        //    with split:  9 = 1 + 2 + 2 + 1 + 3
886        let packed = PackedDeltas::new(vec![150, 200, 1, 0, 1]);
887        assert_eq!(packed.iter_runs().count(), 2);
888        assert_eq!(packed.compute_size(), 9);
889    }
890
891    #[rstest]
892    // Note how the packed data below is b"\x00\x01" and not b"\x00\x01\x01", for the
893    // repeated trailing values can be omitted
894    #[case::one_byte_one_inner_bit(
895        vec![0, 1, 1], 0b00_0000, 1, 1, b"\x00\x01",
896    )]
897    #[case::one_byte_two_inner_bits(
898        vec![0, 1, 2], 0b00_0001, 1, 2, b"\x00\x01\x02",
899    )]
900    #[case::one_byte_three_inner_bits(
901        vec![0, 1, 4], 0b00_0010, 1, 3, b"\x00\x01\x04",
902    )]
903    #[case::one_byte_four_inner_bits(
904        vec![0, 1, 8], 0b00_0011, 1, 4, b"\x00\x01\x08",
905    )]
906    // 256 needs 2 bytes, of which 9 bits for the inner value
907    #[case::two_bytes_nine_inner_bits(
908        vec![0, 1, 256], 0b01_1000, 2, 9, b"\x00\x00\x00\x01\x01\x00",
909    )]
910    #[case::two_bytes_sixteen_inner_bits(
911        vec![0, 1, 0x8000], 0b01_1111, 2, 16, b"\x00\x00\x00\x01\x80\x00",
912    )]
913    // note this gets packed the same as case 'one_byte_two_inner_bits': [0, 1, 2]
914    // above, but it uses only 1 bit for the inner value, while the other bits are
915    // used for the outer value:
916    // 0x0001_0000 => b"\x02" => 0b00000010 => {outer: 1, inner: 0)
917    #[case::one_byte_one_inner_bit_two_vardatas(
918        vec![0, 1, 0x01_0000], 0b00_0000, 1, 1, b"\x00\x01\x02",
919    )]
920    #[case::three_bytes_sixteen_inner_bits(
921        vec![0, 0xFFFF, 0x01_0000],
922        0b10_1111,
923        3,
924        16,
925        b"\x00\x00\x00\x00\xFF\xFF\x01\x00\x00",
926    )]
927    #[case::four_bytes_sixteen_inner_bits(
928        vec![0, 0xFFFF, 0xFFFF_FFFF],
929        0b11_1111,
930        4,
931        16,
932        b"\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF",
933    )]
934    #[test]
935    fn delta_set_index_map_entry_format_and_packed_data(
936        #[case] mapping: Vec<u32>,
937        #[case] expected_format_bits: u8,
938        #[case] expected_entry_size: u8,
939        #[case] expected_inner_bit_count: u8,
940        #[case] expected_map_data: &[u8],
941    ) {
942        let (format, data) = DeltaSetIndexMap::pack_map_data(&mapping);
943        assert_eq!(format.bits(), expected_format_bits);
944        assert_eq!(format.entry_size(), expected_entry_size);
945        assert_eq!(format.bit_count(), expected_inner_bit_count);
946        assert_eq!(data, expected_map_data);
947
948        let dsim: DeltaSetIndexMap = mapping.iter().copied().collect();
949        // all test mappings have fewer than 65536 entries (for practical reasons)
950        // so we should generate a Format0
951        assert!(matches!(dsim, DeltaSetIndexMap::Format0 { .. }));
952
953        // make sure we get the same mapping back after round-tripping to/from bytes
954        let raw_dsim = crate::dump_table(&dsim).unwrap();
955        let dsim2 =
956            read_fonts::tables::variations::DeltaSetIndexMap::read(FontData::new(&raw_dsim))
957                .unwrap();
958        assert_eq!(
959            (0..mapping.len())
960                .map(|i| {
961                    let index = dsim2.get(i as u32).unwrap();
962                    ((index.outer as u32) << 16) | index.inner as u32
963                })
964                .collect::<Vec<_>>(),
965            mapping
966        );
967    }
968
969    #[test]
970    fn delta_set_index_map_from_variation_index_iterator() {
971        // as returned from VariationStoreBuilder::build() in the VariationIndexRemapping
972        use crate::tables::layout::VariationIndex;
973
974        let mapping = vec![
975            VariationIndex::new(0, 0),
976            VariationIndex::new(0, 1),
977            VariationIndex::new(0, 2),
978            VariationIndex::new(1, 0),
979            VariationIndex::new(1, 1),
980            VariationIndex::new(1, 2),
981        ];
982
983        let dsim: DeltaSetIndexMap = mapping.into_iter().collect();
984        let DeltaSetIndexMap::Format0(dsim) = dsim else {
985            panic!("expected DeltaSetIndexMap::Format0, got {:?}", dsim);
986        };
987        assert_eq!(dsim.map_count, 6);
988        assert_eq!(dsim.entry_format.bits(), 0b000001);
989        assert_eq!(dsim.entry_format.entry_size(), 1); // one byte per entry
990        assert_eq!(dsim.entry_format.bit_count(), 2);
991        // for each entry/byte, the right-most 2 bits are the inner value,
992        // the remaining bits are the outer value
993        assert_eq!(
994            dsim.map_data,
995            vec![
996                0b00_00, // (0, 0)
997                0b00_01, // (0, 1)
998                0b00_10, // (0, 2)
999                0b01_00, // (1, 0)
1000                0b01_01, // (1, 1)
1001                0b01_10, // (1, 2)
1002            ]
1003        );
1004    }
1005
1006    #[test]
1007    fn huge_mapping_generates_format_1_delta_set_index_map() {
1008        // 65536 entries, so we need a Format1 with u32 map_count
1009        let mapping = (0..=0xFFFF).collect::<Vec<u32>>();
1010        let map_count = mapping.len() as u32;
1011        let dsim: DeltaSetIndexMap = mapping.into_iter().collect();
1012        let DeltaSetIndexMap::Format1(dsim) = dsim else {
1013            panic!("expected DeltaSetIndexMap::Format1, got {:?}", dsim);
1014        };
1015        assert_eq!(dsim.map_count, map_count);
1016    }
1017}