sr-aef 0.1.0

Uncompile OpenType fonts back into Adobe feature file (fea) source code
Documentation
use crate::UncompileContext;
use fea_rs_ast::{
    AlternateSubstStatement, GlyphClass, GlyphContainer, LigatureSubstStatement, LookupBlock,
    MultipleSubstStatement, SingleSubstStatement, Statement, Subst,
};
use skrifa::{
    GlyphId16,
    raw::{
        ReadError,
        tables::gsub::{
            AlternateSubstFormat1, LigatureSubstFormat1, LookupList, MultipleSubstFormat1,
            ReverseChainSingleSubstFormat1, SingleSubst, SingleSubstFormat1, SingleSubstFormat2,
            SubstitutionLookup, SubstitutionSubtables,
        },
    },
};
impl<'a> UncompileContext<'a> {
    pub(crate) fn uncompile_gsub_lookups(&mut self) -> Result<(), ReadError> {
        let gsub_lookup_list: LookupList<SubstitutionLookup> = match self.gsub.as_ref() {
            Some(gsub) => gsub.lookup_list()?,
            None => return Ok(()),
        };
        for (i, lookup) in gsub_lookup_list.lookups().iter().flatten().enumerate() {
            let subtables = lookup.subtables()?;
            let mut lookupblock = match subtables {
                SubstitutionSubtables::Single(subtables) => {
                    let mut lookupblock =
                        self.create_next_lookup_block("gsub_single", i as u16, Subst);
                    for subtable in subtables.iter().flatten() {
                        match subtable {
                            SingleSubst::Format1(table_ref) => {
                                self.uncompile_gsub1_format1(&mut lookupblock, table_ref)?;
                            }
                            SingleSubst::Format2(table_ref) => {
                                self.uncompile_gsub1_format2(&mut lookupblock, table_ref)?;
                            }
                        }
                    }
                    lookupblock
                }
                SubstitutionSubtables::Multiple(subtables) => {
                    let mut lookupblock =
                        self.create_next_lookup_block("gsub_multiple", i as u16, Subst);
                    for subtable in subtables.iter().flatten() {
                        self.uncompile_gsub2(&mut lookupblock, subtable)?;
                    }
                    lookupblock
                }
                SubstitutionSubtables::Alternate(subtables) => {
                    let mut lookupblock =
                        self.create_next_lookup_block("gsub_alternate", i as u16, Subst);
                    for subtable in subtables.iter().flatten() {
                        self.uncompile_gsub3(&mut lookupblock, subtable)?;
                    }
                    lookupblock
                }
                SubstitutionSubtables::Ligature(subtables) => {
                    let mut lookupblock =
                        self.create_next_lookup_block("gsub_ligature", i as u16, Subst);
                    for subtable in subtables.iter().flatten() {
                        self.uncompile_gsub4(&mut lookupblock, subtable)?;
                    }
                    lookupblock
                }
                SubstitutionSubtables::Contextual(subtables) => {
                    let mut lookupblock =
                        self.create_next_lookup_block("gsub_contextual", i as u16, Subst);
                    for subtable in subtables.iter().flatten() {
                        lookupblock.statements.extend(
                            self.uncompile_sequence_context(subtable, Subst)?
                                .into_iter()
                                .map(Statement::ChainedContextSubst),
                        );
                    }
                    lookupblock
                }
                SubstitutionSubtables::ChainContextual(subtables) => {
                    let mut lookupblock =
                        self.create_next_lookup_block("gsub_chain_contextual", i as u16, Subst);
                    for subtable in subtables.iter().flatten() {
                        lookupblock.statements.extend(
                            self.uncompile_chain_sequence_context(subtable, Subst)?
                                .into_iter()
                                .map(Statement::ChainedContextSubst),
                        );
                    }
                    lookupblock
                }
                SubstitutionSubtables::Reverse(subtables) => {
                    let mut lookupblock =
                        self.create_next_lookup_block("gsub_reverse", i as u16, Subst);
                    for subtable in subtables.iter().flatten() {
                        self.uncompile_gsub7(&mut lookupblock, subtable)?;
                    }
                    lookupblock
                }

                SubstitutionSubtables::EmptyExtension => {
                    self.create_next_lookup_block("gsub_extension", i as u16, Subst)
                }
            };
            self.add_lookup_flags(
                &mut lookupblock,
                lookup.lookup_flag(),
                lookup.mark_filtering_set(),
            );
            self.lookups.insert(lookupblock.name.clone(), lookupblock);
        }
        Ok(())
    }

