From ee0f367b1160212394aa253e0bc2162a04f3f0a9 Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Tue, 3 Nov 2026 12:58:58 +0000 Subject: [PATCH] address review: EPS font look-back stops at the previous font; charge input characters --- crates/pdf/src/ps/eps.rs | 13 ++++++++++++- crates/pdf/src/ps/text.rs | 2 ++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/crates/pdf/src/ps/eps.rs b/crates/pdf/src/ps/eps.rs index aed984bc8..8fb72698a 100644 --- a/crates/pdf/src/ps/eps.rs +++ b/crates/pdf/src/ps/eps.rs @@ -67,7 +67,8 @@ pub(super) fn embedded_fonts(ps: &[u8]) -> HashMap>, bool)> while let Some(ee) = find_from(ps, b"eexec", from) { from = ee + 5; let last_before = |v: &[usize]| v[..v.partition_point(|&p| p < ee)].last().copied().filter(|&p| p >= used); - let start = last_before(&heads).or_else(|| last_before(&names).map(|p| p.saturating_sub(512))); + // The fallback look-back stops at the previous font's end, so it cannot pick up its header. + let start = last_before(&heads).or_else(|| last_before(&names).map(|p| p.saturating_sub(512).max(used))); let Some(start) = start else { continue }; let end = find_from(ps, b"cleartomark", ee).map_or(ps.len(), |p| p + 11); if let Some(name) = name_after(&ps[start..ee], b"/FontName") { @@ -106,4 +107,14 @@ mod tests { assert_eq!(fonts.len(), 1); assert!(fonts.contains_key("Real")); } + + #[test] + fn header_less_font_after_a_short_one_does_not_take_its_header() { + let a = "%!PS-AdobeFont-1.0: A\n/FontName /A def\ncurrentfile eexec\n0000\ncleartomark\n"; + let b = "/FontName /B def\ncurrentfile eexec\n0000\ncleartomark\n"; + let ps = format!("%!PS-Adobe-3.0 EPSF-3.0\n{a}{b}"); + let fonts = embedded_fonts(ps.as_bytes()); + let (data, _) = fonts.get("B").expect("second font found"); + assert!(!String::from_utf8_lossy(data).contains("AdobeFont"), "{}", String::from_utf8_lossy(data)); + } } diff --git a/crates/pdf/src/ps/text.rs b/crates/pdf/src/ps/text.rs index 8ddf6ce85..d4cee07f8 100644 --- a/crates/pdf/src/ps/text.rs +++ b/crates/pdf/src/ps/text.rs @@ -95,6 +95,8 @@ impl Interp { let ctm = self.gs.ctm; for (i, &code) in bytes.iter().enumerate() { let code = code as u32; + // Every input character costs at least one unit, so empty outlines are not free. + self.spend(1)?; if let Some(g) = font.glyph(code) { // Each outline element counts against the path limit and the budget before it // is copied, so a long string cannot expand into unbounded geometry.