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