Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add better subsetting. #76

Merged
merged 5 commits into from
Jun 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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