ttf_parser/
var_store.rs

1//! Implementation of Item Variation Store
2//!
3//! <https://docs.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#item-variation-store>
4
5use crate::parser::{FromData, LazyArray16, NumFrom, Stream};
6use crate::NormalizedCoordinate;
7
8#[derive(Clone, Copy, Debug)]
9pub(crate) struct ItemVariationStore<'a> {
10    data: &'a [u8],
11    data_offsets: LazyArray16<'a, u32>,
12    pub regions: VariationRegionList<'a>,
13}
14
15impl<'a> Default for ItemVariationStore<'a> {
16    #[inline]
17    fn default() -> Self {
18        ItemVariationStore {
19            data: &[],
20            data_offsets: LazyArray16::new(&[]),
21            regions: VariationRegionList {
22                axis_count: 0,
23                regions: LazyArray16::new(&[]),
24            },
25        }
26    }
27}
28
29impl<'a> ItemVariationStore<'a> {
30    #[inline]
31    pub fn parse(mut s: Stream) -> Option<ItemVariationStore> {
32        let data = s.tail()?;
33
34        let mut regions_s = s.clone();
35        let format = s.read::<u16>()?;
36        if format != 1 {
37            return None;
38        }
39
40        let region_list_offset = s.read::<u32>()?;
41        let count = s.read::<u16>()?;
42        let offsets = s.read_array16::<u32>(count)?;
43
44        let regions = {
45            regions_s.advance(usize::num_from(region_list_offset));
46            // TODO: should be the same as in `fvar`
47            let axis_count = regions_s.read::<u16>()?;
48            let count = regions_s.read::<u16>()?;
49            let total = count.checked_mul(axis_count)?;
50            VariationRegionList {
51                axis_count,
52                regions: regions_s.read_array16::<RegionAxisCoordinatesRecord>(total)?,
53            }
54        };
55
56        Some(ItemVariationStore {
57            data,
58            data_offsets: offsets,
59            regions,
60        })
61    }
62
63    pub fn region_indices(&self, index: u16) -> Option<LazyArray16<u16>> {
64        // Offsets in bytes from the start of the item variation store
65        // to each item variation data subtable.
66        let offset = self.data_offsets.get(index)?;
67        let mut s = Stream::new_at(self.data, usize::num_from(offset))?;
68        s.skip::<u16>(); // item_count
69        s.skip::<u16>(); // short_delta_count
70        let count = s.read::<u16>()?;
71        s.read_array16::<u16>(count)
72    }
73
74    pub fn parse_delta(
75        &self,
76        outer_index: u16,
77        inner_index: u16,
78        coordinates: &[NormalizedCoordinate],
79    ) -> Option<f32> {
80        let offset = self.data_offsets.get(outer_index)?;
81        let mut s = Stream::new_at(self.data, usize::num_from(offset))?;
82        let item_count = s.read::<u16>()?;
83        let word_delta_count = s.read::<u16>()?;
84        let region_index_count = s.read::<u16>()?;
85        let region_indices = s.read_array16::<u16>(region_index_count)?;
86
87        if inner_index >= item_count {
88            return None;
89        }
90
91        let has_long_words = (word_delta_count & 0x8000) != 0;
92        let word_delta_count = word_delta_count & 0x7FFF;
93
94        // From the spec: The length of the data for each row, in bytes, is
95        // regionIndexCount + (wordDeltaCount & WORD_DELTA_COUNT_MASK)
96        // if the LONG_WORDS flag is not set, or 2 x that amount if the flag is set.
97        let mut delta_set_len = word_delta_count + region_index_count;
98        if has_long_words {
99            delta_set_len *= 2;
100        }
101
102        s.advance(usize::from(inner_index).checked_mul(usize::from(delta_set_len))?);
103
104        let mut delta = 0.0;
105        let mut i = 0;
106        while i < word_delta_count {
107            let idx = region_indices.get(i)?;
108            let num = if has_long_words {
109                // TODO: use f64?
110                s.read::<i32>()? as f32
111            } else {
112                f32::from(s.read::<i16>()?)
113            };
114            delta += num * self.regions.evaluate_region(idx, coordinates);
115            i += 1;
116        }
117
118        while i < region_index_count {
119            let idx = region_indices.get(i)?;
120            let num = if has_long_words {
121                f32::from(s.read::<i16>()?)
122            } else {
123                f32::from(s.read::<i8>()?)
124            };
125            delta += num * self.regions.evaluate_region(idx, coordinates);
126            i += 1;
127        }
128
129        Some(delta)
130    }
131}
132
133#[derive(Clone, Copy, Debug)]
134pub struct VariationRegionList<'a> {
135    axis_count: u16,
136    regions: LazyArray16<'a, RegionAxisCoordinatesRecord>,
137}
138
139impl<'a> VariationRegionList<'a> {
140    #[inline]
141    pub(crate) fn evaluate_region(&self, index: u16, coordinates: &[NormalizedCoordinate]) -> f32 {
142        let mut v = 1.0;
143        for (i, coord) in coordinates.iter().enumerate() {
144            let region = match self.regions.get(index * self.axis_count + i as u16) {
145                Some(r) => r,
146                None => return 0.0,
147            };
148
149            let factor = region.evaluate_axis(coord.get());
150            if factor == 0.0 {
151                return 0.0;
152            }
153
154            v *= factor;
155        }
156
157        v
158    }
159}
160
161#[derive(Clone, Copy, Debug)]
162struct RegionAxisCoordinatesRecord {
163    start_coord: i16,
164    peak_coord: i16,
165    end_coord: i16,
166}
167
168impl RegionAxisCoordinatesRecord {
169    #[inline]
170    pub fn evaluate_axis(&self, coord: i16) -> f32 {
171        let start = self.start_coord;
172        let peak = self.peak_coord;
173        let end = self.end_coord;
174
175        if start > peak || peak > end {
176            return 1.0;
177        }
178
179        if start < 0 && end > 0 && peak != 0 {
180            return 1.0;
181        }
182
183        if peak == 0 || coord == peak {
184            return 1.0;
185        }
186
187        if coord <= start || end <= coord {
188            return 0.0;
189        }
190
191        if coord < peak {
192            f32::from(coord - start) / f32::from(peak - start)
193        } else {
194            f32::from(end - coord) / f32::from(end - peak)
195        }
196    }
197}
198
199impl FromData for RegionAxisCoordinatesRecord {
200    const SIZE: usize = 6;
201
202    #[inline]
203    fn parse(data: &[u8]) -> Option<Self> {
204        let mut s = Stream::new(data);
205        Some(RegionAxisCoordinatesRecord {
206            start_coord: s.read::<i16>()?,
207            peak_coord: s.read::<i16>()?,
208            end_coord: s.read::<i16>()?,
209        })
210    }
211}