fontcull-klippa 0.1.2

Subsetting a font file according to provided input. (Vendored fork for fontcull)
Documentation
//! impl subset() for VVAR

use crate::{
    hvar::{serialize_index_maps, HvarVvarSubsetPlan, ListupIndexMaps},
    offset::SerializeSubset,
    serialize::Serializer,
    Plan, Subset, SubsetError,
};
use fontcull_write_fonts::{
    read::{
        tables::{variations::DeltaSetIndexMap, vvar::Vvar},
        FontRef, ReadError, TopLevelTable,
    },
    types::Offset32,
    FontBuilder,
};

// reference: subset() for HVAR in harfbuzz
// <https://github.com/harfbuzz/harfbuzz/blob/bcd5aa368d3fd3ef741ea29df15d3d56011811c0/src/hb-ot-var-hvar-table.hh#L330>
impl Subset for Vvar<'_> {
    fn subset(
        &self,
        plan: &Plan,
        _font: &FontRef,
        s: &mut Serializer,
        _builder: &mut FontBuilder,
    ) -> Result<(), SubsetError> {
        let var_store = self
            .item_variation_store()
            .map_err(|_| SubsetError::SubsetTableError(Vvar::TAG))?;

        let index_maps = self
            .listup_index_maps()
            .map_err(|_| SubsetError::SubsetTableError(Vvar::TAG))?;

        let vvar_subset_plan = HvarVvarSubsetPlan::new(plan, &var_store, &index_maps)
            .map_err(|_| SubsetError::SubsetTableError(Vvar::TAG))?;

        s.embed(self.version())
            .map_err(|_| SubsetError::SubsetTableError(Vvar::TAG))?;

        let var_store_offset_pos = s
            .embed(0_u32)
            .map_err(|_| SubsetError::SubsetTableError(Vvar::TAG))?;

        Offset32::serialize_subset(
            &var_store,
            s,
            plan,
            vvar_subset_plan.inner_maps(),
            var_store_offset_pos,
        )
        .map_err(|_| SubsetError::SubsetTableError(Vvar::TAG))?;

        serialize_index_maps(
            s,
            plan,
            &index_maps,
            vvar_subset_plan.index_map_subset_plans(),
        )
        .map_err(|_| SubsetError::SubsetTableError(Vvar::TAG))
    }
}

