fontcull-klippa 0.1.2

Subsetting a font file according to provided input. (Vendored fork for fontcull)
Documentation
//! impl subset() for name table
use crate::{
    serialize::{OffsetWhence, Serializer},
    Plan, Subset,
    SubsetError::{self, SubsetTableError},
    SubsetFlags,
};

use fontcull_write_fonts::{
    read::{
        tables::name::{Name, NameRecord},
        FontRef, TopLevelTable,
    },
    types::FixedSize,
    FontBuilder,
};

// reference: subset() for name table in harfbuzz
// https://github.com/harfbuzz/harfbuzz/blob/a070f9ebbe88dc71b248af9731dd49ec93f4e6e6/src/OT/name/name.hh#L387
impl Subset for Name<'_> {
    fn subset(
        &self,
        plan: &Plan,
        _font: &FontRef,
        s: &mut Serializer,
        _builder: &mut FontBuilder,
    ) -> Result<(), SubsetError> {
        let name_records = self.name_record();
        //TODO: support name_table_override
        //TODO: support name table version 1
        let mut retained_name_record_idxes = name_records
            .iter()
            .enumerate()
            .filter_map(|(idx, record)| {
                if !plan.name_ids.contains(record.name_id())
                    || !plan.name_languages.contains(record.language_id())
                    || (!plan
                        .subset_flags
                        .contains(SubsetFlags::SUBSET_FLAGS_NAME_LEGACY)
                        && !record.is_unicode())
                {
                    return None;
                }
                Some(idx)
            })
            .collect::<Vec<_>>();

        retained_name_record_idxes.sort_unstable_by_key(|nr| {
            let nr = name_records[*nr];
            (
                nr.platform_id(),
                nr.encoding_id(),
                nr.language_id(),
                nr.name_id().to_u16(),
                nr.length(),
            )
        });

        // version
        // TODO: support version 1
        s.embed(0_u16)
            .map_err(|_| SubsetError::SubsetTableError(Name::TAG))?;
        //count
        let count = retained_name_record_idxes.len() as u16;
        s.embed(count)
            .map_err(|_| SubsetError::SubsetTableError(Name::TAG))?;
        //storage_offset
        let storage_offset = count * NameRecord::RAW_BYTE_LEN as u16 + 6;
        s.embed(storage_offset)
            .map_err(|_| SubsetError::SubsetTableError(Name::TAG))?;

        serialize_name_records(self, s, &retained_name_record_idxes)
    }
}

fn serialize_name_records(
    name: &Name,
    s: &mut Serializer,
    retained_name_record_idxes: &[usize],
) -> Result<(), SubsetError> {
    let data = name.offset_data().as_bytes();
    let name_records = name.name_record();
    let name_records_bytes = data.get(name.shape().name_record_byte_range()).unwrap();
    let storage_start = name.storage_offset() as usize;
    for idx in retained_name_record_idxes.iter() {
        let len = s.length();
        let record_pos = idx * NAME_RECORD_SIZE;
        let record_bytes = name_records_bytes
            .get(record_pos..record_pos + NAME_RECORD_SIZE)
            .ok_or(SubsetError::SubsetTableError(Name::TAG))?;
        s.embed_bytes(record_bytes)
            .map_err(|_| SubsetError::SubsetTableError(Name::TAG))?;

        let record = name_records[*idx];
        let offset = record.string_offset().to_u32() as usize;

        // 10 is the position of offset field within a NameRecord
        let offset_pos = len + 10;
        s.push()
            .map_err(|_| SubsetError::SubsetTableError(Name::TAG))?;

        //copy string data
        let str_start = storage_start + offset;
        let str_len = record.length();
        let str_bytes = data
            .get(str_start..str_start + str_len as usize)
            .ok_or(SubsetTableError(Name::TAG))?;
        s.embed_bytes(str_bytes)
            .map_err(|_| SubsetError::SubsetTableError(Name::TAG))?;
        let obj_idx = s
            .pop_pack(true)
            .ok_or(SubsetError::SubsetTableError(Name::TAG))?;
        s.add_link(
            offset_pos..offset_pos + 2,
            obj_idx,
            OffsetWhence::Tail,
            0,
            false,
        )
        .map_err(|_| SubsetError::SubsetTableError(Name::TAG))?;
    }
    Ok(())
}

//NameRecord size in bytes
const NAME_RECORD_SIZE: usize = NameRecord::RAW_BYTE_LEN;