ttf_parser/tables/
fvar.rs

1//! A [Font Variations Table](
2//! https://docs.microsoft.com/en-us/typography/opentype/spec/fvar) implementation.
3
4use core::num::NonZeroU16;
5
6use crate::parser::{f32_bound, Fixed, FromData, LazyArray16, Offset, Offset16, Stream};
7use crate::{NormalizedCoordinate, Tag};
8
9/// A [variation axis](https://docs.microsoft.com/en-us/typography/opentype/spec/fvar#variationaxisrecord).
10#[repr(C)]
11#[allow(missing_docs)]
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 FromData for VariationAxis {
24    const SIZE: usize = 20;
25
26    fn parse(data: &[u8]) -> Option<Self> {
27        let mut s = Stream::new(data);
28        let tag = s.read::<Tag>()?;
29        let min_value = s.read::<Fixed>()?;
30        let def_value = s.read::<Fixed>()?;
31        let max_value = s.read::<Fixed>()?;
32        let flags = s.read::<u16>()?;
33        let name_id = s.read::<u16>()?;
34
35        Some(VariationAxis {
36            tag,
37            min_value: def_value.0.min(min_value.0),
38            def_value: def_value.0,
39            max_value: def_value.0.max(max_value.0),
40            name_id,
41            hidden: (flags >> 3) & 1 == 1,
42        })
43    }
44}
45
46impl VariationAxis {
47    /// Returns a normalized variation coordinate for this axis.
48    pub(crate) fn normalized_value(&self, mut v: f32) -> NormalizedCoordinate {
49        // Based on
50        // https://docs.microsoft.com/en-us/typography/opentype/spec/avar#overview
51
52        v = f32_bound(self.min_value, v, self.max_value);
53        if v == self.def_value {
54            v = 0.0;
55        } else if v < self.def_value {
56            v = (v - self.def_value) / (self.def_value - self.min_value);
57        } else {
58            v = (v - self.def_value) / (self.max_value - self.def_value);
59        }
60
61        NormalizedCoordinate::from(v)
62    }
63}
64
65/// A [Font Variations Table](
66/// https://docs.microsoft.com/en-us/typography/opentype/spec/fvar).
67#[derive(Clone, Copy, Debug)]
68pub struct Table<'a> {
69    /// A list of variation axes.
70    pub axes: LazyArray16<'a, VariationAxis>,
71}
72
73impl<'a> Table<'a> {
74    /// Parses a table from raw data.
75    pub fn parse(data: &'a [u8]) -> Option<Self> {
76        let mut s = Stream::new(data);
77        let version = s.read::<u32>()?;
78        if version != 0x00010000 {
79            return None;
80        }
81
82        let axes_array_offset = s.read::<Offset16>()?;
83        s.skip::<u16>(); // reserved
84        let axis_count = s.read::<u16>()?;
85
86        // 'If axisCount is zero, then the font is not functional as a variable font,
87        // and must be treated as a non-variable font;
88        // any variation-specific tables or data is ignored.'
89        let axis_count = NonZeroU16::new(axis_count)?;
90
91        let mut s = Stream::new_at(data, axes_array_offset.to_usize())?;
92        let axes = s.read_array16::<VariationAxis>(axis_count.get())?;
93
94        Some(Table { axes })
95    }
96}