Skip to content

Commit

Permalink
Add better subsetting. (#76)
Browse files Browse the repository at this point in the history
  • Loading branch information
LaurenzV authored Jun 16, 2024
1 parent 43aa468 commit 39f8ad3
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 30 deletions.
5 changes: 2 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ usvg = { version = "0.42.0", default-features = false }
tiny-skia = "0.11.4"
unicode-properties = "0.1.1"
resvg = { version = "0.42.0", default-features = false }
subsetter = "0.1.1"
subsetter = {git = "https://github.com/typst/subsetter", rev = "4e0058b"}
ttf-parser = { version = "0.21.1" }
siphasher = { version = "1.0.1"}

Expand Down
53 changes: 27 additions & 26 deletions src/render/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use siphasher::sip128::{Hasher128, SipHasher13};
use std::collections::{BTreeMap, HashMap};
use std::hash::Hash;
use std::sync::Arc;
use subsetter::GlyphRemapper;
use ttf_parser::{name_id, Face, GlyphId, PlatformId, Tag};
use unicode_properties::{GeneralCategory, UnicodeGeneralCategory};
use usvg::{Fill, Group, ImageKind, Node, PaintOrder, Stroke, Transform};
Expand Down Expand Up @@ -45,6 +46,7 @@ pub fn write_font(
let data_ref = alloc.alloc_ref();

let glyph_set = &mut font.glyph_set;
let glyph_remapper = &font.glyph_remapper;

// Do we have a TrueType or CFF font?
//
Expand Down Expand Up @@ -83,14 +85,10 @@ pub fn write_font(
}

let mut widths = vec![];
for gid in std::iter::once(0).chain(glyph_set.keys().copied()) {
let width = ttf.glyph_hor_advance(GlyphId(gid)).unwrap_or(0);
for old_gid in glyph_remapper.remapped_gids() {
let width = ttf.glyph_hor_advance(GlyphId(old_gid)).unwrap_or(0);
let units = (width as f64 / units_per_em as f64) * 1000.0;
let cid = glyph_cid(&ttf, gid);
if usize::from(cid) >= widths.len() {
widths.resize(usize::from(cid) + 1, 0.0);
widths[usize::from(cid)] = units as f32;
}
widths.push(units as f32);
}

// Write all non-zero glyph widths.
Expand Down Expand Up @@ -156,12 +154,12 @@ pub fn write_font(

font_descriptor.finish();

let cmap = create_cmap(&ttf, glyph_set);
let cmap =
create_cmap(&ttf, glyph_set, glyph_remapper).ok_or(SubsetError(font.id))?;
chunk.cmap(cmap_ref, &cmap.finish());

// Subset and write the font's bytes.
let glyphs: Vec<_> = glyph_set.keys().copied().collect();
let data = subset_font(&font.face_data, font.face_index, &glyphs, font.id)?;
let data = subset_font(&font.face_data, font.face_index, glyph_remapper, font.id)?;

let mut stream = chunk.stream(data_ref, &data);
stream.filter(Filter::FlateDecode);
Expand All @@ -174,7 +172,11 @@ pub fn write_font(
}

/// Create a /ToUnicode CMap.
fn create_cmap(ttf: &Face, glyph_set: &mut BTreeMap<u16, String>) -> UnicodeCmap {
fn create_cmap(
ttf: &Face,
glyph_set: &mut BTreeMap<u16, String>,
glyph_remapper: &GlyphRemapper,
) -> Option<UnicodeCmap> {
// For glyphs that have codepoints mapping to them in the font's cmap table,
// we prefer them over pre-existing text mappings from the document. Only
// things that don't have a corresponding codepoint (or only a private-use
Expand All @@ -201,24 +203,25 @@ fn create_cmap(ttf: &Face, glyph_set: &mut BTreeMap<u16, String>) -> UnicodeCmap
// Produce a reverse mapping from glyphs' CIDs to unicode strings.
let mut cmap = UnicodeCmap::new(CMAP_NAME, SYSTEM_INFO);
for (&g, text) in glyph_set.iter() {
let new_gid = glyph_remapper.get(g)?;
if !text.is_empty() {
cmap.pair_with_multiple(glyph_cid(ttf, g), text.chars());
cmap.pair_with_multiple(new_gid, text.chars());
}
}

cmap
Some(cmap)
}

fn subset_font(
font_data: &[u8],
index: u32,
glyphs: &[u16],
glyph_remapper: &GlyphRemapper,
id: fontdb::ID,
) -> Result<Vec<u8>> {
let data = font_data;
let profile = subsetter::Profile::pdf(glyphs);
let subsetted = subsetter::subset(data, index, profile);
let mut data = subsetted.as_deref().unwrap_or(data);
let subsetted =
subsetter::subset(data, index, glyph_remapper).map_err(|_| SubsetError(id))?;
let mut data = subsetted.as_ref();

// Extract the standalone CFF font program if applicable.
let face = ttf_parser::RawFace::parse(data, 0).map_err(|_| SubsetError(id))?;
Expand Down Expand Up @@ -265,7 +268,8 @@ pub fn render(

let name = font_names.get(&font.reference).unwrap();

let gid = glyph.id.0;
// TODO: Remove unwraps and switch to error-based handling.
let cid = font.glyph_remapper.get(glyph.id.0).unwrap();
let ts = glyph
.outline_transform()
.pre_scale(font.units_per_em as f32, font.units_per_em as f32)
Expand All @@ -277,7 +281,7 @@ pub fn render(
content.begin_text();
content.set_text_matrix(ts.to_pdf_transform());
content.set_font(Name(name.as_bytes()), span.font_size.get());
content.show(Str(&[(gid >> 8) as u8, (gid & 0xff) as u8]));
content.show(Str(&[(cid >> 8) as u8, (cid & 0xff) as u8]));
content.end_text();
content.restore_state();
}
Expand Down Expand Up @@ -451,13 +455,6 @@ fn decode_mac_roman(coded: &[u8]) -> String {
coded.iter().copied().map(char_from_mac_roman).collect()
}

fn glyph_cid(ttf: &Face, glyph_id: u16) -> u16 {
ttf.tables()
.cff
.and_then(|cff| cff.glyph_cid(GlyphId(glyph_id)))
.unwrap_or(glyph_id)
}

/// Extra methods for [`[T]`](slice).
pub trait SliceExt<T> {
/// Split a slice into consecutive runs with the same key and yield for
Expand Down Expand Up @@ -501,6 +498,7 @@ where
pub struct Font {
pub id: fontdb::ID,
pub glyph_set: BTreeMap<u16, String>,
pub glyph_remapper: GlyphRemapper,
pub reference: Ref,
pub face_data: Arc<Vec<u8>>,
pub units_per_em: u16,
Expand All @@ -525,12 +523,14 @@ pub fn fill_fonts(group: &Group, ctx: &mut Context, fontdb: &fontdb::Database) {
{
let reference = allocator.alloc_ref();
let glyph_set = BTreeMap::new();
let glyph_remapper = GlyphRemapper::new();
return Some(Font {
id: g.font,
reference,
face_data: Arc::new(Vec::from(data)),
units_per_em: ttf.units_per_em(),
glyph_set,
glyph_remapper,
face_index,
});
}
Expand All @@ -542,6 +542,7 @@ pub fn fill_fonts(group: &Group, ctx: &mut Context, fontdb: &fontdb::Database) {

if let Some(ref mut font) = font {
font.glyph_set.insert(g.id.0, g.text.clone());
font.glyph_remapper.remap(g.id.0);
}
}
}
Expand Down

0 comments on commit 39f8ad3

Please sign in to comment.