skera 0.6.0

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

use crate::{
    hvar::{serialize_index_maps, HvarVvarSubsetPlan, ListupIndexMaps},
    offset::SerializeSubset,
    serialize::{SerializeErrorFlags, Serializer},
    Plan, Serialize, Subset, SubsetError,
};
use write_fonts::{
    read::{
        tables::{variations::DeltaSetIndexMap, vvar::Vvar},
        FontRef, ReadError, TopLevelTable,
    },
    types::{FixedSize, 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> {
        Self::serialize(s, (self, plan)).map_err(|_| SubsetError::SubsetTableError(Vvar::TAG))
    }
}

impl<'a> Serialize<'a> for Vvar<'_> {
    type Args = (&'a Vvar<'a>, &'a Plan);
    fn serialize(s: &mut Serializer, args: Self::Args) -> Result<(), SerializeErrorFlags> {
        let (vvar, plan) = args;
        s.embed(vvar.version())?;
        if vvar.item_variation_store_offset().is_null() {
            return s
                .allocate_size(5 * Offset32::RAW_BYTE_LEN, true)
                .map(|_| ());
        }
        let var_store = vvar
            .item_variation_store()
            .map_err(|_| s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR))?;

        let index_maps = vvar
            .listup_index_maps()
            .map_err(|_| s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR))?;

        let vvar_subset_plan = HvarVvarSubsetPlan::new(plan, &var_store, &index_maps)
            .map_err(|_| s.set_err(SerializeErrorFlags::SERIALIZE_ERROR_READ_ERROR))?;

        let var_store_offset_pos = s.embed(0_u32)?;

        Offset32::serialize_subset(
            &var_store,
            s,
            plan,
            (vvar_subset_plan.inner_maps(), true),
            var_store_offset_pos,
        )?;

        serialize_index_maps(
            s,
            plan,
            &index_maps,
            vvar_subset_plan.index_map_subset_plans(),
        )
    }
}

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