allsorts_subset_browser/tables/variable_fonts/
avar.rs1#![deny(missing_docs)]
2
3use crate::binary::read::{ReadArray, ReadBinary, ReadCtxt, ReadFrom, ReadScope, ReadUnchecked};
14use crate::error::ParseError;
15use crate::tables::{F2Dot14, Fixed};
16
17pub struct AvarTable<'a> {
19 pub major_version: u16,
21 pub minor_version: u16,
23 pub axis_count: u16,
25 segments_map_scope: ReadScope<'a>,
26}
27
28pub struct SegmentMap<'a> {
33 axis_value_maps: ReadArray<'a, AxisValueMap>,
35}
36
37#[derive(Debug, Copy, Clone, Eq, PartialEq)]
39pub struct AxisValueMap {
40 pub from_coordinate: F2Dot14,
42 pub to_coordinate: F2Dot14,
44}
45
46impl AvarTable<'_> {
47 pub fn segment_maps(&self) -> impl Iterator<Item = SegmentMap<'_>> {
51 (0..self.axis_count).scan(self.segments_map_scope.ctxt(), |ctxt, _i| {
52 ctxt.read::<SegmentMap<'_>>().ok()
53 })
54 }
55}
56
57impl ReadBinary for AvarTable<'_> {
58 type HostType<'a> = AvarTable<'a>;
59
60 fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
61 let major_version = ctxt.read_u16be()?;
62 ctxt.check_version(major_version == 1)?;
63 let minor_version = ctxt.read_u16be()?;
64 let _reserved = ctxt.read_u16be()?;
65 let axis_count = ctxt.read_u16be()?;
66
67 let segment_map_scope = ctxt.scope();
68 let mut segment_maps_len = 0;
69
70 for _ in 0..axis_count {
71 let segment_map = ctxt.read::<SegmentMap<'_>>()?;
72 segment_maps_len += segment_map.axis_value_maps.len() * AxisValueMap::SIZE + 2
74 }
75
76 let segments_map_scope = segment_map_scope.offset_length(0, segment_maps_len)?;
77
78 Ok(AvarTable {
79 major_version,
80 minor_version,
81 axis_count,
82 segments_map_scope,
83 })
84 }
85}
86
87impl SegmentMap<'_> {
88 pub fn axis_value_mappings(&self) -> impl Iterator<Item = AxisValueMap> + '_ {
90 self.axis_value_maps.iter()
91 }
92
93 pub fn normalize(&self, mut normalized_value: Fixed) -> Fixed {
98 let mut start_seg: Option<AxisValueMap> = None;
101 for end_seg in self.axis_value_mappings() {
102 if let Some(start_seg) = start_seg {
105 let end_seg_from_coordinate = Fixed::from(end_seg.from_coordinate);
106 if end_seg_from_coordinate == normalized_value {
107 normalized_value = end_seg.to_coordinate.into();
108 break;
109 } else if end_seg_from_coordinate > normalized_value {
110 let ratio = (normalized_value - Fixed::from(start_seg.from_coordinate))
113 / (Fixed::from(end_seg.from_coordinate)
114 - Fixed::from(start_seg.from_coordinate));
115 normalized_value = Fixed::from(start_seg.to_coordinate)
116 + ratio
117 * (Fixed::from(end_seg.to_coordinate)
118 - Fixed::from(start_seg.to_coordinate));
119 break;
120 }
121 }
122 start_seg = Some(end_seg);
123 }
124 normalized_value
125 }
126}
127
128impl ReadBinary for SegmentMap<'_> {
129 type HostType<'a> = SegmentMap<'a>;
130
131 fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
132 let position_map_count = ctxt.read_u16be()?;
133 let axis_value_maps = ctxt.read_array::<AxisValueMap>(usize::from(position_map_count))?;
134
135 Ok(SegmentMap { axis_value_maps })
136 }
137}
138
139impl ReadFrom for AxisValueMap {
140 type ReadType = (F2Dot14, F2Dot14);
141
142 fn read_from((from_coordinate, to_coordinate): (F2Dot14, F2Dot14)) -> Self {
143 AxisValueMap {
144 from_coordinate,
145 to_coordinate,
146 }
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use super::{AvarTable, AxisValueMap, F2Dot14, ReadScope};
153 use crate::binary::write::{WriteBinary, WriteBuffer};
154 use crate::binary::U16Be;
155 use crate::error::ReadWriteError;
156 use crate::font_data::FontData;
157 use crate::tables::variable_fonts::avar::SegmentMap;
158 use crate::tables::{Fixed, FontTableProvider};
159 use crate::tag;
160 use crate::tests::{assert_fixed_close, read_fixture};
161
162 #[test]
163 fn avar() {
164 let buffer = read_fixture("tests/fonts/opentype/NotoSans-VF.abc.ttf");
165 let scope = ReadScope::new(&buffer);
166 let font_file = scope
167 .read::<FontData<'_>>()
168 .expect("unable to parse font file");
169 let table_provider = font_file
170 .table_provider(0)
171 .expect("unable to create font provider");
172 let avar_data = table_provider
173 .read_table_data(tag::AVAR)
174 .expect("unable to read avar table data");
175 let avar = ReadScope::new(&avar_data).read::<AvarTable<'_>>().unwrap();
176
177 let segment_maps = avar
178 .segment_maps()
179 .map(|segment_map| segment_map.axis_value_mappings().collect::<Vec<_>>())
180 .collect::<Vec<_>>();
181 let expected = vec![
182 vec![
183 AxisValueMap {
184 from_coordinate: F2Dot14::from(-1.0),
185 to_coordinate: F2Dot14::from(-1.0),
186 },
187 AxisValueMap {
188 from_coordinate: F2Dot14::from(-0.6667),
189 to_coordinate: F2Dot14::from(-0.7969),
190 },
191 AxisValueMap {
192 from_coordinate: F2Dot14::from(-0.3333),
193 to_coordinate: F2Dot14::from(-0.5),
194 },
195 AxisValueMap {
196 from_coordinate: F2Dot14::from(0.0),
197 to_coordinate: F2Dot14::from(0.0),
198 },
199 AxisValueMap {
200 from_coordinate: F2Dot14::from(0.2),
201 to_coordinate: F2Dot14::from(0.18),
202 },
203 AxisValueMap {
204 from_coordinate: F2Dot14::from(0.4),
205 to_coordinate: F2Dot14::from(0.38),
206 },
207 AxisValueMap {
208 from_coordinate: F2Dot14::from(0.6),
209 to_coordinate: F2Dot14::from(0.61),
210 },
211 AxisValueMap {
212 from_coordinate: F2Dot14::from(0.8),
213 to_coordinate: F2Dot14::from(0.79),
214 },
215 AxisValueMap {
216 from_coordinate: F2Dot14::from(1.0),
217 to_coordinate: F2Dot14::from(1.0),
218 },
219 ],
220 vec![
221 AxisValueMap {
222 from_coordinate: F2Dot14::from(-1.0),
223 to_coordinate: F2Dot14::from(-1.0),
224 },
225 AxisValueMap {
226 from_coordinate: F2Dot14::from(-0.6667),
227 to_coordinate: F2Dot14::from(-0.7),
228 },
229 AxisValueMap {
230 from_coordinate: F2Dot14::from(-0.3333),
231 to_coordinate: F2Dot14::from(-0.36664),
232 },
233 AxisValueMap {
234 from_coordinate: F2Dot14::from(0.0),
235 to_coordinate: F2Dot14::from(0.0),
236 },
237 AxisValueMap {
238 from_coordinate: F2Dot14::from(1.0),
239 to_coordinate: F2Dot14::from(1.0),
240 },
241 ],
242 vec![
243 AxisValueMap {
244 from_coordinate: F2Dot14::from(-1.0),
245 to_coordinate: F2Dot14::from(-1.0),
246 },
247 AxisValueMap {
248 from_coordinate: F2Dot14::from(0.0),
249 to_coordinate: F2Dot14::from(0.0),
250 },
251 AxisValueMap {
252 from_coordinate: F2Dot14::from(1.0),
253 to_coordinate: F2Dot14::from(1.0),
254 },
255 ],
256 ];
257 assert_eq!(segment_maps, expected);
258 }
259
260 #[test]
261 fn test_avar_normalize() -> Result<(), ReadWriteError> {
262 let mut buf = WriteBuffer::new();
265 U16Be::write(&mut buf, 6u16)?; [
267 (-1.0, -1.0),
268 (-0.75, -0.5),
269 (0., 0.),
270 (0.4, 0.4),
271 (0.6, 0.9),
272 (1.0, 1.0),
273 ]
274 .iter()
275 .copied()
276 .try_for_each(|(from_coord, to_coord)| {
277 F2Dot14::write(&mut buf, F2Dot14::from(from_coord))?;
278 F2Dot14::write(&mut buf, F2Dot14::from(to_coord))
279 })?;
280
281 let data = buf.into_inner();
282 let mut ctxt = ReadScope::new(&data).ctxt();
283 let segment_map = ctxt.read::<SegmentMap<'_>>()?;
284 [
285 (-1.0, -1.0),
286 (-0.75, -0.5),
287 (-0.5, -0.3333),
288 (-0.25, -0.1667),
289 (0., 0.),
290 (0.25, 0.25),
291 (0.5, 0.65),
292 (0.75, 0.9375),
293 (1.0, 1.0),
294 ]
295 .iter()
296 .copied()
297 .for_each(|(input, expected)| {
298 assert_fixed_close(segment_map.normalize(Fixed::from(input)), expected);
299 });
300
301 Ok(())
302 }
303}