1use crate::UncompileContext;
2use fea_rs_ast::{ChainedContextStatement, GlyphContainer, SubOrPos};
3use skrifa::raw::{
4 ReadError,
5 tables::{gsub::ChainedSequenceContext, layout::SequenceContext},
6};
7use std::collections::HashMap;
8
9impl<'a> UncompileContext<'a> {
10 pub(crate) fn class_members_to_container(
11 &self,
12 members: Vec<GlyphContainer>,
13 ) -> GlyphContainer {
14 if members.len() == 1 {
15 members.into_iter().next().unwrap()
16 } else {
17 GlyphContainer::GlyphClass(fea_rs_ast::GlyphClass::new(members, 0..0))
18 }
19 }
20
21 pub(crate) fn uncompile_sequence_context<T: SubOrPos>(
22 &mut self,
23 sequence: SequenceContext,
24 sub_or_pos: T,
25 ) -> Result<Vec<ChainedContextStatement<T>>, ReadError> {
26 let mut statements = vec![];
27 match sequence {
28 SequenceContext::Format1(seq_f1) => {
29 let first_glyphs = seq_f1
30 .coverage()?
31 .iter()
32 .map(|g| GlyphContainer::GlyphName(self.get_name(g)))
33 .collect::<Vec<GlyphContainer>>();
34 for (first_glyph, ruleset) in first_glyphs.iter().zip(seq_f1.seq_rule_sets().iter())
35 {
36 if let Some(ruleset) = ruleset.transpose()? {
37 for rule in ruleset.seq_rules().iter().flatten() {
38 let mut input: Vec<_> = rule
39 .input_sequence()
40 .iter()
41 .map(|g| GlyphContainer::GlyphName(self.get_name(g.get())))
42 .collect();
43 input.insert(0, first_glyph.clone());
44 let mut lookups = vec![vec![]; input.len()];
45 for lookup_record in rule.seq_lookup_records() {
46 if let Some(lu) =
47 lookups.get_mut(lookup_record.sequence_index() as usize)
48 {
49 lu.push(self.get_lookup_name(
50 lookup_record.lookup_list_index(),
51 sub_or_pos,
52 ));
53 }
54 }
55 let statement = ChainedContextStatement::new(
56 input,
57 vec![],
58 vec![],
59 lookups,
60 0..0,
61 sub_or_pos,
62 );
63 statements.push(statement);
64 }
65 }
66 }
67 }
68 SequenceContext::Format2(_table_ref) => {
69 let table_ref = _table_ref;
70 let class_def = table_ref.class_def()?;
71 let classes = self.resolve_classes(Some(&class_def));
72
73 let mut covered_input_classes: HashMap<u16, Vec<GlyphContainer>> = HashMap::new();
74 for gid in table_ref.coverage()?.iter() {
75 let class_id = class_def.get(gid);
76 covered_input_classes
77 .entry(class_id)
78 .or_default()
79 .push(GlyphContainer::GlyphName(self.get_name(gid)));
80 }
81
82 for (class_id, ruleset) in table_ref.class_seq_rule_sets().iter().enumerate() {
83 let Some(ruleset) = ruleset.transpose()? else {
84 continue;
85 };
86 let Some(first_class_members) = covered_input_classes.get(&(class_id as u16))
87 else {
88 continue;
89 };
90 let first = self.class_members_to_container(first_class_members.clone());
91
92 for rule in ruleset.class_seq_rules().iter().flatten() {
93 let mut input = vec![first.clone()];
94 input.extend(rule.input_sequence().iter().map(|class_id| {
95 self.class_members_to_container(
96 classes.get(&class_id.get()).cloned().unwrap_or_default(),
97 )
98 }));
99
100 let mut lookups = vec![vec![]; input.len()];
101 for lookup_record in rule.seq_lookup_records() {
102 if let Some(lu) =
103 lookups.get_mut(lookup_record.sequence_index() as usize)
104 {
105 lu.push(self.get_lookup_name(
106 lookup_record.lookup_list_index(),
107 sub_or_pos,
108 ));
109 }
110 }
111
112 let statement = ChainedContextStatement::new(
113 input,
114 vec![],
115 vec![],
116 lookups,
117 0..0,
118 sub_or_pos,
119 );
120 statements.push(statement);
121 }
122 }
123 }
124 SequenceContext::Format3(table_ref) => {
125 let input: Vec<GlyphContainer> = table_ref
126 .coverages()
127 .iter()
128 .flatten()
129 .map(|coverage| self.resolve_coverage_to_class(&coverage, true))
130 .collect();
131 let mut lookups = vec![vec![]; input.len()];
132 for lookup_record in table_ref.seq_lookup_records() {
133 if let Some(lu) = lookups.get_mut(lookup_record.sequence_index() as usize) {
134 lu.push(
135 self.get_lookup_name(lookup_record.lookup_list_index(), sub_or_pos),
136 );
137 }
138 }
139 let statement =
140 ChainedContextStatement::new(input, vec![], vec![], lookups, 0..0, sub_or_pos);
141 statements.push(statement);
142 }
143 }
144 Ok(statements)
145 }
146 pub fn uncompile_chain_sequence_context<T: SubOrPos>(
147 &mut self,
148 chainsequence: ChainedSequenceContext,
149 sub_or_pos: T,
150 ) -> Result<Vec<ChainedContextStatement<T>>, ReadError> {
151 let mut statements = vec![];
152 match chainsequence {
153 ChainedSequenceContext::Format1(seq_f1) => {
154 let first_glyphs = seq_f1
155 .coverage()?
156 .iter()
157 .map(|g| GlyphContainer::GlyphName(self.get_name(g)))
158 .collect::<Vec<GlyphContainer>>();
159 for (first_glyph, ruleset) in first_glyphs
160 .iter()
161 .zip(seq_f1.chained_seq_rule_sets().iter())
162 {
163 if let Some(ruleset) = ruleset.transpose()? {
164 for rule in ruleset.chained_seq_rules().iter().flatten() {
165 let mut input: Vec<_> = rule
166 .input_sequence()
167 .iter()
168 .map(|g| GlyphContainer::GlyphName(self.get_name(g.get())))
169 .collect();
170 input.insert(0, first_glyph.clone());
171 let prefix = rule
172 .backtrack_sequence()
173 .iter()
174 .rev()
175 .map(|g| GlyphContainer::GlyphName(self.get_name(g.get())))
176 .collect::<Vec<GlyphContainer>>();
177 let suffix = rule
178 .lookahead_sequence()
179 .iter()
180 .map(|g| GlyphContainer::GlyphName(self.get_name(g.get())))
181 .collect::<Vec<GlyphContainer>>();
182 let mut lookups = vec![vec![]; input.len()];
183 for lookup_record in rule.seq_lookup_records() {
184 if let Some(lu) =
185 lookups.get_mut(lookup_record.sequence_index() as usize)
186 {
187 lu.push(self.get_lookup_name(
188 lookup_record.lookup_list_index(),
189 sub_or_pos,
190 ));
191 }
192 }
193 let statement = ChainedContextStatement::new(
194 input,
195 prefix,
196 suffix,
197 lookups,
198 0..0,
199 sub_or_pos,
200 );
201 statements.push(statement);
202 }
203 }
204 }
205 }
206 ChainedSequenceContext::Format2(_table_ref) => {
207 let table_ref = _table_ref;
208 let input_class_def = table_ref.input_class_def().ok();
209 let backtrack_class_def = table_ref.backtrack_class_def().ok();
210 let lookahead_class_def = table_ref.lookahead_class_def().ok();
211
212 let input_classes = self.resolve_classes(input_class_def.as_ref());
213 let backtrack_classes = self.resolve_classes(backtrack_class_def.as_ref());
214 let lookahead_classes = self.resolve_classes(lookahead_class_def.as_ref());
215
216 let mut covered_input_classes: HashMap<u16, Vec<GlyphContainer>> = HashMap::new();
217 for gid in table_ref.coverage()?.iter() {
218 let class_id = input_class_def
219 .as_ref()
220 .map_or(0, |class_def| class_def.get(gid));
221 covered_input_classes
222 .entry(class_id)
223 .or_default()
224 .push(GlyphContainer::GlyphName(self.get_name(gid)));
225 }
226
227 for (class_id, ruleset) in
228 table_ref.chained_class_seq_rule_sets().iter().enumerate()
229 {
230 let Some(ruleset) = ruleset.transpose()? else {
231 continue;
232 };
233 let Some(first_class_members) = covered_input_classes.get(&(class_id as u16))
234 else {
235 continue;
236 };
237 let first = self.class_members_to_container(first_class_members.clone());
238
239 for rule in ruleset.chained_class_seq_rules().iter().flatten() {
240 let mut input = vec![first.clone()];
241 input.extend(rule.input_sequence().iter().map(|class_id| {
242 self.class_members_to_container(
243 input_classes
244 .get(&class_id.get())
245 .cloned()
246 .unwrap_or_default(),
247 )
248 }));
249
250 let mut prefix: Vec<GlyphContainer> = rule
251 .backtrack_sequence()
252 .iter()
253 .map(|class_id| {
254 self.class_members_to_container(
255 backtrack_classes
256 .get(&class_id.get())
257 .cloned()
258 .unwrap_or_default(),
259 )
260 })
261 .collect();
262 prefix.reverse();
263
264 let suffix: Vec<GlyphContainer> = rule
265 .lookahead_sequence()
266 .iter()
267 .map(|class_id| {
268 self.class_members_to_container(
269 lookahead_classes
270 .get(&class_id.get())
271 .cloned()
272 .unwrap_or_default(),
273 )
274 })
275 .collect();
276
277 let mut lookups = vec![vec![]; input.len()];
278 for lookup_record in rule.seq_lookup_records() {
279 if let Some(lu) =
280 lookups.get_mut(lookup_record.sequence_index() as usize)
281 {
282 lu.push(self.get_lookup_name(
283 lookup_record.lookup_list_index(),
284 sub_or_pos,
285 ));
286 }
287 }
288
289 let statement = ChainedContextStatement::new(
290 input,
291 prefix,
292 suffix,
293 lookups,
294 0..0,
295 sub_or_pos,
296 );
297 statements.push(statement);
298 }
299 }
300 }
301 ChainedSequenceContext::Format3(table_ref) => {
302 let input: Vec<GlyphContainer> = table_ref
303 .input_coverages()
304 .iter()
305 .flatten()
306 .map(|coverage| self.resolve_coverage_to_class(&coverage, true))
307 .collect();
308 let mut pre: Vec<GlyphContainer> = table_ref
309 .backtrack_coverages()
310 .iter()
311 .flatten()
312 .map(|coverage| self.resolve_coverage_to_class(&coverage, true))
313 .collect();
314 pre.reverse();
315 let post: Vec<GlyphContainer> = table_ref
316 .lookahead_coverages()
317 .iter()
318 .flatten()
319 .map(|coverage| self.resolve_coverage_to_class(&coverage, true))
320 .collect();
321
322 let mut lookups = vec![vec![]; input.len()];
323 for lookup_record in table_ref.seq_lookup_records() {
324 if let Some(lu) = lookups.get_mut(lookup_record.sequence_index() as usize) {
325 lu.push(
326 self.get_lookup_name(lookup_record.lookup_list_index(), sub_or_pos),
327 );
328 }
329 }
330 let statement =
331 ChainedContextStatement::new(input, pre, post, lookups, 0..0, sub_or_pos);
332 statements.push(statement);
333 }
334 }
335 Ok(statements)
336 }
337}