Skip to content

Commit

Permalink
Implement name record pruning
Browse files Browse the repository at this point in the history
  • Loading branch information
LaurenzV committed Feb 24, 2024
1 parent 8fa0d5c commit c67360a
Show file tree
Hide file tree
Showing 10 changed files with 130 additions and 9 deletions.
Binary file removed fonts/ClickerScript-Regular.ttf
Binary file not shown.
Binary file removed fonts/LatinModernRoman-Regular.otf
Binary file not shown.
Binary file removed fonts/NewCMMath-Regular.otf
Binary file not shown.
Binary file removed fonts/NotoSansCJKsc-Regular.otf
Binary file not shown.
4 changes: 2 additions & 2 deletions ft.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
fonttools subset fonts/NotoSans-Regular.ttf --drop-tables=GSUB,GPOS,GDEF \
--gids=80-100,3,4,10,30,31,300-330 --glyph-names --output-file=out_ft.ttf \
--notdef-outline --no-prune-unicode-ranges --name-IDs='*'
--gids=5,6,9,10 --glyph-names --output-file=out_ft.ttf \
--notdef-outline --no-prune-unicode-ranges
fonttools ttx -f -o out_ft.ttx out_ft.ttf
2 changes: 1 addition & 1 deletion src/cmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ pub(crate) fn subset(ctx: &mut Context) -> Result<()> {
}
}

if subsetted_subtables.len() == 0 {
if subsetted_subtables.len() == 0 && num_tables != 0 {
// The font only contains non-Unicode subtables.
return Err(Error::Unimplemented);
}
Expand Down
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ mod head;
mod hhea;
mod hmtx;
mod maxp;
mod name;
mod post;
mod stream;

Expand Down Expand Up @@ -318,6 +319,7 @@ impl<'a> Context<'a> {
Tag::POST => post::subset(self)?,
Tag::CMAP => cmap::subset(self)?,
Tag::MAXP => maxp::subset(self)?,
Tag::NAME => name::subset(self)?,
_ => self.push(tag, data),
}

Expand All @@ -328,8 +330,6 @@ impl<'a> Context<'a> {
let mut original_gids = self.subset.iter().collect::<Vec<_>>();
original_gids.sort();

println!("{:?}", original_gids);

let mut counter = 0;
for gid in original_gids {
self.gid_map.insert(*gid, counter);
Expand Down
4 changes: 1 addition & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ fn main() {
// Keep only three glyphs and the OpenType tables
// required for embedding the font in a PDF file.
let mut glyphs = vec![];
glyphs.extend(80..=100);
glyphs.extend([3, 4, 10, 30, 31]);
glyphs.extend(300..=330);
glyphs.extend([5, 6, 9, 10]);
let profile = Profile::pdf(&glyphs);
let sub = subset(&data, 0, profile).unwrap();

Expand Down
124 changes: 124 additions & 0 deletions src/name.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
use super::*;

struct NameRecord {
platform_id: u16,
encoding_id: u16,
language_id: u16,
name_id: u16,
length: u16,
string_offset: u16,
}

impl NameRecord {
fn is_unicode(&self) -> bool {
self.platform_id == 0
|| (self.platform_id == 3 && [0, 1, 10].contains(&self.encoding_id))
}
}

impl Structure<'_> for NameRecord {
fn read(r: &mut Reader<'_>) -> Result<Self> {
let platform_id = r.read::<u16>()?;
let encoding_id = r.read::<u16>()?;
let language_id = r.read::<u16>()?;
let name_id = r.read::<u16>()?;
let length = r.read::<u16>()?;
let string_offset = r.read::<u16>()?;

Ok(Self {
platform_id,
encoding_id,
language_id,
name_id,
length,
string_offset,
})
}

fn write(&self, w: &mut Writer) {
w.write::<u16>(self.platform_id);
w.write::<u16>(self.encoding_id);
w.write::<u16>(self.language_id);
w.write::<u16>(self.name_id);
w.write::<u16>(self.length);
w.write::<u16>(self.string_offset);
}
}

pub(crate) fn subset(ctx: &mut Context) -> Result<()> {
let name = ctx.expect_table(Tag::NAME)?;
let mut r = Reader::new(name);

// From the over 3k font (variations) I had locally, none had version 1.
// So we only focus on subsetting version 0 and on the off-chance
// that a font has version 1, we just add it as is.
let version = r.read::<u16>()?;

if version != 0 {
ctx.push(Tag::NAME, name);
return Ok(());
}

let count = r.read::<u16>()?;
r.read::<u16>()?; // storage offset

let mut name_records = vec![];

for _ in 0..count {
name_records.push(r.read::<NameRecord>()?);
}

let storage = r.data();

let mut pruned = prune_name_records(name_records);

if pruned.len() == 0 && count != 0 {
// Only contains non-Unicode records, so we don't subset.
ctx.push(Tag::NAME, name);
return Ok(());
}

let mut sub_name = Writer::new();
let mut new_storage = Writer::new();
let mut cur_storage_offset = 0;

let count = pruned.len() as u16;

// version
sub_name.write::<u16>(0);
// count
sub_name.write::<u16>(count);
// storage offset
sub_name.write::<u16>(2 * 3 + count * 12);

for record in &mut pruned {
new_storage.give(
&storage[(record.string_offset as usize)
..((record.string_offset + record.length) as usize)],
);
record.string_offset = cur_storage_offset;
sub_name.write_ref::<NameRecord>(record);
cur_storage_offset += record.length;
}

sub_name.give(&new_storage.finish());

ctx.push(Tag::NAME, sub_name.finish());
Ok(())
}

fn prune_name_records(name_records: Vec<NameRecord>) -> Vec<NameRecord> {
let mut pruned = vec![];

for record in name_records {
if record.is_unicode() {
// TODO: Determine which exact records we need. But the PDF reference
// doesn't seem to indicate anything about this.
if [0, 1, 2, 3, 4, 5, 6].contains(&record.name_id) {
pruned.push(record);
}
}
}

pruned
}
1 change: 0 additions & 1 deletion src/post.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ pub(crate) fn subset(ctx: &mut Context) -> Result<()> {
}

let num_glyphs = ctx.subset.len() as u16;
println!("{:?}", num_glyphs);

// Start writing a new subsetted post table.
let mut sub_post = Writer::new();
Expand Down

0 comments on commit c67360a

Please sign in to comment.