Coverage Report

Created: 2026-10-13 06:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ttf-parser/src/ggg/feature_variations.rs
Line
Count
Source
1
use super::{Feature, FeatureIndex, RecordListItem, VariationIndex};
2
use crate::parser::{FromData, LazyArray16, LazyArray32};
3
use crate::parser::{Offset, Offset32, Stream};
4
use crate::{NormalizedCoordinate, Tag};
5
6
/// A [Feature Variations Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#featurevariations-table).
7
#[derive(Clone, Copy, Debug)]
8
pub struct FeatureVariations<'a> {
9
    data: &'a [u8],
10
    records: LazyArray32<'a, FeatureVariationRecord>,
11
}
12
13
impl<'a> FeatureVariations<'a> {
14
265
    pub(crate) fn parse(data: &'a [u8]) -> Option<Self> {
15
265
        let mut s = Stream::new(data);
16
265
        let major_version = s.read::<u16>()?;
17
257
        s.skip::<u16>(); // minor version
18
257
        if major_version != 1 {
19
147
            return None;
20
110
        }
21
22
110
        let count = s.read::<u32>()?;
23
94
        let records = s.read_array32(count)?;
24
24
        Some(Self { data, records })
25
265
    }
26
27
    /// Returns a [`VariationIndex`] for variation coordinates.
28
0
    pub fn find_index(&self, coords: &[NormalizedCoordinate]) -> Option<VariationIndex> {
29
0
        for i in 0..self.records.len() {
30
0
            let record = self.records.get(i)?;
31
0
            let offset = record.conditions.to_usize();
32
0
            let set = ConditionSet::parse(self.data.get(offset..)?)?;
33
0
            if set.evaluate(coords) {
34
0
                return Some(i);
35
0
            }
36
        }
37
0
        None
38
0
    }
39
40
    /// Returns a [`Feature`] at specified indices.
41
0
    pub fn find_substitute(
42
0
        &self,
43
0
        feature_index: FeatureIndex,
44
0
        variation_index: VariationIndex,
45
0
    ) -> Option<Feature<'a>> {
46
0
        let offset = self.records.get(variation_index)?.substitutions.to_usize();
47
0
        let subst = FeatureTableSubstitution::parse(self.data.get(offset..)?)?;
48
0
        subst.find_substitute(feature_index)
49
0
    }
50
}
51
52
#[derive(Clone, Copy, Debug)]
53
struct FeatureVariationRecord {
54
    conditions: Offset32,
55
    substitutions: Offset32,
56
}
57
58
impl FromData for FeatureVariationRecord {
59
    const SIZE: usize = 8;
60
61
    #[inline]
62
0
    fn parse(data: &[u8]) -> Option<Self> {
63
0
        let mut s = Stream::new(data);
64
        Some(Self {
65
0
            conditions: s.read::<Offset32>()?,
66
0
            substitutions: s.read::<Offset32>()?,
67
        })
68
0
    }
69
}
70
71
#[derive(Clone, Copy, Debug)]
72
struct ConditionSet<'a> {
73
    data: &'a [u8],
74
    conditions: LazyArray16<'a, Offset32>,
75
}
76
77
impl<'a> ConditionSet<'a> {
78
0
    fn parse(data: &'a [u8]) -> Option<Self> {
79
0
        let mut s = Stream::new(data);
80
0
        let count = s.read::<u16>()?;
81
0
        let conditions = s.read_array16(count)?;
82
0
        Some(Self { data, conditions })
83
0
    }
84
85
0
    fn evaluate(&self, coords: &[NormalizedCoordinate]) -> bool {
86
0
        self.conditions.into_iter().all(|offset| {
87
0
            self.data
88
0
                .get(offset.to_usize()..)
89
0
                .and_then(Condition::parse)
90
0
                .map_or(false, |c| c.evaluate(coords))
91
0
        })
92
0
    }
93
}
94
95
#[derive(Clone, Copy, Debug)]
96
enum Condition {
97
    Format1 {
98
        axis_index: u16,
99
        filter_range_min: i16,
100
        filter_range_max: i16,
101
    },
102
}
103
104
impl Condition {
105
0
    fn parse(data: &[u8]) -> Option<Self> {
106
0
        let mut s = Stream::new(data);
107
0
        let format = s.read::<u16>()?;
108
0
        match format {
109
            1 => {
110
0
                let axis_index = s.read::<u16>()?;
111
0
                let filter_range_min = s.read::<i16>()?;
112
0
                let filter_range_max = s.read::<i16>()?;
113
0
                Some(Self::Format1 {
114
0
                    axis_index,
115
0
                    filter_range_min,
116
0
                    filter_range_max,
117
0
                })
118
            }
119
0
            _ => None,
120
        }
121
0
    }
122
123
0
    fn evaluate(&self, coords: &[NormalizedCoordinate]) -> bool {
124
        let Self::Format1 {
125
0
            axis_index,
126
0
            filter_range_min,
127
0
            filter_range_max,
128
0
        } = *self;
129
0
        let coord = coords
130
0
            .get(usize::from(axis_index))
131
0
            .map(|c| c.get())
132
0
            .unwrap_or(0);
133
0
        filter_range_min <= coord && coord <= filter_range_max
134
0
    }
135
}
136
137
#[derive(Clone, Copy, Debug)]
138
struct FeatureTableSubstitution<'a> {
139
    data: &'a [u8],
140
    records: LazyArray16<'a, FeatureTableSubstitutionRecord>,
141
}
142
143
impl<'a> FeatureTableSubstitution<'a> {
144
0
    fn parse(data: &'a [u8]) -> Option<Self> {
145
0
        let mut s = Stream::new(data);
146
0
        let major_version = s.read::<u16>()?;
147
0
        s.skip::<u16>(); // minor version
148
0
        if major_version != 1 {
149
0
            return None;
150
0
        }
151
152
0
        let count = s.read::<u16>()?;
153
0
        let records = s.read_array16(count)?;
154
0
        Some(Self { data, records })
155
0
    }
156
157
0
    fn find_substitute(&self, feature_index: FeatureIndex) -> Option<Feature<'a>> {
158
0
        for record in self.records {
159
0
            if record.feature_index == feature_index {
160
0
                let offset = record.feature.to_usize();
161
                // TODO: set tag
162
0
                return Feature::parse(Tag::from_bytes(b"DFLT"), self.data.get(offset..)?);
163
0
            }
164
        }
165
0
        None
166
0
    }
167
}
168
169
#[derive(Clone, Copy, Debug)]
170
struct FeatureTableSubstitutionRecord {
171
    feature_index: FeatureIndex,
172
    feature: Offset32,
173
}
174
175
impl FromData for FeatureTableSubstitutionRecord {
176
    const SIZE: usize = 6;
177
178
    #[inline]
179
0
    fn parse(data: &[u8]) -> Option<Self> {
180
0
        let mut s = Stream::new(data);
181
        Some(Self {
182
0
            feature_index: s.read::<FeatureIndex>()?,
183
0
            feature: s.read::<Offset32>()?,
184
        })
185
0
    }
186
}