read_fonts/tables/
cvar.rs

1//! The [cvar (CVT Variations)](https://learn.microsoft.com/en-us/typography/opentype/spec/cvar)
2//! table.
3
4include!("../../generated/generated_cvar.rs");
5
6use super::variations::{
7    PackedPointNumbers, TupleDelta, TupleVariationCount, TupleVariationData, TupleVariationHeader,
8};
9
10/// Variation data specialized for the CVT variation table.
11pub type CvtVariationData<'a> = TupleVariationData<'a, CvtDelta>;
12
13impl<'a> Cvar<'a> {
14    /// Returns the variation data containing the tuples and deltas for the
15    /// control value table.
16    ///
17    /// This table doesn't contain an axis count field so this must be provided
18    /// by the user and can be read from the `fvar` table.
19    pub fn variation_data(&self, axis_count: u16) -> Result<CvtVariationData<'a>, ReadError> {
20        let count = self.tuple_variation_count();
21        let data = self.data()?;
22        let header_data = self.raw_tuple_header_data();
23        // if there are shared point numbers, get them now
24        let (shared_point_numbers, serialized_data) = if count.shared_point_numbers() {
25            let (packed, data) = PackedPointNumbers::split_off_front(data);
26            (Some(packed), data)
27        } else {
28            (None, data)
29        };
30        Ok(CvtVariationData {
31            tuple_count: count,
32            axis_count,
33            shared_tuples: None,
34            shared_point_numbers,
35            header_data,
36            serialized_data,
37            _marker: std::marker::PhantomData,
38        })
39    }
40
41    /// Computes the accumulated deltas for the given set of normalized
42    /// coordinates and stores them in `deltas`.
43    ///
44    /// The `axis_count` parameter expects the value from the `fvar`
45    /// table.
46    ///
47    /// The `deltas` slice should have a length greater than or equal
48    /// to the number of values in the `cvt` table. The values are
49    /// computed in 16.16 format.
50    pub fn deltas(
51        &self,
52        axis_count: u16,
53        coords: &[F2Dot14],
54        deltas: &mut [i32],
55    ) -> Result<(), ReadError> {
56        let var_data = self.variation_data(axis_count)?;
57        for (tuple, scalar) in var_data.active_tuples_at(coords) {
58            for delta in tuple.deltas() {
59                let ix = delta.position as usize;
60                if let Some(value) = deltas.get_mut(ix) {
61                    *value += delta.apply_scalar(scalar).to_bits();
62                }
63            }
64        }
65        Ok(())
66    }
67
68    fn raw_tuple_header_data(&self) -> FontData<'a> {
69        let range = self.shape.tuple_variation_headers_byte_range();
70        self.data.split_off(range.start).unwrap()
71    }
72}
73
74/// Delta for an entry in the control value table.
75#[derive(Clone, Copy, Debug, PartialEq, Eq)]
76pub struct CvtDelta {
77    /// The index in the CVT.
78    pub position: u16,
79    /// The delta to apply to the value in the CVT.
80    pub value: i32,
81}
82
83impl CvtDelta {
84    /// Applies a tuple scalar to this delta.
85    pub fn apply_scalar(self, scalar: Fixed) -> Fixed {
86        Fixed::from_i32(self.value) * scalar
87    }
88}
89
90impl TupleDelta for CvtDelta {
91    fn is_point() -> bool {
92        false
93    }
94
95    fn new(position: u16, x: i32, _y: i32) -> Self {
96        Self { position, value: x }
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use font_types::F2Dot14;
103
104    use crate::{FontRef, TableProvider};
105
106    #[test]
107    fn scaled_deltas() {
108        let font = FontRef::new(font_test_data::CVAR).unwrap();
109        // Elements are ([coords], [deltas]) where deltas are fixed point
110        // values.
111        // These were generated by fancy printf debugging in FreeType.
112        let cases = &[
113            (
114                [0.5, 0.5],
115                [
116                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
117                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
118                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 720896, 3276800, 1179648, 0, 0, 0, 0, 0,
119                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 622592, 0, 1179648, 0, 0, 0, 0, 0, 622592,
120                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
121                ],
122            ),
123            (
124                [-0.5, 0.5],
125                [
126                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
127                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
128                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1441792, -2162688, -1277952, 0, 0, 0,
129                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -917504, 0, -1277952, 0, 0, 0, 0, 0,
130                    -720896, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
131                    0, 0, 0,
132                ],
133            ),
134            (
135                [0.5, -0.5],
136                [
137                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
138                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
139                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1900544, 2621440, 2129920, 0, 0, 0, 0,
140                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 360448, 0, 1015808, 0, 0, 0, 0, 0,
141                    524288, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
142                    0, 0,
143                ],
144            ),
145            (
146                [-0.5, -0.5],
147                [
148                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
149                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
150                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1212416, -2293760, -1130496, 0, 0, 0,
151                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1097728, 0, -1277952, 0, 0, 0, 0, 0,
152                    -737280, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
153                    0, 0, 0,
154                ],
155            ),
156            (
157                [-1.0, -1.0],
158                [
159                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
160                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
161                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2490368, -4325376, -2490368, 0, 0, 0,
162                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1835008, 0, -2555904, 0, 0, 0, 0, 0,
163                    -1441792, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
164                    0, 0, 0,
165                ],
166            ),
167            (
168                [1.0, 1.0],
169                [
170                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
171                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
172                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1441792, 6553600, 2359296, 0, 0, 0, 0,
173                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1245184, 0, 2359296, 0, 0, 0, 0, 0,
174                    1245184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
175                    0, 0, 0,
176                ],
177            ),
178            (
179                [-1.0, 1.0],
180                [
181                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
182                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
183                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2883584, -4325376, -2555904, 0, 0, 0,
184                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1835008, 0, -2555904, 0, 0, 0, 0, 0,
185                    -1441792, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
186                    0, 0, 0,
187                ],
188            ),
189            (
190                [1.0, -1.0],
191                [
192                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
193                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
194                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5636096, 4456448, 5636096, 0, 0, 0, 0,
195                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 917504, 0, 1703936, 0, 0, 0, 0, 0,
196                    917504, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
197                    0, 0,
198                ],
199            ),
200        ];
201        let cvar = font.cvar().unwrap();
202        let axis_count = font.fvar().unwrap().axis_count();
203        let cvar_data = cvar.variation_data(axis_count).unwrap();
204        for (coords, expected_deltas) in cases {
205            let coords = coords.map(F2Dot14::from_f32);
206            let mut deltas = vec![0; expected_deltas.len()];
207            for (tuple, weight) in cvar_data.active_tuples_at(&coords) {
208                for delta in tuple.deltas() {
209                    let scaled_delta = delta.apply_scalar(weight);
210                    deltas[delta.position as usize] += scaled_delta.to_bits();
211                }
212            }
213            assert_eq!(&deltas, expected_deltas);
214        }
215    }
216
217    #[test]
218    fn raw_tuple_deltas() {
219        let font = FontRef::new(font_test_data::CVAR).unwrap();
220        let cvar = font.cvar().unwrap();
221        let axis_count = font.fvar().unwrap().axis_count();
222        let cvar_data = cvar.variation_data(axis_count).unwrap();
223        // An array of slices of (point number, delta) pairs, one for each
224        // tuple.
225        // These were taken directly from the ttx
226        let expected = [
227            &[(65, 8), (66, -8), (67, 8), (85, -11), (87, 0), (93, -1)],
228            &[(65, -2), (66, 8), (67, -7), (85, 11), (87, 0), (93, 1)],
229            &[(65, 56), (66, -24), (67, 42), (85, 6), (87, -10), (93, -4)],
230            &[
231                (65, -44),
232                (66, -66),
233                (67, -39),
234                (85, -28),
235                (87, -39),
236                (93, -22),
237            ],
238            &[(65, 22), (66, 100), (67, 36), (85, 19), (87, 36), (93, 19)],
239            &[(65, 8), (66, 0), (67, 8), (85, -43), (87, -49), (93, -32)],
240            &[(65, -8), (66, 0), (67, -8), (85, 11), (87, 9), (93, 1)],
241            &[(65, -80), (66, 0), (67, -90), (85, -6), (87, -47), (93, 4)],
242            &[(65, -16), (66, 0), (67, -21), (85, 28), (87, 39), (93, 22)],
243            &[
244                (65, -46),
245                (66, 0),
246                (67, -22),
247                (85, -19),
248                (87, 35),
249                (93, -19),
250            ],
251            &[(65, 2), (66, 0), (67, 7), (85, -11), (87, -9), (93, -1)],
252        ];
253        let mut count = 0;
254        for (tuple, expected) in cvar_data.tuples().zip(&expected) {
255            count += 1;
256            let deltas = tuple
257                .deltas()
258                .map(|delta| (delta.position, delta.value))
259                .collect::<Vec<_>>();
260            assert_eq!(&deltas, expected);
261        }
262        assert_eq!(count, expected.len());
263    }
264}