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    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}