Panic: "remapper was overflowed" when composite glyphs reach all 65,536 glyph IDs
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 56/100
Research direction
Start by reading subsetter-0.2.6/src/remapper.rs around the overflowing u16 count and trace how glyf::closure remaps composite components. The reproduction in the issue demonstrates the failure when all 65,536 glyph IDs are reached; use it to check a fix. Done means subset returns an error or handles out-of-range component IDs without panicking.
Written by the indexing model from the issue text.
Description
subset panics on a TrueType font whose glyph closure reaches all 65,536 glyph IDs, in any build:
thread 'main' panicked at subsetter-0.2.6/src/remapper.rs:91:18:
remapper was overflowed
glyf::closure remaps every glyph it reaches through composite components, and Remapper::remap counts them in a u16 with .expect("remapper was overflowed"). Components may name glyph IDs past maxp's glyph count, which Table::glyph_data still reads where loca gives them an entry, so a composite glyph (or several) can reach every ID from 0 to 65535: the 65,536th glyph remapped panics. This happens with 0.2.6 and with main (b0adcc1). (#34 is the same expect reached through the Font DICT remapper of CFF fonts.)
To reproduce with subsetter 0.2.6 (default-features = false). The font is built for this report: three tables, glyf, head and loca, where .notdef is a composite of the empty glyphs 1 to 65535.
fn main() {
// A TrueType font of three tables, built for this report: glyph 0 is a composite of
// the empty glyphs 1 to 65535, which `loca` gives entries.
let mut glyf = vec![0xFF, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0]; // numberOfContours -1, bbox
for glyph in 1..=u16::MAX {
let flags: u16 = if glyph == u16::MAX { 0x0002 } else { 0x0022 }; // MORE_COMPONENTS
glyf.extend(flags.to_be_bytes());
glyf.extend(glyph.to_be_bytes());
glyf.extend([0, 0]); // byte arguments
}
let mut loca = Vec::new();
loca.extend(0u32.to_be_bytes());
for _ in 0..=u16::MAX {
loca.extend((glyf.len() as u32).to_be_bytes()); // glyphs 1.. are empty
}
let mut head = vec![0; 54];
head[51] = 1; // indexToLocFormat: long
let tables: [(&[u8; 4], Vec<u8>); 3] = [(b"glyf", glyf), (b"head", head), (b"loca", loca)];
let mut font = vec![0, 1, 0, 0, 0, 3, 0, 32, 0, 1, 0, 16];
let mut offset = 12 + 16 * tables.len();
for (tag, table) in &tables {
font.extend(*tag);
font.extend([0; 4]); // checksum, not checked
font.extend((offset as u32).to_be_bytes());
font.extend((table.len() as u32).to_be_bytes());
offset += table.len().next_multiple_of(4);
}
for (_, table) in &tables {
font.extend(table);
font.resize(font.len().next_multiple_of(4), 0);
}
let remapper = subsetter::GlyphRemapper::new();
println!("{:?}", subsetter::subset(&font, 0, &remapper).map(|font| font.len()));
}
cargo run and cargo run --release both panic as above.
It was found by the independent review of a officewright PR that checks glyphs for subsetting; until it is fixed, our call to subset refuses such a subset before the subsetter runs.
A possible fix: return an error from closure (for example MalformedFont) once the remapper holds 65,536 glyphs, or skip components that are not below maxp's glyph count.
- Dominant language
- Rust
- Stars
- 61
- Forks
- 16
- PR merge metrics
- No merged PRs in 30d
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from typst/subsetter
-
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 83/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
-
head table bounding box is not recalculated for variable font instancesPossibly taken @khaledhosny claimed this 16 days ago. Open
Difficulty 3/5 1-2 days Newbie friendliness 56/100
-
Difficulty 5/5 Over a week Newbie friendliness 45/100
Similar issues
-
good first issue help wanted
Difficulty 2/5 1-3 hours Newbie friendliness 80/100
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
swc-project/swc-ai-issues#907 ·
-
Difficulty 1/5 Under an hour Newbie friendliness 62/100
-
area:helper kind:bug priority:p3 ready
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
Maintainers usually reply within 1 day
-
bug UI
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
wardian-app/Wardian#1746 ·
Maintainers usually reply within 1 day