diff --git a/crates/engine/src/cmd/object.rs b/crates/engine/src/cmd/object.rs index aac0d0eb1..d4584b6c4 100644 --- a/crates/engine/src/cmd/object.rs +++ b/crates/engine/src/cmd/object.rs @@ -2836,7 +2836,12 @@ fn clipping_path(s: &mut Session, p: &Value) -> Result { let Some(px) = designcraft_render::decode_pixmap_page(&asset.data, asset.page) else { continue }; // A placed PDF cropped to a box: trace that box, which graphic space spans. let px = match designcraft_render::shown_box(asset) { - Some(frac) => designcraft_render::crop_pixmap(&px, frac).unwrap_or(px), + // A crop too big to trace is skipped, as a graphic that fails to decode is: tracing + // the whole page instead would trace something else than the box. + Some(frac) => match designcraft_render::crop_pixmap(&px, frac) { + Some(cropped) => cropped, + None => continue, + }, None => px, }; let (pw, ph) = (px.width() as usize, px.height() as usize); @@ -4637,6 +4642,40 @@ mod clipping_tests { assert!(s.execute("object.clippingPath", &json!({"ids": [f], "threshold": 255})).is_err()); } + /// A placed PDF whose crop box is too large to trace is skipped, not traced from the whole page. + #[test] + fn clipping_path_skips_a_pdf_whose_crop_is_too_large_to_trace() { + let content = "0 0 0 rg 10 10 50 50 re f"; + let objs = [ + "<>".to_string(), + "<>".into(), + "<>".into(), + format!("<>\nstream\n{content}\nendstream", content.len()), + ]; + let mut bytes = b"%PDF-1.7\n".to_vec(); + let mut offsets = Vec::new(); + for (i, body) in objs.iter().enumerate() { + offsets.push(bytes.len()); + bytes.extend_from_slice(format!("{} 0 obj\n{body}\nendobj\n", i + 1).as_bytes()); + } + let xref = bytes.len(); + bytes.extend_from_slice(b"xref\n0 5\n0000000000 65535 f\r\n"); + for at in offsets { + bytes.extend_from_slice(format!("{at:010} 00000 n\r\n").as_bytes()); + } + bytes.extend_from_slice(format!("trailer\n<>\nstartxref\n{xref}\n%%EOF\n").as_bytes()); + let mut s = Session::new(); + s.execute("file.new", &json!({})).unwrap(); + let f = s.execute("frame.create", &json!({"rect": [100, 100, 200, 200]})).unwrap()["id"].as_u64().unwrap(); + let placed = s + .execute("file.place", &json!({"base64": super::super::file::base64_encode(&bytes), "name": "wide.pdf", "frame": f, "pdfCrop": "media"})); + assert!(placed.is_ok(), "{placed:?}"); + let before = s.doc().unwrap().doc.item(ItemId(f)).unwrap().shape; + let e = s.execute("object.clippingPath", &json!({"ids": [f], "type": "alpha"})); + assert!(e.is_err(), "the graphic is skipped, so there is nothing to trace: {e:?}"); + assert_eq!(s.doc().unwrap().doc.item(ItemId(f)).unwrap().shape, before, "the frame keeps its shape"); + } + #[test] fn clipping_path_options_report_invert_and_remove_the_clip() { let mut s = Session::new();