From c2339fca817dc7bed45b785594ff8609256dcd73 Mon Sep 17 00:00:00 2001 From: fastmikey99 <218057203+fastmikey99@users.noreply.github.com> Date: Fri, 9 Oct 2026 17:48:03 +1300 Subject: [PATCH] PDF import: leave art past the page unclipped in a PDF-compatible .ai The PDF part of an .ai stands in for the editable document, whose art crossing the artboard edge isn't clipped, so it keeps opening whole and editable (#646). Plain PDFs still get the clip to the crop box. path_count is back to counting every path: no clipping paths appear in the .ai cases it counts. Co-Authored-By: Claude Opus 5.5 --- crates/pdf/src/import.rs | 6 ++++-- crates/pdf/src/tests_import_layers.rs | 22 ++++++++++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/crates/pdf/src/import.rs b/crates/pdf/src/import.rs index a570db28..c4db8f81 100644 --- a/crates/pdf/src/import.rs +++ b/crates/pdf/src/import.rs @@ -235,11 +235,13 @@ pub fn import_with_report(bytes: &[u8], opts: &ImportOptions) -> Result usize { let mut n = 0; - d.walk(|c| n += usize::from(matches!(c.kind, NodeKind::Path { clipping: false, .. }))); + d.walk(|c| n += usize::from(matches!(c.kind, NodeKind::Path { .. }))); n } @@ -368,6 +367,25 @@ fn art_past_the_scan_limit_of_shown_layers_stays_shown() { assert!(r.warnings.iter().any(|w| w.contains("went to its page's layer")), "{:?}", r.warnings); } +/// Art reaching past the page keeps a clip to it in a plain PDF (as viewers show it), but not in +/// a PDF-compatible `.ai`, whose art crossing the artboard edge opens whole and editable (#646). +#[test] +fn art_past_the_page_is_clipped_in_a_pdf_but_not_in_an_ai_file() { + for ai in [false, true] { + let page = PdfPage { + entries: if ai { "/PieceInfo << /Illustrator << /Private << /AIPrivateData1 7 /NumBlock 1 >> >> >>".into() } else { String::new() }, + ..PdfPage::new(100.0, 100.0, "0 0 100 100 re W n 1 0 0 rg 50 50 200 200 re f") + }; + let r = import_with_report(&pdf_with_catalog(&[page], &[], "", None), &ImportOptions::default()).unwrap(); + let mut clips = 0; + r.document.walk(|n| clips += usize::from(matches!(n.kind, NodeKind::Group { clip: true, .. }))); + let past = r.warnings.iter().any(|w| w == crate::import::PAST_PAGE_NOTE); + assert_eq!((clips, past), if ai { (0, false) } else { (1, true) }, "ai: {ai}: {:?}", r.warnings); + let art = r.document.art_bounds().unwrap(); + assert_eq!(art.x1 > 100.5, ai, "ai: {ai}: {art:?}"); + } +} + /// The editor's private data of a `.ai` is its `AIPrivateData` streams, joined in order. #[test] fn an_ai_files_private_data_is_its_streams_joined() {