Panic: "remapper was overflowed" when composite glyphs reach all 65,536 glyph IDs

Aperta
#35 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
4/5
Tempo stimato
1-2 giorni
Idoneità per principianti
56/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
rust
Ambito
backend

Direzione di ricerca

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.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

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.

Lingua principale
Rust
Stelle
61
Fork
16
Metriche di merge delle PR
Nessuna PR unita negli ultimi 30g

Preparare l'ambiente

Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di typst/subsetter

Tutte le issue di typst/subsetter

Issue simili

Altre issue su Rust

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.