ttf_parser/tables/
fvar.rs

1// https://docs.microsoft.com/en-us/typography/opentype/spec/fvar
2
3use core::num::NonZeroU16;
4
5use crate::{Tag, NormalizedCoordinate};
6use crate::parser::{Stream, FromData, Fixed, Offset16, Offset, LazyArray16, LazyArrayIter16, f32_bound};
7
8
9/// A [variation axis](https://docs.microsoft.com/en-us/typography/opentype/spec/fvar#variationaxisrecord).
10#[allow(missing_docs)]
11#[repr(C)]
12#[derive(Clone, Copy, PartialEq, Debug)]
13pub struct VariationAxis {
14    pub tag: Tag,
15    pub min_value: f32,
16    pub def_value: f32,
17    pub max_value: f32,
18    /// An axis name in the `name` table.
19    pub name_id: u16,
20    pub hidden: bool,
21}
22
23impl VariationAxis {
24    /// Returns a normalized variation coordinate for this axis.
25    pub(crate) fn normalized_value(&self, mut v: f32) -> NormalizedCoordinate {
26        // Based on
27        // https://docs.microsoft.com/en-us/typography/opentype/spec/avar#overview
28
29        v = f32_bound(self.min_value, v, self.max_value);
30        if v == self.def_value {
31            v = 0.0;
32        } else if v < self.def_value {
33            v = (v - self.def_value) / (self.def_value - self.min_value);
34        } else {
35            v = (v - self.def_value) / (self.max_value - self.def_value);
36        }
37
38        NormalizedCoordinate::from(v)
39    }
40}
41
42
43#[derive(Clone, Copy)]
44pub(crate) struct Table<'a> {
45    axes: LazyArray16<'a, VariationAxisRecord>,
46}
47
48impl<'a> Table<'a> {
49    pub fn parse(data: &'a [u8]) -> Option<Self> {
50        let mut s = Stream::new(data);
51        let version: u32 = s.read()?;
52        if version != 0x00010000 {
53            return None;
54        }
55
56        let axes_array_offset: Offset16 = s.read()?;
57        s.skip::<u16>(); // reserved
58        let axis_count: u16 = s.read()?;
59
60        // 'If axisCount is zero, then the font is not functional as a variable font,
61        // and must be treated as a non-variable font;
62        // any variation-specific tables or data is ignored.'
63        let axis_count = NonZeroU16::new(axis_count)?;
64
65        let mut s = Stream::new_at(data, axes_array_offset.to_usize())?;
66        let axes = s.read_array16::<VariationAxisRecord>(axis_count.get())?;
67
68        Some(Table { axes })
69    }
70
71    pub fn axes(&self) -> VariationAxes<'a> {
72        VariationAxes { iter: self.axes.into_iter() }
73    }
74
75    // TODO: add axis_by_tag
76}
77
78
79/// An iterator over variation axes.
80#[allow(missing_debug_implementations)]
81#[derive(Clone, Copy, Default)]
82pub struct VariationAxes<'a> {
83    iter: LazyArrayIter16<'a, VariationAxisRecord>,
84}
85
86impl<'a> Iterator for VariationAxes<'a> {
87    type Item = VariationAxis;
88
89    #[inline]
90    fn next(&mut self) -> Option<Self::Item> {
91        let record = self.iter.next()?;
92
93        let def_value = record.def_value;
94        let min_value = def_value.min(record.min_value);
95        let max_value = def_value.max(record.max_value);
96
97        Some(VariationAxis {
98            tag: record.axis_tag,
99            min_value,
100            def_value,
101            max_value,
102            name_id: record.axis_name_id,
103            hidden: (record.flags >> 3) & 1 == 1,
104        })
105    }
106
107    #[inline]
108    fn count(self) -> usize {
109        self.iter.count()
110    }
111}
112
113
114#[derive(Clone, Copy)]
115struct VariationAxisRecord {
116    axis_tag: Tag,
117    min_value: f32,
118    def_value: f32,
119    max_value: f32,
120    flags: u16,
121    axis_name_id: u16,
122}
123
124impl FromData for VariationAxisRecord {
125    const SIZE: usize = 20;
126
127    #[inline]
128    fn parse(data: &[u8]) -> Option<Self> {
129        let mut s = Stream::new(data);
130        Some(VariationAxisRecord {
131            axis_tag: s.read::<Tag>()?,
132            min_value: s.read::<Fixed>()?.0,
133            def_value: s.read::<Fixed>()?.0,
134            max_value: s.read::<Fixed>()?.0,
135            flags: s.read::<u16>()?,
136            axis_name_id: s.read::<u16>()?,
137        })
138    }
139}