allsorts_subset_browser/tables/variable_fonts/
gvar.rs

1#![deny(missing_docs)]
2
3//! `gvar` Glyph Variations Table
4//!
5//! <https://learn.microsoft.com/en-us/typography/opentype/spec/gvar>
6
7use crate::binary::read::{ReadBinary, ReadCtxt, ReadScope, ReadUnchecked};
8use crate::binary::{U16Be, U32Be};
9use crate::error::ParseError;
10use crate::tables::loca::LocaOffsets;
11use crate::tables::variable_fonts::{ReadTuple, TupleVariationStore};
12use crate::tables::F2Dot14;
13use crate::SafeFrom;
14use std::fmt;
15use std::fmt::Formatter;
16
17/// `gvar` Glyph Variations Table
18///
19/// <https://learn.microsoft.com/en-us/typography/opentype/spec/gvar#gvar-header>
20pub struct GvarTable<'a> {
21    /// Major version number of the glyph variations table.
22    pub major_version: u16,
23    /// Minor version number of the glyph variations table.
24    pub minor_version: u16,
25    /// The number of variation axes for this font.
26    ///
27    /// This must be the same number as axisCount in
28    /// the 'fvar' table.
29    pub axis_count: u16,
30    /// The number of shared tuple records.
31    ///
32    /// Shared tuple records can be referenced within glyph
33    /// variation data tables for multiple glyphs, as opposed to other tuple
34    /// records stored directly within a glyph variation data table.
35    shared_tuple_count: u16,
36    /// Scope containing data for the shared tuple records.
37    shared_tuples_scope: ReadScope<'a>,
38    /// The number of glyphs in this font.
39    ///
40    /// This must match the number of glyphs stored elsewhere in
41    /// the font.
42    pub glyph_count: u16,
43    /// Scope containing the data for the array of GlyphVariationData tables.
44    glyph_variation_data_array_scope: ReadScope<'a>,
45    /// Offsets from the start of the GlyphVariationData array to each
46    /// GlyphVariationData table.
47    glyph_variation_data_offsets: LocaOffsets<'a>,
48}
49
50/// A count of the number of points in a glyph including the four
51/// [phantom points](https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructing_glyphs#phantom-points).
52#[derive(Debug, Copy, Clone)]
53pub struct NumPoints(u32);
54
55impl NumPoints {
56    /// Create a new NumPoints instance with `num` glyph points (excluding
57    /// phantom points).
58    pub fn new(num: u16) -> NumPoints {
59        NumPoints(u32::from(num) + 4)
60    }
61
62    /// Construct a NumPoints instance from a value that has already had the
63    /// phantom points added.
64    pub(crate) fn from_raw(num: u32) -> NumPoints {
65        NumPoints(num)
66    }
67
68    /// Get the underlying number of points (including phantom points).
69    pub fn get(self) -> u32 {
70        self.0
71    }
72}
73
74impl fmt::Display for NumPoints {
75    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
76        self.0.fmt(f)
77    }
78}
79
80impl<'a> GvarTable<'a> {
81    /// Returns the variation data for the glyph at `glyph_index` that has
82    /// `num_points` points (including and phantom points).
83    ///
84    /// If the glyph has no variations, such as when the glyph is an empty
85    /// glyph, then `None` is returned.
86    pub fn glyph_variation_data(
87        &self,
88        glyph_index: u16,
89        num_points: NumPoints,
90    ) -> Result<Option<TupleVariationStore<'a, super::Gvar>>, ParseError> {
91        let glyph_index = usize::from(glyph_index);
92        let start = self
93            .glyph_variation_data_offsets
94            .get(glyph_index)
95            .map(usize::safe_from)
96            .ok_or(ParseError::BadIndex)?;
97        let end = self
98            .glyph_variation_data_offsets
99            .get(glyph_index + 1)
100            .map(usize::safe_from)
101            .ok_or(ParseError::BadIndex)?;
102        let length = end.checked_sub(start).ok_or(ParseError::BadOffset)?;
103        if length > 0 {
104            let scope = self
105                .glyph_variation_data_array_scope
106                .offset_length(start, length)?;
107            scope
108                .read_dep::<TupleVariationStore<'_, super::Gvar>>((
109                    self.axis_count,
110                    num_points.get(),
111                    scope,
112                ))
113                .map(Some)
114        } else {
115            Ok(None)
116        }
117    }
118
119    /// Returns the shared peak tuple at the supplied index.
120    pub fn shared_tuple(&self, index: u16) -> Result<ReadTuple<'a>, ParseError> {
121        if index >= self.shared_tuple_count {
122            return Err(ParseError::BadIndex);
123        }
124
125        let offset = usize::from(index) * usize::from(self.axis_count) * F2Dot14::SIZE;
126        let shared_tuple = self
127            .shared_tuples_scope
128            .offset(offset)
129            .ctxt()
130            .read_array::<F2Dot14>(usize::from(self.axis_count))
131            .map(ReadTuple)?;
132        Ok(shared_tuple)
133    }
134}
135
136impl ReadBinary for GvarTable<'_> {
137    type HostType<'a> = GvarTable<'a>;
138
139    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
140        let scope = ctxt.scope();
141        let major_version = ctxt.read_u16be()?;
142        ctxt.check_version(major_version == 1)?;
143        let minor_version = ctxt.read_u16be()?;
144        let axis_count = ctxt.read_u16be()?;
145        let shared_tuple_count = ctxt.read_u16be()?;
146        let shared_tuples_offset = ctxt.read_u32be()?;
147        let glyph_count = ctxt.read_u16be()?;
148        let flags = ctxt.read_u16be()?;
149        // Offset from the start of this table to the array of GlyphVariationData
150        // tables.
151        let glyph_variation_data_array_offset = ctxt.read_u32be()?;
152        // Offsets from the start of the GlyphVariationData array to each
153        // GlyphVariationData table. If bit 0 is clear, the offsets are uint16;
154        // if bit 0 is set, the offsets are uint32.
155        let glyph_variation_data_offsets = if flags & 1 == 1 {
156            // The actual local offset is stored. The value of n is numGlyphs + 1.
157            LocaOffsets::Long(ctxt.read_array::<U32Be>(usize::from(glyph_count) + 1)?)
158        } else {
159            // The actual local offset divided by 2 is stored. The value of n is numGlyphs +
160            // 1.
161            LocaOffsets::Short(ctxt.read_array::<U16Be>(usize::from(glyph_count) + 1)?)
162        };
163
164        // Store the shared tuples
165        let shared_tuples_len =
166            usize::from(shared_tuple_count) * usize::from(axis_count) * F2Dot14::SIZE;
167        let shared_tuples_scope =
168            scope.offset_length(usize::safe_from(shared_tuples_offset), shared_tuples_len)?;
169
170        // Read the glyph variation data
171        if glyph_variation_data_offsets.len() < 2 {
172            return Err(ParseError::BadIndex);
173        }
174        // NOTE(unwrap): Safe due to check above
175        let glyph_variation_data_array_scope = scope.offset_length(
176            usize::safe_from(glyph_variation_data_array_offset),
177            usize::safe_from(glyph_variation_data_offsets.last().unwrap()),
178        )?;
179
180        Ok(GvarTable {
181            major_version,
182            minor_version,
183            axis_count,
184            shared_tuple_count,
185            shared_tuples_scope,
186            glyph_count,
187            glyph_variation_data_array_scope,
188            glyph_variation_data_offsets,
189        })
190    }
191}
192
193#[cfg(test)]
194mod tests {
195    use super::*;
196    use crate::binary::read::ReadScope;
197    use crate::error::ReadWriteError;
198    use crate::font_data::FontData;
199    use crate::tables::glyf::GlyfTable;
200    use crate::tables::loca::LocaTable;
201    use crate::tables::{FontTableProvider, HeadTable, MaxpTable};
202    use crate::tag;
203    use crate::tests::read_fixture;
204
205    #[test]
206    fn gvar() {
207        let buffer = read_fixture("tests/fonts/opentype/NotoSans-VF.abc.ttf");
208        let scope = ReadScope::new(&buffer);
209        let font_file = scope
210            .read::<FontData<'_>>()
211            .expect("unable to parse font file");
212        let table_provider = font_file
213            .table_provider(0)
214            .expect("unable to create font provider");
215        let stat_data = table_provider
216            .read_table_data(tag::GVAR)
217            .expect("unable to read fvar table data");
218        let gvar = ReadScope::new(&stat_data).read::<GvarTable<'_>>().unwrap();
219        assert_eq!(gvar.major_version, 1);
220        assert_eq!(gvar.minor_version, 0);
221        assert_eq!(gvar.axis_count, 3);
222        assert_eq!(gvar.shared_tuple_count, 15);
223        assert_eq!(gvar.shared_tuples_scope.data().len(), 90);
224        assert_eq!(gvar.glyph_count, 4);
225        assert_eq!(gvar.glyph_variation_data_array_scope.data().len(), 3028);
226        assert_eq!(gvar.glyph_variation_data_offsets.len(), 5);
227    }
228
229    #[test]
230    fn glyph_variation_data() -> Result<(), ReadWriteError> {
231        let buffer = read_fixture("tests/fonts/opentype/NotoSans-VF.abc.ttf");
232        let scope = ReadScope::new(&buffer);
233        let font_file = scope.read::<FontData<'_>>()?;
234        let provider = font_file.table_provider(0)?;
235        let head = ReadScope::new(&provider.read_table_data(tag::HEAD)?).read::<HeadTable>()?;
236        let maxp = ReadScope::new(&provider.read_table_data(tag::MAXP)?).read::<MaxpTable>()?;
237        let loca_data = provider.read_table_data(tag::LOCA)?;
238        let loca = ReadScope::new(&loca_data)
239            .read_dep::<LocaTable<'_>>((usize::from(maxp.num_glyphs), head.index_to_loc_format))?;
240        let glyf_data = provider.read_table_data(tag::GLYF)?;
241        let glyf = ReadScope::new(&glyf_data).read_dep::<GlyfTable<'_>>(&loca)?;
242        let gvar_data = provider.read_table_data(tag::GVAR)?;
243        let gvar = ReadScope::new(&gvar_data).read::<GvarTable<'_>>().unwrap();
244
245        let glyph = 3; // 'c' glyph
246        let num_points = NumPoints::new(glyf.records()[3].number_of_points()?);
247        let store = gvar
248            .glyph_variation_data(glyph, num_points)?
249            .expect("variation store");
250        let variation_data = (0..store.tuple_variation_headers.len())
251            .into_iter()
252            .map(|i| store.variation_data(i as u16))
253            .collect::<Result<Vec<_>, _>>()?;
254
255        // This one uses shared point numbers, which specify all 34 points in the glyph
256        let deltas = variation_data[0].iter().collect::<Vec<_>>();
257        let expected = vec![
258            (0, (2, 0)),
259            (1, (-11, 0)),
260            (2, (-8, 13)),
261            (3, (4, 14)),
262            (4, (4, -4)),
263            (5, (4, -22)),
264            (6, (1, -21)),
265            (7, (4, -8)),
266            (8, (13, -8)),
267            (9, (8, -8)),
268            (10, (-9, -3)),
269            (11, (-5, -3)),
270            (12, (17, 45)),
271            (13, (11, 50)),
272            (14, (16, 44)),
273            (15, (15, 44)),
274            (16, (-3, 44)),
275            (17, (-37, 26)),
276            (18, (-60, 2)),
277            (19, (-60, -5)),
278            (20, (-60, -9)),
279            (21, (-49, -31)),
280            (22, (-22, -51)),
281            (23, (3, -51)),
282            (24, (-5, -51)),
283            (25, (-1, -55)),
284            (26, (0, -56)),
285            (27, (0, -3)),
286            (28, (2, 1)),
287            (29, (-3, 0)),
288            (30, (0, 0)),
289            (31, (-8, 0)),
290            (32, (0, 0)),
291            (33, (0, 0)),
292        ];
293        assert_eq!(deltas, expected);
294
295        // This one has private point numbers
296        let deltas = variation_data[4].iter().collect::<Vec<_>>();
297        let expected = vec![
298            (1, (-15, 0)),
299            (5, (-4, 0)),
300            (9, (-20, 0)),
301            (11, (-24, 0)),
302            (12, (-24, 0)),
303            (14, (-20, 0)),
304            (17, (-7, 0)),
305            (20, (-5, 0)),
306            (22, (-12, 0)),
307            (23, (-17, 0)),
308            (24, (-20, 0)),
309            (27, (-24, 0)),
310            (31, (-26, 0)),
311        ];
312        assert_eq!(deltas, expected);
313
314        Ok(())
315    }
316}