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