Skip to content

Commit

Permalink
Introduce mapper struct
Browse files Browse the repository at this point in the history
  • Loading branch information
LaurenzV committed Feb 26, 2024
1 parent 147566f commit b53563c
Show file tree
Hide file tree
Showing 8 changed files with 68 additions and 28 deletions.
2 changes: 1 addition & 1 deletion src/cmap/subtable12.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ pub(crate) fn subset_subtable12(ctx: &Context, data: &[u8]) -> crate::Result<Vec
.filter_map(|c| {
if let Some(g) = subtable.glyph_index(c) {
if ctx.requested_glyphs.contains(&g) {
if let Some(new_g) = ctx.gid_map.get(&g) {
if let Some(new_g) = ctx.mapper.forward.get(&g) {
return Some((c, *new_g));
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/cmap/subtable4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ pub(crate) fn subset_subtable4(ctx: &Context, data: &[u8]) -> crate::Result<Vec<
.filter_map(|c| {
if let Some(g) = subtable.glyph_index(c as u32) {
if ctx.requested_glyphs.contains(&g) {
if let Some(new_g) = ctx.gid_map.get(&g) {
if let Some(new_g) = ctx.mapper.forward.get(&g) {
return Some((c, *new_g));
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/glyf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ pub(crate) fn subset(ctx: &mut Context) -> 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)?;

Expand Down Expand Up @@ -185,7 +185,7 @@ fn remap_component_glyphs(ctx: &Context, data: &[u8]) -> Result<Vec<u8>> {
let flags = r.read::<u16>()?;
w.write(flags);
let component = r.read::<u16>()?;
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 {
Expand Down
2 changes: 1 addition & 1 deletion src/hmtx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
26 changes: 13 additions & 13 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand All @@ -63,7 +67,7 @@ pub fn subset(
data: &[u8],
index: u32,
profile: &[u16],
) -> Result<(Vec<u8>, HashMap<u16, u16>)> {
) -> Result<(Vec<u8>, Mapper)> {
let face = parse(data, index)?;
let kind = match face.table(Tag::CFF).or(face.table(Tag::CFF2)) {
Some(_) => FontKind::Cff,
Expand All @@ -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,
Expand Down Expand Up @@ -164,7 +167,7 @@ fn parse(data: &[u8], index: u32) -> Result<Face<'_>> {
}

/// Construct a brand new font.
fn construct(mut ctx: Context) -> (Vec<u8>, HashMap<u16, u16>) {
fn construct(mut ctx: Context) -> (Vec<u8>, Mapper) {
let mut w = Writer::new();
w.write::<FontKind>(ctx.kind);

Expand Down Expand Up @@ -228,7 +231,7 @@ fn construct(mut ctx: Context) -> (Vec<u8>, HashMap<u16, u16>) {
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
Expand All @@ -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<u16>,
// A map from old gids to new gids
gid_map: HashMap<u16, u16>,
// 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<u16>,
// A map from old gids to new gids, and the reverse
mapper: InternalMapper,
/// The kind of face.
kind: FontKind,
/// Subsetted tables.
Expand Down Expand Up @@ -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);
}
}

Expand Down
41 changes: 41 additions & 0 deletions src/mapper.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use std::collections::HashMap;

#[derive(Debug, Clone)]
pub(crate) struct InternalMapper {
pub(crate) forward: HashMap<u16, u16>,
pub(crate) backward: Vec<u16>
}

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<u16> {
match self.0 {
MapperVariant::IdentityMapper => Some(gid),
MapperVariant::HashmapMapper(ref h) => h.forward.get(&gid).copied()
}
}
}
2 changes: 1 addition & 1 deletion src/post.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
17 changes: 8 additions & 9 deletions tests/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
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]
mod ttf;

type Result<T> = std::result::Result<T, Box<dyn Error>>;

const SAVE_SUBSETS: bool = true;
const SAVE_SUBSETS: bool = false;

struct TestContext {
font: Vec<u8>,
subset: Vec<u8>,
gid_map: HashMap<u16, u16>,
mapper: Mapper,
gids: Vec<u16>,
}

Expand All @@ -42,13 +41,13 @@ fn get_test_context(font_file: &str, gids: &str) -> Result<TestContext> {
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<u16> {
Expand Down Expand Up @@ -96,7 +95,7 @@ fn cmap(font_file: &str, gids: &str) {
.collect::<Vec<_>>();

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));
}
Expand Down Expand Up @@ -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)),
Expand Down Expand Up @@ -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);
}
}
Expand Down

0 comments on commit b53563c

Please sign in to comment.