impl ListupIndexMaps for Vvar<'_> {
    fn listup_index_maps(&self) -> Result<Vec<Option<DeltaSetIndexMap<'_>>>, ReadError> {
        let out = vec![
            self.advance_height_mapping().transpose()?,
            self.tsb_mapping().transpose()?,
            self.bsb_mapping().transpose()?,
            self.v_org_mapping().transpose()?,
        ];
        Ok(out)
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::SubsetFlags;
    use fontcull_write_fonts::{
        read::{FontData, FontRead},
        types::GlyphId,
    };
    #[test]
    fn test_subset_vvar_noop() {
        let raw_bytes: [u8; 102] = [
            0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x01, 0x00, 0x04,
            0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x00, 0x04, 0x02, 0x01, 0x00, 0x03, 0x00, 0x01,
            0x00, 0x00, 0x00, 0x2e, 0x00, 0x02, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x10,
            0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x01, 0x0b, 0x02,
            0x0c, 0x03, 0x0d, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x20, 0x11,
            0x00, 0x0e, 0x00, 0x01, 0x00, 0x02, 0xc0, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x40, 0x00, 0x40, 0x00,
        ];

        let vvar = Vvar::read(FontData::new(&raw_bytes)).unwrap();
        let mut builder = FontBuilder::new();

        //dummy font
        let font = FontRef::new(&raw_bytes).unwrap();

        let mut plan = Plan::default();
        plan.new_to_old_gid_list
            .push((GlyphId::NOTDEF, GlyphId::NOTDEF));
        plan.new_to_old_gid_list
            .push((GlyphId::from(1_u32), GlyphId::from(1_u32)));
        plan.new_to_old_gid_list
            .push((GlyphId::from(2_u32), GlyphId::from(2_u32)));
        plan.new_to_old_gid_list
            .push((GlyphId::from(3_u32), GlyphId::from(3_u32)));

        plan.glyphset
            .insert_range(GlyphId::NOTDEF..=GlyphId::from(3_u32));

        let mut s = Serializer::new(1024);
        assert_eq!(s.start_serialize(), Ok(()));
        let ret = vvar.subset(&plan, &font, &mut s, &mut builder);
        assert!(ret.is_ok());
        assert!(!s.in_error());
        s.end_serialize();

        let subsetted_data = s.copy_bytes();
        assert_eq!(subsetted_data, raw_bytes);
    }

    #[test]
    fn test_subset_vvar() {
        let raw_bytes: [u8; 102] = [
            0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x01, 0x00, 0x04,
            0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x00, 0x04, 0x02, 0x01, 0x00, 0x03, 0x00, 0x01,
            0x00, 0x00, 0x00, 0x2e, 0x00, 0x02, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x10,
            0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x01, 0x0b, 0x02,
            0x0c, 0x03, 0x0d, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x20, 0x11,
            0x00, 0x0e, 0x00, 0x01, 0x00, 0x02, 0xc0, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x40, 0x00, 0x40, 0x00,
        ];

        let vvar = Vvar::read(FontData::new(&raw_bytes)).unwrap();
        let mut builder = FontBuilder::new();

        //dummy font
        let font = FontRef::new(&raw_bytes).unwrap();

        let mut plan = Plan::default();
        plan.new_to_old_gid_list
            .push((GlyphId::NOTDEF, GlyphId::NOTDEF));
        plan.new_to_old_gid_list
            .push((GlyphId::from(1_u32), GlyphId::from(1_u32)));
        plan.new_to_old_gid_list
            .push((GlyphId::from(2_u32), GlyphId::from(3_u32)));

        plan.glyphset.insert(GlyphId::NOTDEF);
        plan.glyphset.insert(GlyphId::from(1_u32));
        plan.glyphset.insert(GlyphId::from(3_u32));

        let mut s = Serializer::new(1024);
        assert_eq!(s.start_serialize(), Ok(()));
        let ret = vvar.subset(&plan, &font, &mut s, &mut builder);
        assert!(ret.is_ok());
        assert!(!s.in_error());
        s.end_serialize();

        let subsetted_data = s.copy_bytes();
        let expected_data: [u8; 97] = [
            0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x01, 0x00, 0x03,
            0x04, 0x05, 0x06, 0x00, 0x01, 0x00, 0x03, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00,
            0x00, 0x2b, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03,
            0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x01, 0x0b, 0x02, 0x0d, 0x04,
            0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x11, 0x00, 0x0e, 0x00, 0x01, 0x00,
            0x02, 0xc0, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00,
        ];
        assert_eq!(subsetted_data, expected_data);
    }

    #[test]
    fn test_subset_vvar_retain_gids() {
        let raw_bytes: [u8; 102] = [
            0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x01, 0x00, 0x04,
            0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x00, 0x04, 0x02, 0x01, 0x00, 0x03, 0x00, 0x01,
            0x00, 0x00, 0x00, 0x2e, 0x00, 0x02, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x10,
            0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x01, 0x0b, 0x02,
            0x0c, 0x03, 0x0d, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x20, 0x11,
            0x00, 0x0e, 0x00, 0x01, 0x00, 0x02, 0xc0, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x40, 0x00, 0x40, 0x00,
        ];

        let vvar = Vvar::read(FontData::new(&raw_bytes)).unwrap();
        let mut builder = FontBuilder::new();

        //dummy font
        let font = FontRef::new(&raw_bytes).unwrap();

        let mut plan = Plan::default();
        plan.new_to_old_gid_list
            .push((GlyphId::NOTDEF, GlyphId::NOTDEF));
        plan.new_to_old_gid_list
            .push((GlyphId::from(1_u32), GlyphId::from(1_u32)));
        plan.new_to_old_gid_list
            .push((GlyphId::from(3_u32), GlyphId::from(3_u32)));

        plan.glyphset.insert(GlyphId::NOTDEF);
        plan.glyphset.insert(GlyphId::from(1_u32));
        plan.glyphset.insert(GlyphId::from(3_u32));

        plan.subset_flags |= SubsetFlags::SUBSET_FLAGS_RETAIN_GIDS;

        let mut s = Serializer::new(1024);
        assert_eq!(s.start_serialize(), Ok(()));
        let ret = vvar.subset(&plan, &font, &mut s, &mut builder);
        assert!(ret.is_ok());
        assert!(!s.in_error());
        s.end_serialize();

        let subsetted_data = s.copy_bytes();
        let expected_data: [u8; 99] = [
            00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x01, 0x00, 0x04, 0x04,
            0x05, 0x00, 0x06, 0x00, 0x01, 0x00, 0x04, 0x01, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
            0x00, 0x00, 0x2b, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x10, 0x00,
            0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x01, 0x0b, 0x02, 0x0d,
            0x04, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x11, 0x00, 0x0e, 0x00, 0x01,
            0x00, 0x02, 0xc0, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00,
        ];
        assert_eq!(subsetted_data, expected_data);
    }
}