diff --git a/src/cmap/subtable12.rs b/src/cmap/subtable12.rs index 88aad4a4..a566fc11 100644 --- a/src/cmap/subtable12.rs +++ b/src/cmap/subtable12.rs @@ -105,7 +105,7 @@ pub(crate) fn subset_subtable12(ctx: &Context, data: &[u8]) -> crate::Result crate::Result Result<()> { } }; - for old_gid in &ctx.reverse_gid_map { + for old_gid in &ctx.mapper.backward { write_offset(sub_glyf.len()); let data = table.glyph_data(*old_gid)?; @@ -185,7 +185,7 @@ fn remap_component_glyphs(ctx: &Context, data: &[u8]) -> Result> { let flags = r.read::()?; w.write(flags); let component = r.read::()?; - let new_component = *ctx.gid_map.get(&component).ok_or(Error::InvalidData)?; + let new_component = *ctx.mapper.forward.get(&component).ok_or(Error::InvalidData)?; w.write(new_component); if flags & ARG_1_AND_2_ARE_WORDS != 0 { diff --git a/src/hmtx.rs b/src/hmtx.rs index bd659c29..958130aa 100644 --- a/src/hmtx.rs +++ b/src/hmtx.rs @@ -17,7 +17,7 @@ pub(crate) fn subset(ctx: &mut Context) -> Result<()> { let mut last_advance_width = 0; for i in 0..ctx.subset.len() { - let original_gid = ctx.reverse_gid_map[i]; + let original_gid = ctx.mapper.backward[i]; if original_gid < num_h_metrics { let offset = original_gid as usize * 4; diff --git a/src/lib.rs b/src/lib.rs index 72078255..ab517eb0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,11 +48,15 @@ mod maxp; mod name; mod post; mod stream; +mod mapper; use crate::stream::{Reader, Structure, Writer}; use std::borrow::Cow; -use std::collections::{HashMap, HashSet}; +use std::collections::HashSet; use std::fmt::{self, Debug, Display, Formatter}; +use crate::mapper::{InternalMapper, MapperVariant}; + +pub use crate::mapper::Mapper; /// Subset a font face to include less glyphs and tables. /// @@ -63,7 +67,7 @@ pub fn subset( data: &[u8], index: u32, profile: &[u16], -) -> Result<(Vec, HashMap)> { +) -> Result<(Vec, Mapper)> { let face = parse(data, index)?; let kind = match face.table(Tag::CFF).or(face.table(Tag::CFF2)) { Some(_) => FontKind::Cff, @@ -82,8 +86,7 @@ pub fn subset( num_glyphs, subset: HashSet::new(), requested_glyphs, - gid_map: HashMap::new(), - reverse_gid_map: vec![], + mapper: InternalMapper::new(), kind, tables: vec![], long_loca: true, @@ -164,7 +167,7 @@ fn parse(data: &[u8], index: u32) -> Result> { } /// Construct a brand new font. -fn construct(mut ctx: Context) -> (Vec, HashMap) { +fn construct(mut ctx: Context) -> (Vec, Mapper) { let mut w = Writer::new(); w.write::(ctx.kind); @@ -228,7 +231,7 @@ fn construct(mut ctx: Context) -> (Vec, HashMap) { data[i..i + 4].copy_from_slice(&val.to_be_bytes()); } - (data, ctx.gid_map) + (data, Mapper(MapperVariant::HashmapMapper(ctx.mapper))) } /// Calculate a checksum over the sliced data as a sum of u32s. If the data @@ -255,11 +258,8 @@ struct Context<'a> { /// Actual glyphs that are needed to subset the font correctly, /// including glyphs referenced indirectly through components. subset: HashSet, - // A map from old gids to new gids - gid_map: HashMap, - // A map from new gids to old gids. The index represents the - // new gid, and the value at that index the old gid. - reverse_gid_map: Vec, + // A map from old gids to new gids, and the reverse + mapper: InternalMapper, /// The kind of face. kind: FontKind, /// Subsetted tables. @@ -303,8 +303,8 @@ impl<'a> Context<'a> { original_gids.sort(); for (counter, gid) in original_gids.into_iter().enumerate() { - self.gid_map.insert(*gid, counter as u16); - self.reverse_gid_map.push(*gid); + self.mapper.forward.insert(*gid, counter as u16); + self.mapper.backward.push(*gid); } } diff --git a/src/mapper.rs b/src/mapper.rs new file mode 100644 index 00000000..56501613 --- /dev/null +++ b/src/mapper.rs @@ -0,0 +1,41 @@ +use std::collections::HashMap; + +#[derive(Debug, Clone)] +pub(crate) struct InternalMapper { + pub(crate) forward: HashMap, + pub(crate) backward: Vec +} + +impl InternalMapper { + pub fn new() -> Self { + Self { + forward: HashMap::new(), + backward: Vec::new() + } + } +} + +#[derive(Debug, Clone)] +pub(crate) enum MapperVariant { + IdentityMapper, + HashmapMapper(InternalMapper) +} + +/// A mapper that maps old gids to new ones. +#[derive(Debug, Clone)] +pub struct Mapper(pub(crate) MapperVariant); + +impl Mapper { + /// Create a mapper that maps each gid to itself. + pub fn identity_mapper() -> Self { + Self(MapperVariant::IdentityMapper) + } + + /// Get the newly mapped gid for an old gid. + pub fn get(&self, gid: u16) -> Option { + match self.0 { + MapperVariant::IdentityMapper => Some(gid), + MapperVariant::HashmapMapper(ref h) => h.forward.get(&gid).copied() + } + } +} \ No newline at end of file diff --git a/src/post.rs b/src/post.rs index 25fd0d0e..186d5529 100644 --- a/src/post.rs +++ b/src/post.rs @@ -40,7 +40,7 @@ pub(crate) fn subset(ctx: &mut Context) -> Result<()> { let mut sub_strings = Writer::new(); let mut count = 0; for i in 0..num_glyphs { - let old_gid = ctx.reverse_gid_map[i as usize]; + let old_gid = ctx.mapper.backward[i as usize]; let index = indices[old_gid as usize]; if index <= 257 { diff --git a/tests/src/main.rs b/tests/src/main.rs index 6d0cc17e..04ea092c 100644 --- a/tests/src/main.rs +++ b/tests/src/main.rs @@ -1,9 +1,8 @@ use sha2::{Digest, Sha256}; -use std::collections::HashMap; use std::error::Error; use std::fs; use std::path::PathBuf; -use subsetter::subset; +use subsetter::{Mapper, subset}; use ttf_parser::GlyphId; #[rustfmt::skip] @@ -11,12 +10,12 @@ mod ttf; type Result = std::result::Result>; -const SAVE_SUBSETS: bool = true; +const SAVE_SUBSETS: bool = false; struct TestContext { font: Vec, subset: Vec, - gid_map: HashMap, + mapper: Mapper, gids: Vec, } @@ -42,13 +41,13 @@ fn get_test_context(font_file: &str, gids: &str) -> Result { let data = std::fs::read(font_path)?; let gids: Vec<_> = parse_gids(gids); - let (subset, gid_map) = subset(&data, 0, &gids)?; + let (subset, mapper) = subset(&data, 0, &gids)?; if SAVE_SUBSETS { save_font(&subset); } - Ok(TestContext { font: data, subset, gid_map, gids }) + Ok(TestContext { font: data, subset, mapper: mapper, gids }) } fn parse_gids(gids: &str) -> Vec { @@ -96,7 +95,7 @@ fn cmap(font_file: &str, gids: &str) { .collect::>(); for (c, gid) in relevant_chars { - let mapped_gid = ctx.gid_map.get(&gid.0).copied(); + let mapped_gid = ctx.mapper.get(gid.0); let cur_gid = new_face.glyph_index(c).map(|g| g.0); assert_eq!((c, mapped_gid), (c, cur_gid)); } @@ -139,7 +138,7 @@ fn glyph_metrics(font_file: &str, gids: &str) { .copied() .filter(|g| ctx.gids.contains(g) && *g < old_face.number_of_glyphs()) { - let mapped = *ctx.gid_map.get(&glyph).unwrap(); + let mapped = ctx.mapper.get(glyph).unwrap(); assert_eq!( old_face.glyph_bounding_box(GlyphId(glyph)), @@ -177,7 +176,7 @@ pub fn glyph_outlines(font_file: &str, gids: &str) { let mut sink1 = Sink::default(); let mut sink2 = Sink::default(); old_face.outline_glyph(GlyphId(glyph), &mut sink1); - new_face.outline_glyph(GlyphId(*ctx.gid_map.get(&glyph).unwrap()), &mut sink2); + new_face.outline_glyph(GlyphId(ctx.mapper.get(glyph).unwrap()), &mut sink2); assert_eq!(sink1, sink2); } }