1
use rustc_hash::FxHashMap;
2

            
3
use super::{GlyfRecord, GlyfTable, Glyph, ParseError};
4
use crate::subset::SubsetGlyphs;
5
use crate::tables::glyf::CompositeGlyph;
6

            
7
/// A subset glyph
8
#[derive(Clone)]
9
pub struct SubsetGlyph<'a> {
10
    /// The old glyph id of this glyph
11
    pub old_id: u16,
12
    /// The glyph
13
    pub record: GlyfRecord<'a>,
14
}
15

            
16
/// A `glyf` table that has been subset.
17
#[derive(Clone)]
18
pub struct SubsetGlyf<'a> {
19
    glyphs: Vec<SubsetGlyph<'a>>,
20
    /// Maps an old glyph index to its index in the new table
21
    old_to_new_id: FxHashMap<u16, u16>,
22
}
23

            
24
impl<'a> GlyfTable<'a> {
25
    /// Returns a copy of this table that only contains the glyphs specified by `glyph_ids`.
26
    pub fn subset(&self, glyph_ids: &[u16]) -> Result<SubsetGlyf<'a>, ParseError> {
27
        let mut glyph_ids = glyph_ids.to_vec();
28
        let mut records = Vec::with_capacity(glyph_ids.len());
29

            
30
        let mut i = 0;
31
        while i < glyph_ids.len() {
32
            let glyph_id = glyph_ids[i];
33
            let mut record = self
34
                .records
35
                .get(usize::from(glyph_id))
36
                .ok_or(ParseError::BadIndex)?
37
                .clone();
38
            if record.is_composite() {
39
                record.parse()?;
40
                let GlyfRecord::Parsed(Glyph::Composite(composite)) = &mut record else {
41
                    unreachable!("not a composite glyph")
42
                };
43
                add_glyph(&mut glyph_ids, composite);
44
            }
45
            records.push(SubsetGlyph {
46
                old_id: glyph_id,
47
                record,
48
            });
49
            i += 1;
50
        }
51
        // Cast should be safe as there must be less than u16::MAX glyphs in a font
52
        let old_to_new_id = records
53
            .iter()
54
            .enumerate()
55
            .map(|(new_id, glyph)| (glyph.old_id, new_id as u16))
56
            .collect();
57
        Ok(SubsetGlyf {
58
            glyphs: records,
59
            old_to_new_id,
60
        })
61
    }
62
}
63

            
64
impl SubsetGlyphs for SubsetGlyf<'_> {
65
    fn len(&self) -> usize {
66
        self.glyphs.len()
67
    }
68

            
69
    fn old_id(&self, new_id: u16) -> u16 {
70
        self.glyphs[usize::from(new_id)].old_id
71
    }
72

            
73
    fn new_id(&self, old_id: u16) -> u16 {
74
        self.old_to_new_id.get(&old_id).copied().unwrap_or(0)
75
    }
76
}
77

            
78
impl<'a> From<SubsetGlyf<'a>> for GlyfTable<'a> {
79
    fn from(subset_glyphs: SubsetGlyf<'a>) -> Self {
80
        let records = subset_glyphs
81
            .glyphs
82
            .into_iter()
83
            .map(|subset_record| subset_record.record)
84
            .collect();
85

            
86
        GlyfTable { records }
87
    }
88
}
89

            
90
/// Add each of the child glyphs contained within a composite glyph to the subset font.
91
///
92
/// Updates the composite glyph indexes to point at the new child indexes.
93
fn add_glyph(glyph_ids: &mut Vec<u16>, composite: &mut CompositeGlyph) {
94
    for composite_glyph in composite.glyphs.iter_mut() {
95
        let new_id = glyph_ids
96
            .iter()
97
            .position(|&id| id == composite_glyph.glyph_index)
98
            .unwrap_or_else(|| {
99
                // Add this glyph to the list of ids to include in the subset font
100
                let new_id = glyph_ids.len();
101
                glyph_ids.push(composite_glyph.glyph_index);
102
                new_id
103
            });
104
        composite_glyph.glyph_index = new_id as u16;
105
    }
106
}