ttf_parser/ggg/
feature_variations.rs1use crate::{NormalizedCoordinate, Tag};
2use crate::parser::{FromData, LazyArray16, LazyArray32};
3use crate::parser::{Offset, Offset32, Stream};
4use super::{Feature, FeatureIndex, VariationIndex, RecordListItem};
5
6#[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>(); 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 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 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>(); 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 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}