ttf_parser/tables/
avar.rs

1//! An [Axis Variations Table](
2//! https://docs.microsoft.com/en-us/typography/opentype/spec/avar) implementation.
3
4use core::convert::TryFrom;
5
6use crate::parser::{FromData, LazyArray16, Stream};
7use crate::NormalizedCoordinate;
8
9/// An axis value map.
10#[derive(Clone, Copy, Debug)]
11pub struct AxisValueMap {
12    /// A normalized coordinate value obtained using default normalization.
13    pub from_coordinate: i16,
14    /// The modified, normalized coordinate value.
15    pub to_coordinate: i16,
16}
17
18impl FromData for AxisValueMap {
19    const SIZE: usize = 4;
20
21    #[inline]
22    fn parse(data: &[u8]) -> Option<Self> {
23        let mut s = Stream::new(data);
24        Some(AxisValueMap {
25            from_coordinate: s.read::<i16>()?,
26            to_coordinate: s.read::<i16>()?,
27        })
28    }
29}
30
31/// A list of segment maps.
32///
33/// Can be empty.
34///
35/// The internal data layout is not designed for random access,
36/// therefore we're not providing the `get()` method and only an iterator.
37#[derive(Clone, Copy)]
38pub struct SegmentMaps<'a> {
39    count: u16,
40    data: &'a [u8],
41}
42
43impl<'a> SegmentMaps<'a> {
44    /// Returns the number of segments.
45    pub fn len(&self) -> u16 {
46        self.count
47    }
48
49    /// Checks if there are any segments.
50    pub fn is_empty(&self) -> bool {
51        self.count == 0
52    }
53}
54
55impl core::fmt::Debug for SegmentMaps<'_> {
56    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
57        write!(f, "SegmentMaps {{ ... }}")
58    }
59}
60
61impl<'a> IntoIterator for SegmentMaps<'a> {
62    type Item = LazyArray16<'a, AxisValueMap>;
63    type IntoIter = SegmentMapsIter<'a>;
64
65    #[inline]
66    fn into_iter(self) -> Self::IntoIter {
67        SegmentMapsIter {
68            stream: Stream::new(self.data),
69        }
70    }
71}
72
73/// An iterator over maps.
74#[allow(missing_debug_implementations)]
75pub struct SegmentMapsIter<'a> {
76    stream: Stream<'a>,
77}
78
79impl<'a> Iterator for SegmentMapsIter<'a> {
80    type Item = LazyArray16<'a, AxisValueMap>;
81
82    fn next(&mut self) -> Option<Self::Item> {
83        let count = self.stream.read::<u16>()?;
84        self.stream.read_array16::<AxisValueMap>(count)
85    }
86}
87
88/// An [Axis Variations Table](
89/// https://docs.microsoft.com/en-us/typography/opentype/spec/avar).
90#[derive(Clone, Copy, Debug)]
91pub struct Table<'a> {
92    /// The segment maps array — one segment map for each axis
93    /// in the order of axes specified in the `fvar` table.
94    pub segment_maps: SegmentMaps<'a>,
95}
96
97impl<'a> Table<'a> {
98    /// Parses a table from raw data.
99    pub fn parse(data: &'a [u8]) -> Option<Self> {
100        let mut s = Stream::new(data);
101
102        let version = s.read::<u32>()?;
103        if version != 0x00010000 {
104            return None;
105        }
106
107        s.skip::<u16>(); // reserved
108        Some(Self {
109            segment_maps: SegmentMaps {
110                // TODO: check that `axisCount` is the same as in `fvar`?
111                count: s.read::<u16>()?,
112                data: s.tail()?,
113            },
114        })
115    }
116
117    /// Maps a single coordinate
118    pub fn map_coordinate(
119        &self,
120        coordinates: &mut [NormalizedCoordinate],
121        coordinate_index: usize,
122    ) -> Option<()> {
123        if usize::from(self.segment_maps.count) != coordinates.len() {
124            return None;
125        }
126
127        if let Some((map, coord)) = self
128            .segment_maps
129            .into_iter()
130            .zip(coordinates)
131            .nth(coordinate_index)
132        {
133            *coord = NormalizedCoordinate::from(map_value(&map, coord.0)?);
134        }
135
136        Some(())
137    }
138}
139
140fn map_value(map: &LazyArray16<AxisValueMap>, value: i16) -> Option<i16> {
141    // This code is based on harfbuzz implementation.
142
143    if map.is_empty() {
144        return Some(value);
145    } else if map.len() == 1 {
146        let record = map.get(0)?;
147        return Some(value - record.from_coordinate + record.to_coordinate);
148    }
149
150    let record_0 = map.get(0)?;
151    if value <= record_0.from_coordinate {
152        return Some(value - record_0.from_coordinate + record_0.to_coordinate);
153    }
154
155    let mut i = 1;
156    while i < map.len() && value > map.get(i)?.from_coordinate {
157        i += 1;
158    }
159
160    if i == map.len() {
161        i -= 1;
162    }
163
164    let record_curr = map.get(i)?;
165    let curr_from = record_curr.from_coordinate;
166    let curr_to = record_curr.to_coordinate;
167    if value >= curr_from {
168        return Some(value - curr_from + curr_to);
169    }
170
171    let record_prev = map.get(i - 1)?;
172    let prev_from = record_prev.from_coordinate;
173    let prev_to = record_prev.to_coordinate;
174    if prev_from == curr_from {
175        return Some(prev_to);
176    }
177
178    let curr_from = i32::from(curr_from);
179    let curr_to = i32::from(curr_to);
180    let prev_from = i32::from(prev_from);
181    let prev_to = i32::from(prev_to);
182
183    let denom = curr_from - prev_from;
184    let k = (curr_to - prev_to) * (i32::from(value) - prev_from) + denom / 2;
185    let value = prev_to + k / denom;
186    i16::try_from(value).ok()
187}