Skip to content

Commit 39f8ad3

Browse files
authored
Add better subsetting. (#76)
1 parent 43aa468 commit 39f8ad3

3 files changed

Lines changed: 30 additions & 30 deletions

File tree

Cargo.lock

Lines changed: 2 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ usvg = { version = "0.42.0", default-features = false }
2929
tiny-skia = "0.11.4"
3030
unicode-properties = "0.1.1"
3131
resvg = { version = "0.42.0", default-features = false }
32-
subsetter = "0.1.1"
32+
subsetter = {git = "https://github.com/typst/subsetter", rev = "4e0058b"}
3333
ttf-parser = { version = "0.21.1" }
3434
siphasher = { version = "1.0.1"}
3535

src/render/text.rs

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use siphasher::sip128::{Hasher128, SipHasher13};
1313
use std::collections::{BTreeMap, HashMap};
1414
use std::hash::Hash;
1515
use std::sync::Arc;
16+
use subsetter::GlyphRemapper;
1617
use ttf_parser::{name_id, Face, GlyphId, PlatformId, Tag};
1718
use unicode_properties::{GeneralCategory, UnicodeGeneralCategory};
1819
use usvg::{Fill, Group, ImageKind, Node, PaintOrder, Stroke, Transform};
@@ -45,6 +46,7 @@ pub fn write_font(
4546
let data_ref = alloc.alloc_ref();
4647

4748
let glyph_set = &mut font.glyph_set;
49+
let glyph_remapper = &font.glyph_remapper;
4850

4951
// Do we have a TrueType or CFF font?
5052
//
@@ -83,14 +85,10 @@ pub fn write_font(
8385
}
8486

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

9694
// Write all non-zero glyph widths.
@@ -156,12 +154,12 @@ pub fn write_font(
156154

157155
font_descriptor.finish();
158156

159-
let cmap = create_cmap(&ttf, glyph_set);
157+
let cmap =
158+
create_cmap(&ttf, glyph_set, glyph_remapper).ok_or(SubsetError(font.id))?;
160159
chunk.cmap(cmap_ref, &cmap.finish());
161160

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

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

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

209-
cmap
212+
Some(cmap)
210213
}
211214

212215
fn subset_font(
213216
font_data: &[u8],
214217
index: u32,
215-
glyphs: &[u16],
218+
glyph_remapper: &GlyphRemapper,
216219
id: fontdb::ID,
217220
) -> Result<Vec<u8>> {
218221
let data = font_data;
219-
let profile = subsetter::Profile::pdf(glyphs);
220-
let subsetted = subsetter::subset(data, index, profile);
221-
let mut data = subsetted.as_deref().unwrap_or(data);
222+
let subsetted =
223+
subsetter::subset(data, index, glyph_remapper).map_err(|_| SubsetError(id))?;
224+
let mut data = subsetted.as_ref();
222225

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

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

268-
let gid = glyph.id.0;
271+
// TODO: Remove unwraps and switch to error-based handling.
272+
let cid = font.glyph_remapper.get(glyph.id.0).unwrap();
269273
let ts = glyph
270274
.outline_transform()
271275
.pre_scale(font.units_per_em as f32, font.units_per_em as f32)
@@ -277,7 +281,7 @@ pub fn render(
277281
content.begin_text();
278282
content.set_text_matrix(ts.to_pdf_transform());
279283
content.set_font(Name(name.as_bytes()), span.font_size.get());
280-
content.show(Str(&[(gid >> 8) as u8, (gid & 0xff) as u8]));
284+
content.show(Str(&[(cid >> 8) as u8, (cid & 0xff) as u8]));
281285
content.end_text();
282286
content.restore_state();
283287
}
@@ -451,13 +455,6 @@ fn decode_mac_roman(coded: &[u8]) -> String {
451455
coded.iter().copied().map(char_from_mac_roman).collect()
452456
}
453457

454-
fn glyph_cid(ttf: &Face, glyph_id: u16) -> u16 {
455-
ttf.tables()
456-
.cff
457-
.and_then(|cff| cff.glyph_cid(GlyphId(glyph_id)))
458-
.unwrap_or(glyph_id)
459-
}
460-
461458
/// Extra methods for [`[T]`](slice).
462459
pub trait SliceExt<T> {
463460
/// Split a slice into consecutive runs with the same key and yield for
@@ -501,6 +498,7 @@ where
501498
pub struct Font {
502499
pub id: fontdb::ID,
503500
pub glyph_set: BTreeMap<u16, String>,
501+
pub glyph_remapper: GlyphRemapper,
504502
pub reference: Ref,
505503
pub face_data: Arc<Vec<u8>>,
506504
pub units_per_em: u16,
@@ -525,12 +523,14 @@ pub fn fill_fonts(group: &Group, ctx: &mut Context, fontdb: &fontdb::Database) {
525523
{
526524
let reference = allocator.alloc_ref();
527525
let glyph_set = BTreeMap::new();
526+
let glyph_remapper = GlyphRemapper::new();
528527
return Some(Font {
529528
id: g.font,
530529
reference,
531530
face_data: Arc::new(Vec::from(data)),
532531
units_per_em: ttf.units_per_em(),
533532
glyph_set,
533+
glyph_remapper,
534534
face_index,
535535
});
536536
}
@@ -542,6 +542,7 @@ pub fn fill_fonts(group: &Group, ctx: &mut Context, fontdb: &fontdb::Database) {
542542

543543
if let Some(ref mut font) = font {
544544
font.glyph_set.insert(g.id.0, g.text.clone());
545+
font.glyph_remapper.remap(g.id.0);
545546
}
546547
}
547548
}

0 commit comments

Comments
 (0)