Skip to main content

sr_aef/
contextual.rs

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}