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 | 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)] |
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 | fn parse(data: &[u8]) -> Option<Self> { |
63 | let mut s: Stream<'_> = 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)] |
72 | struct ConditionSet<'a> { |
73 | data: &'a [u8], |
74 | conditions: LazyArray16<'a, Offset32>, |
75 | } |
76 | |
77 | impl<'a> ConditionSet<'a> { |
78 | fn parse(data: &'a [u8]) -> Option<Self> { |
79 | let mut s: Stream<'_> = Stream::new(data); |
80 | let count: u16 = s.read::<u16>()?; |
81 | let conditions: LazyArray16<'_, Offset32> = 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: Offset32| { |
87 | self.data |
88 | .get(offset.to_usize()..) |
89 | .and_then(Condition::parse) |
90 | .map_or(default:false, |c: Condition| c.evaluate(coords)) |
91 | }) |
92 | } |
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 | 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)] |
138 | struct FeatureTableSubstitution<'a> { |
139 | data: &'a [u8], |
140 | records: LazyArray16<'a, FeatureTableSubstitutionRecord>, |
141 | } |
142 | |
143 | impl<'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)] |
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 | fn parse(data: &[u8]) -> Option<Self> { |
180 | let mut s: Stream<'_> = Stream::new(data); |
181 | Some(Self { |
182 | feature_index: s.read::<FeatureIndex>()?, |
183 | feature: s.read::<Offset32>()?, |
184 | }) |
185 | } |
186 | } |
187 | |