allsorts_subset_browser/tables/glyf/
subset.rs

1use rustc_hash::FxHashMap;
2
3use super::{GlyfRecord, GlyfTable, Glyph, ParseError};
4use crate::subset::SubsetGlyphs;
5use crate::tables::glyf::CompositeGlyph;
6
7#[derive(Clone)]
8pub struct SubsetGlyph<'a> {
9    pub old_id: u16,
10    pub record: GlyfRecord<'a>,
11}
12
13/// A `glyf` table that has been subset.
14#[derive(Clone)]
15pub struct SubsetGlyf<'a> {
16    glyphs: Vec<SubsetGlyph<'a>>,
17    /// Maps an old glyph index to its index in the new table
18    old_to_new_id: FxHashMap<u16, u16>,
19}
20
21impl<'a> GlyfTable<'a> {
22    /// Returns a copy of this table that only contains the glyphs specified by `glyph_ids`.
23    pub fn subset(&self, glyph_ids: &[u16]) -> Result<SubsetGlyf<'a>, ParseError> {
24        let mut glyph_ids = glyph_ids.to_vec();
25        let mut records = Vec::with_capacity(glyph_ids.len());
26
27        let mut i = 0;
28        while i < glyph_ids.len() {
29            let glyph_id = glyph_ids[i];
30            let mut record = self
31                .records
32                .get(usize::from(glyph_id))
33                .ok_or(ParseError::BadIndex)?
34                .clone();
35            if record.is_composite() {
36                record.parse()?;
37                let GlyfRecord::Parsed(Glyph::Composite(composite)) = &mut record else {
38                    unreachable!("not a composite glyph")
39                };
40                add_glyph(&mut glyph_ids, composite);
41            }
42            records.push(SubsetGlyph {
43                old_id: glyph_id,
44                record,
45            });
46            i += 1;
47        }
48        // Cast should be safe as there must be less than u16::MAX glyphs in a font
49        let old_to_new_id = records
50            .iter()
51            .enumerate()
52            .map(|(new_id, glyph)| (glyph.old_id, new_id as u16))
53            .collect();
54        Ok(SubsetGlyf {
55            glyphs: records,
56            old_to_new_id,
57        })
58    }
59}
60
61impl<'a> SubsetGlyphs for SubsetGlyf<'a> {
62    fn len(&self) -> usize {
63        self.glyphs.len()
64    }
65
66    fn old_id(&self, new_id: u16) -> u16 {
67        self.glyphs[usize::from(new_id)].old_id
68    }
69
70    fn new_id(&self, old_id: u16) -> u16 {
71        self.old_to_new_id.get(&old_id).copied().unwrap_or(0)
72    }
73}
74
75impl<'a> From<SubsetGlyf<'a>> for GlyfTable<'a> {
76    fn from(subset_glyphs: SubsetGlyf<'a>) -> Self {
77        let records = subset_glyphs
78            .glyphs
79            .into_iter()
80            .map(|subset_record| subset_record.record)
81            .collect();
82
83        GlyfTable { records }
84    }
85}
86
87/// Add each of the child glyphs contained within a composite glyph to the subset font.
88///
89/// Updates the composite glyph indexes to point at the new child indexes.
90fn add_glyph(glyph_ids: &mut Vec<u16>, composite: &mut CompositeGlyph<'_>) {
91    for composite_glyph in composite.glyphs.iter_mut() {
92        let new_id = glyph_ids
93            .iter()
94            .position(|&id| id == composite_glyph.glyph_index)
95            .unwrap_or_else(|| {
96                // Add this glyph to the list of ids to include in the subset font
97                let new_id = glyph_ids.len();
98                glyph_ids.push(composite_glyph.glyph_index);
99                new_id
100            });
101        composite_glyph.glyph_index = new_id as u16;
102    }
103}