mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-14 19:26:56 -04:00
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 <[email protected]>
This commit is contained in:
1 parent
1d0305fc2a
commit
c2339fca81
2 files changed
+24
-4
No files matched your search
@@ -235,11 +235,13 @@ pub fn import_with_report(bytes: &[u8], opts: &ImportOptions) -> Result<ImportRe
|
||||
// dropped as redundant (by hayro and by `push_clip_path`), so restore the box here:
|
||||
// each object reaching past it keeps its own clip, which Fit to Artwork Bounds and
|
||||
// `CropTo::Bounding` then respect.
|
||||
// The crop box is what viewers show, whichever box the artboard takes.
|
||||
// The crop box is what viewers show, whichever box the artboard takes. Not in a
|
||||
// PDF-compatible `.ai`: its PDF part stands in for the editable document, whose art
|
||||
// crossing the artboard edge isn't clipped, so it opens whole (#646).
|
||||
let shown = xf.transform_rect_bbox(crate::pages::page_box(page, CropTo::Crop));
|
||||
let page_box = shown.inflate(0.01, 0.01);
|
||||
let mut clipped = false;
|
||||
for (_, art) in &mut parts {
|
||||
for (_, art) in parts.iter_mut().filter(|_| !ai) {
|
||||
for n in art.iter_mut() {
|
||||
if n.visual_bounds().is_some_and(|r| !contains(page_box, r)) {
|
||||
let clip = Arc::new(b.clip_node(&shown.to_path(0.1), FillRule::NonZero));
|
||||
|
||||
@@ -165,10 +165,9 @@ fn an_ai_file_notes_that_its_art_off_the_artboards_is_not_read() {
|
||||
}
|
||||
}
|
||||
|
||||
/// Paths that are art (not clipping paths: art reaching past the page keeps a clip to it).
|
||||
fn path_count(d: &Document) -> 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() {
|
||||
|
||||
Reference in new issue
Block a user