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