Coverage Report

Created: 2026-08-31 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ttf-parser/src/tables/fvar.rs
Line
Count
Source
1
//! A [Font Variations Table](
2
//! https://docs.microsoft.com/en-us/typography/opentype/spec/fvar) implementation.
3
4
use core::num::NonZeroU16;
5
6
use crate::parser::{Fixed, FromData, LazyArray16, Offset, Offset16, Stream, f32_bound};
7
use 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)]
13
pub 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
23
impl FromData for VariationAxis {
24
    const SIZE: usize = 20;
25
26
21.6k
    fn parse(data: &[u8]) -> Option<Self> {
27
21.6k
        let mut s = Stream::new(data);
28
21.6k
        let tag = s.read::<Tag>()?;
29
21.6k
        let min_value = s.read::<Fixed>()?;
30
21.6k
        let def_value = s.read::<Fixed>()?;
31
21.6k
        let max_value = s.read::<Fixed>()?;
32
21.6k
        let flags = s.read::<u16>()?;
33
21.6k
        let name_id = s.read::<u16>()?;
34
35
21.6k
        Some(VariationAxis {
36
21.6k
            tag,
37
21.6k
            min_value: def_value.0.min(min_value.0),
38
21.6k
            def_value: def_value.0,
39
21.6k
            max_value: def_value.0.max(max_value.0),
40
21.6k
            name_id,
41
21.6k
            hidden: flags & 1 == 1,
42
21.6k
        })
43
21.6k
    }
44
}
45
46
impl VariationAxis {
47
    /// Returns a normalized variation coordinate for this axis.
48
5.30k
    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
5.30k
        v = f32_bound(self.min_value, v, self.max_value);
53
5.30k
        if v == self.def_value {
54
1.65k
            v = 0.0;
55
3.64k
        } else if v < self.def_value {
56
776
            v = (v - self.def_value) / (self.def_value - self.min_value);
57
2.86k
        } else {
58
2.86k
            v = (v - self.def_value) / (self.max_value - self.def_value);
59
2.86k
        }
60
61
5.30k
        NormalizedCoordinate::from(v)
62
5.30k
    }
63
}
64
65
/// A [Font Variations Table](
66
/// https://docs.microsoft.com/en-us/typography/opentype/spec/fvar).
67
#[derive(Clone, Copy, Debug)]
68
pub struct Table<'a> {
69
    /// A list of variation axes.
70
    pub axes: LazyArray16<'a, VariationAxis>,
71
}
72
73
impl<'a> Table<'a> {
74
    /// Parses a table from raw data.
75
8.91k
    pub fn parse(data: &'a [u8]) -> Option<Self> {
76
8.91k
        let mut s = Stream::new(data);
77
8.91k
        let version = s.read::<u32>()?;
78
8.90k
        if version != 0x00010000 {
79
328
            return None;
80
8.57k
        }
81
82
8.57k
        let axes_array_offset = s.read::<Offset16>()?;
83
8.56k
        s.skip::<u16>(); // reserved
84
8.56k
        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
8.55k
        let axis_count = NonZeroU16::new(axis_count)?;
90
91
8.52k
        let mut s = Stream::new_at(data, axes_array_offset.to_usize())?;
92
8.46k
        let axes = s.read_array16::<VariationAxis>(axis_count.get())?;
93
94
8.27k
        Some(Table { axes })
95
8.91k
    }
96
}