    fn uncompile_gsub1_format1(
        &self,
        lookupblock: &mut LookupBlock,
        gsub1: SingleSubstFormat1,
    ) -> Result<(), ReadError> {
        let delta = gsub1.delta_glyph_id();
        let inputs = self.resolve_coverage(&gsub1.coverage()?);
        let replacements = gsub1
            .coverage()?
            .iter()
            .map(|g| GlyphId16::new(g.to_u16().saturating_add_signed(delta)))
            .map(|g| GlyphContainer::GlyphName(self.get_name(g)))
            .collect::<Vec<GlyphContainer>>();
        let subst = SingleSubstStatement::new(inputs, replacements, vec![], vec![], 0..0, false);
        lookupblock.statements.push(Statement::SingleSubst(subst));

        Ok(())
    }
    fn uncompile_gsub1_format2(
        &self,
        lookupblock: &mut LookupBlock,
        gsub1: SingleSubstFormat2,
    ) -> Result<(), ReadError> {
        let inputs = self.resolve_coverage(&gsub1.coverage()?);
        let replacements = gsub1
            .substitute_glyph_ids()
            .iter()
            .map(|g| GlyphContainer::GlyphName(self.get_name(g.get())))
            .collect();
        let subst = SingleSubstStatement::new(inputs, replacements, vec![], vec![], 0..0, false);
        lookupblock.statements.push(Statement::SingleSubst(subst));
        Ok(())
    }
    fn uncompile_gsub2(
        &self,
        lookupblock: &mut LookupBlock,
        gsub2: MultipleSubstFormat1,
    ) -> Result<(), ReadError> {
        let inputs = self.resolve_coverage(&gsub2.coverage()?);
        for (input, sequence) in inputs.iter().zip(gsub2.sequences().iter().flatten()) {
            let replacements = sequence
                .substitute_glyph_ids()
                .iter()
                .map(|g| GlyphContainer::GlyphName(self.get_name(g.get())))
                .collect();
            let subst = MultipleSubstStatement::new(
                input.clone(),
                replacements,
                vec![],
                vec![],
                0..0,
                false,
            );
            lookupblock.statements.push(Statement::MultipleSubst(subst));
        }
        Ok(())
    }
    fn uncompile_gsub3(
        &self,
        _lookupblock: &mut LookupBlock,
        gsub3: AlternateSubstFormat1,
    ) -> Result<(), ReadError> {
        let inputs = self.resolve_coverage(&gsub3.coverage()?);
        for (input, alternate_set) in inputs.iter().zip(gsub3.alternate_sets().iter().flatten()) {
            let alternates = alternate_set
                .alternate_glyph_ids()
                .iter()
                .map(|g| GlyphContainer::GlyphName(self.get_name(g.get())))
                .collect();
            let subst = AlternateSubstStatement::new(
                input.clone(),
                GlyphContainer::GlyphClass(GlyphClass::new(alternates, 0..0)),
                vec![],
                vec![],
                0..0,
                false,
            );
            _lookupblock
                .statements
                .push(Statement::AlternateSubst(subst));
        }
        {}
        Ok(())
    }
    fn uncompile_gsub4(
        &self,
        lookupblock: &mut LookupBlock,
        gsub4: LigatureSubstFormat1,
    ) -> Result<(), ReadError> {
        let inputs = self.resolve_coverage(&gsub4.coverage()?);
        for (input, ligature_set) in inputs.iter().zip(gsub4.ligature_sets().iter().flatten()) {
            for ligature in ligature_set.ligatures().iter().flatten() {
                let mut components: Vec<GlyphContainer> = ligature
                    .component_glyph_ids()
                    .iter()
                    .map(|g| GlyphContainer::GlyphName(self.get_name(g.get())))
                    .collect();
                components.insert(0, input.clone());
                let subst = LigatureSubstStatement::new(
                    components,
                    GlyphContainer::GlyphName(self.get_name(ligature.ligature_glyph())),
                    vec![],
                    vec![],
                    0..0,
                    false,
                );
                lookupblock.statements.push(Statement::LigatureSubst(subst));
            }
        }
        Ok(())
    }

    fn uncompile_gsub7(
        &self,
        _lookupblock: &mut LookupBlock,
        _gsub7: ReverseChainSingleSubstFormat1,
    ) -> Result<(), ReadError> {
        todo!()
    }
}