ttf_parser/ggg/
feature_variations.rs

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