//! The [cvar (CVT Variations)](https://learn.microsoft.com/en-us/typography/opentype/spec/cvar) //! table. include!("../../generated/generated_cvar.rs"); use super::variations::{ PackedPointNumbers, TupleDelta, TupleVariationCount, TupleVariationData, TupleVariationHeader, }; /// Variation data specialized for the CVT variation table. pub type CvtVariationData<'a> = TupleVariationData<'a, CvtDelta>; impl<'a> Cvar<'a> { /// Returns the variation data containing the tuples and deltas for the /// control value table. /// /// This table doesn't contain an axis count field so this must be provided /// by the user and can be read from the `fvar` table. pub fn variation_data(&self, axis_count: u16) -> Result, ReadError> { let count = self.tuple_variation_count(); let data = self.data()?; let header_data = self.raw_tuple_header_data(); // if there are shared point numbers, get them now let (shared_point_numbers, serialized_data) = if count.shared_point_numbers() { let (packed, data) = PackedPointNumbers::split_off_front(data); (Some(packed), data) } else { (None, data) }; Ok(CvtVariationData { tuple_count: count, axis_count, shared_tuples: None, shared_point_numbers, header_data, serialized_data, _marker: std::marker::PhantomData, }) } /// Computes the accumulated deltas for the given set of normalized /// coordinates and stores them in `deltas`. /// /// The `axis_count` parameter expects the value from the `fvar` /// table. /// /// The `deltas` slice should have a length greater than or equal /// to the number of values in the `cvt` table. The values are /// computed in 16.16 format. pub fn deltas( &self, axis_count: u16, coords: &[F2Dot14], deltas: &mut [i32], ) -> Result<(), ReadError> { let var_data = self.variation_data(axis_count)?; for (tuple, scalar) in var_data.active_tuples_at(coords) { for delta in tuple.deltas() { let ix = delta.position as usize; if let Some(value) = deltas.get_mut(ix) { *value += delta.apply_scalar(scalar).to_bits(); } } } Ok(()) } fn raw_tuple_header_data(&self) -> FontData<'a> { let range = self.tuple_variation_headers_byte_range(); self.data.split_off(range.start).unwrap() } } /// Delta for an entry in the control value table. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct CvtDelta { /// The index in the CVT. pub position: u16, /// The delta to apply to the value in the CVT. pub value: i32, } impl CvtDelta { /// Applies a tuple scalar to this delta. pub fn apply_scalar(self, scalar: Fixed) -> Fixed { Fixed::from_i32(self.value) * scalar } } impl TupleDelta for CvtDelta { fn is_point() -> bool { false } fn new(position: u16, x: i32, _y: i32) -> Self { Self { position, value: x } } } #[cfg(test)] mod tests { use font_types::F2Dot14; use crate::{FontRef, TableProvider}; #[test] fn scaled_deltas() { let font = FontRef::new(font_test_data::CVAR).unwrap(); // Elements are ([coords], [deltas]) where deltas are fixed point // values. // These were generated by fancy printf debugging in FreeType. let cases = &[ ( [0.5, 0.5], [ 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 720896, 3276800, 1179648, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 622592, 0, 1179648, 0, 0, 0, 0, 0, 622592, 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, ], ), ( [-0.5, 0.5], [ 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1441792, -2162688, -1277952, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -917504, 0, -1277952, 0, 0, 0, 0, 0, -720896, 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, ], ), ( [0.5, -0.5], [ 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1900544, 2621440, 2129920, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 360448, 0, 1015808, 0, 0, 0, 0, 0, 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, 0, 0, ], ), ( [-0.5, -0.5], [ 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1212416, -2293760, -1130496, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1097728, 0, -1277952, 0, 0, 0, 0, 0, -737280, 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, ], ), ( [-1.0, -1.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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2490368, -4325376, -2490368, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1835008, 0, -2555904, 0, 0, 0, 0, 0, -1441792, 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, ], ), ( [1.0, 1.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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1441792, 6553600, 2359296, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1245184, 0, 2359296, 0, 0, 0, 0, 0, 1245184, 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, ], ), ( [-1.0, 1.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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2883584, -4325376, -2555904, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1835008, 0, -2555904, 0, 0, 0, 0, 0, -1441792, 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, ], ), ( [1.0, -1.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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5636096, 4456448, 5636096, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 917504, 0, 1703936, 0, 0, 0, 0, 0, 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, 0, 0, ], ), ]; let cvar = font.cvar().unwrap(); let axis_count = font.fvar().unwrap().axis_count(); let cvar_data = cvar.variation_data(axis_count).unwrap(); for (coords, expected_deltas) in cases { let coords = coords.map(F2Dot14::from_f32); let mut deltas = vec![0; expected_deltas.len()]; for tuple in cvar_data.tuples() { let Some(scalar) = tuple.compute_scalar(&coords) else { continue; }; for delta in tuple.deltas() { let scaled_delta = delta.apply_scalar(scalar); deltas[delta.position as usize] += scaled_delta.to_bits(); } } assert_eq!(&deltas, expected_deltas); } } #[test] fn raw_tuple_deltas() { let font = FontRef::new(font_test_data::CVAR).unwrap(); let cvar = font.cvar().unwrap(); let axis_count = font.fvar().unwrap().axis_count(); let cvar_data = cvar.variation_data(axis_count).unwrap(); // An array of slices of (point number, delta) pairs, one for each // tuple. // These were taken directly from the ttx let expected = [ &[(65, 8), (66, -8), (67, 8), (85, -11), (87, 0), (93, -1)], &[(65, -2), (66, 8), (67, -7), (85, 11), (87, 0), (93, 1)], &[(65, 56), (66, -24), (67, 42), (85, 6), (87, -10), (93, -4)], &[ (65, -44), (66, -66), (67, -39), (85, -28), (87, -39), (93, -22), ], &[(65, 22), (66, 100), (67, 36), (85, 19), (87, 36), (93, 19)], &[(65, 8), (66, 0), (67, 8), (85, -43), (87, -49), (93, -32)], &[(65, -8), (66, 0), (67, -8), (85, 11), (87, 9), (93, 1)], &[(65, -80), (66, 0), (67, -90), (85, -6), (87, -47), (93, 4)], &[(65, -16), (66, 0), (67, -21), (85, 28), (87, 39), (93, 22)], &[ (65, -46), (66, 0), (67, -22), (85, -19), (87, 35), (93, -19), ], &[(65, 2), (66, 0), (67, 7), (85, -11), (87, -9), (93, -1)], ]; let mut count = 0; for (tuple, expected) in cvar_data.tuples().zip(&expected) { count += 1; let deltas = tuple .deltas() .map(|delta| (delta.position, delta.value)) .collect::>(); assert_eq!(&deltas, expected); } assert_eq!(count, expected.len()); } }