//! An [Axis Variations Table]( //! https://docs.microsoft.com/en-us/typography/opentype/spec/avar) implementation. use core::convert::TryFrom; use crate::parser::{FromData, LazyArray16, Stream}; use crate::NormalizedCoordinate; /// An axis value map. #[derive(Clone, Copy, Debug)] pub struct AxisValueMap { /// A normalized coordinate value obtained using default normalization. pub from_coordinate: i16, /// The modified, normalized coordinate value. pub to_coordinate: i16, } impl FromData for AxisValueMap { const SIZE: usize = 4; #[inline] fn parse(data: &[u8]) -> Option { let mut s = Stream::new(data); Some(AxisValueMap { from_coordinate: s.read::()?, to_coordinate: s.read::()?, }) } } /// A list of segment maps. /// /// Can be empty. /// /// The internal data layout is not designed for random access, /// therefore we're not providing the `get()` method and only an iterator. #[derive(Clone, Copy)] pub struct SegmentMaps<'a> { count: u16, data: &'a [u8], } impl<'a> SegmentMaps<'a> { /// Returns the number of segments. pub fn len(&self) -> u16 { self.count } /// Checks if there are any segments. pub fn is_empty(&self) -> bool { self.count == 0 } } impl core::fmt::Debug for SegmentMaps<'_> { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { write!(f, "SegmentMaps {{ ... }}") } } impl<'a> IntoIterator for SegmentMaps<'a> { type Item = LazyArray16<'a, AxisValueMap>; type IntoIter = SegmentMapsIter<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { SegmentMapsIter { stream: Stream::new(self.data), } } } /// An iterator over maps. #[allow(missing_debug_implementations)] pub struct SegmentMapsIter<'a> { stream: Stream<'a>, } impl<'a> Iterator for SegmentMapsIter<'a> { type Item = LazyArray16<'a, AxisValueMap>; fn next(&mut self) -> Option { let count = self.stream.read::()?; self.stream.read_array16::(count) } } /// An [Axis Variations Table]( /// https://docs.microsoft.com/en-us/typography/opentype/spec/avar). #[derive(Clone, Copy, Debug)] pub struct Table<'a> { /// The segment maps array — one segment map for each axis /// in the order of axes specified in the `fvar` table. pub segment_maps: SegmentMaps<'a>, } impl<'a> Table<'a> { /// Parses a table from raw data. pub fn parse(data: &'a [u8]) -> Option { let mut s = Stream::new(data); let version = s.read::()?; if version != 0x00010000 { return None; } s.skip::(); // reserved Some(Self { segment_maps: SegmentMaps { // TODO: check that `axisCount` is the same as in `fvar`? count: s.read::()?, data: s.tail()?, }, }) } /// Maps coordinates. pub fn map_coordinates(&self, coordinates: &mut [NormalizedCoordinate]) -> Option<()> { if usize::from(self.segment_maps.count) != coordinates.len() { return None; } for (map, coord) in self.segment_maps.into_iter().zip(coordinates) { *coord = NormalizedCoordinate::from(map_value(&map, coord.0)?); } Some(()) } } fn map_value(map: &LazyArray16, value: i16) -> Option { // This code is based on harfbuzz implementation. if map.is_empty() { return Some(value); } else if map.len() == 1 { let record = map.get(0)?; return Some(value - record.from_coordinate + record.to_coordinate); } let record_0 = map.get(0)?; if value <= record_0.from_coordinate { return Some(value - record_0.from_coordinate + record_0.to_coordinate); } let mut i = 1; while i < map.len() && value > map.get(i)?.from_coordinate { i += 1; } if i == map.len() { i -= 1; } let record_curr = map.get(i)?; let curr_from = record_curr.from_coordinate; let curr_to = record_curr.to_coordinate; if value >= curr_from { return Some(value - curr_from + curr_to); } let record_prev = map.get(i - 1)?; let prev_from = record_prev.from_coordinate; let prev_to = record_prev.to_coordinate; if prev_from == curr_from { return Some(prev_to); } let curr_from = i32::from(curr_from); let curr_to = i32::from(curr_to); let prev_from = i32::from(prev_from); let prev_to = i32::from(prev_to); let denom = curr_from - prev_from; let k = (curr_to - prev_to) * (i32::from(value) - prev_from) + denom / 2; let value = prev_to + k / denom; i16::try_from(value).ok() }