ttf_parser/ggg/
feature_variations.rs

1use crate::{NormalizedCoordinate, Tag};
2use crate::parser::{FromData, LazyArray16, LazyArray32};
3use crate::parser::{Offset, Offset32, Stream};
4use super::{Feature, FeatureIndex, VariationIndex, RecordListItem};
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.get(offset.to_usize()..)
88                .and_then(Condition::parse)
89                .map_or(false, |c| c.evaluate(coords))
90        })
91    }
92}
93
94#[derive(Clone, Copy, Debug)]
95enum Condition {
96    Format1 {
97        axis_index: u16,
98        filter_range_min: i16,
99        filter_range_max: i16,
100    }
101}
102
103impl Condition {
104    fn parse(data: &[u8]) -> Option<Self> {
105        let mut s = Stream::new(data);
106        let format = s.read::<u16>()?;
107        match format {
108            1 => {
109                let axis_index = s.read::<u16>()?;
110                let filter_range_min = s.read::<i16>()?;
111                let filter_range_max = s.read::<i16>()?;
112                Some(Self::Format1 { axis_index, filter_range_min, filter_range_max })
113            }
114            _ => None,
115        }
116    }
117
118    fn evaluate(&self, coords: &[NormalizedCoordinate]) -> bool {
119        let Self::Format1 { axis_index, filter_range_min, filter_range_max } = *self;
120        let coord = coords.get(usize::from(axis_index)).map(|c| c.get()).unwrap_or(0);
121        filter_range_min <= coord && coord <= filter_range_max
122    }
123}
124
125#[derive(Clone, Copy, Debug)]
126struct FeatureTableSubstitution<'a> {
127    data: &'a [u8],
128    records: LazyArray16<'a, FeatureTableSubstitutionRecord>,
129}
130
131impl<'a> FeatureTableSubstitution<'a> {
132    fn parse(data: &'a [u8]) -> Option<Self> {
133        let mut s = Stream::new(data);
134        let major_version = s.read::<u16>()?;
135        s.skip::<u16>(); // minor version
136        if major_version != 1 {
137            return None;
138        }
139
140        let count = s.read::<u16>()?;
141        let records = s.read_array16(count)?;
142        Some(Self { data, records })
143    }
144
145    fn find_substitute(&self, feature_index: FeatureIndex) -> Option<Feature<'a>> {
146        for record in self.records {
147            if record.feature_index == feature_index {
148                let offset = record.feature.to_usize();
149                // TODO: set tag
150                return Feature::parse(Tag::from_bytes(b"DFLT"), self.data.get(offset..)?);
151            }
152        }
153        None
154    }
155}
156
157#[derive(Clone, Copy, Debug)]
158struct FeatureTableSubstitutionRecord {
159    feature_index: FeatureIndex,
160    feature: Offset32,
161}
162
163impl FromData for FeatureTableSubstitutionRecord {
164    const SIZE: usize = 6;
165
166    #[inline]
167    fn parse(data: &[u8]) -> Option<Self> {
168        let mut s = Stream::new(data);
169        Some(Self {
170            feature_index: s.read::<FeatureIndex>()?,
171            feature: s.read::<Offset32>()?,
172        })
173    }
174}