Merge pull request #286 from p0lyg0n/fix/ai-neighbouring-artboards

PDF/.ai import: art of a neighbouring artboard stays on its own page
This commit is contained in:
BFlat authored and GitHub committed 2026-10-07 13:31:29 +07:00
commit 89ff09e9fd
3 files changed
+24 -2

No files matched your search

+1 -1
View File
@@ -137,7 +137,7 @@ grade by behaviour against `plan/illustrator/`, not by whether a menu item exist
- **Fonts:** a font is found by any of its names (family and style in every language of its name table, legacy family/style pairs, PostScript name), so documents from other apps open with the font they name; Find Font and `text.fonts` say for each font whether it was found exactly, by another name, substituted or missing, and how many glyphs are missing.
- **Files (M4):**
- `.vectorcraft` (lossless JSON, compressed, atomic saves, save down to v1), Save As with a format chooser, Save a Copy, Revert, templates, Data Recovery after a crash, background save and export.
- SVG/SVGZ in and out with SVG Options, Preserve Editing, symbols, filters, rich text and physical units; PDF and PDF-compatible `.ai` in and out (layers, masks, editable text with type set along a curve coming back as type on a path, spot colours, presets, security, marks and bleed, ICC output intents, raster effects, subset fonts, PDF/X, PDF layers, overprint, page thumbnails, fast web view, PDF 1.3).
- SVG/SVGZ in and out with SVG Options, Preserve Editing, symbols, filters, rich text and physical units; PDF and PDF-compatible `.ai` in and out (layers, masks, editable text with type set along a curve coming back as type on a path, each artboard's art on its own page only, spot colours, presets, security, marks and bleed, ICC output intents, raster effects, subset fonts, PDF/X, PDF layers, overprint, page thumbnails, fast web view, PDF 1.3).
- EPS and DXF in and out, EMF/WMF in and out; PNG (with PNG-8), JPEG, WebP, GIF, TIFF, BMP, Targa and layered PSD export. EPS import runs Illustrator's AGM/CoolType prologs (resource categories, `resourceforall`, `clipsave`/`cliprestore`, subarrays and substrings that share storage) and falls back to its palette TIFF preview. DXF TEXT justified Fit or Aligned spans its two points (stretched, or scaled as a whole).
- Place and the Links panel, Package, File Info, Print with print presets, PostScript output and print tiling, slices and Save for Web, Export for Screens and Asset Export, CSS Properties, PNG/PDF/SVG/text clipboard flavours.
- **Performance:** 20k shapes + 1k texts render in 27 ms per full-retina frame (7.8 ms zoomed), 7× faster than the first version. The UI thread never blocks. The web build is 7.1 MB gzipped.
+10 -1
View File
@@ -111,7 +111,16 @@ pub fn import_with_report(bytes: &[u8], opts: &ImportOptions) -> Result<ImportRe
let scan = scan_page(page, &mut ocgs, all_on, &mut b.fonts);
b.begin_page(scan);
interpret_page(page, &mut ctx, &mut b);
let parts = b.end_page();
let mut parts = b.end_page();
// A file with several artboards writes, on each page, the art of its neighbours that
// reaches into the page's box: art lying wholly outside this page is theirs (each page
// draws it shifted by its own artboard spacing), so keep it only where it belongs.
if picked.len() > 1 {
for (_, art) in &mut parts {
art.retain(|n| n.visual_bounds().is_none_or(|r| r.x0 <= ab.x1 && r.x1 >= ab.x0 && r.y0 <= ab.y1 && r.y1 >= ab.y0));
}
parts.retain(|(_, art)| !art.is_empty());
}
let children: Vec<Arc<Node>> = parts.iter().flat_map(|(_, v)| v.iter().cloned()).collect();
placeholder &= crate::pages::has_private_data(page) && only_text(&children);
let mut right = ab.x1;
+13
View File
@@ -106,6 +106,19 @@ fn bounding_boxes_of_several_pages_do_not_overlap() {
assert!(b.x0 >= a.x1 + 36.0, "{a:?} {b:?}");
}
#[test]
fn art_of_neighbouring_artboards_off_a_page_is_left_to_its_own_page() {
// Page 2 also draws page 1's square, off to its left (outside its box), as files with several
// artboards are written: only page 1 keeps it, so it doesn't come back twice.
let pages = [PdfPage::new(100.0, 100.0, "0 g 10 10 20 20 re f"), PdfPage::new(100.0, 100.0, "0 g -90 10 20 20 re f 1 0 0 rg 50 10 20 20 re f")];
let d = open(&pdf(&pages, None), ImportOptions::default());
let count = |i: usize| d.layers[i].children().map_or(0, |c| c.len());
assert_eq!((count(0), count(1)), (1, 1));
// A single page keeps its art off the page (the pasteboard).
let d = open(&pdf(&pages[1..], None), ImportOptions::default());
assert_eq!(d.layers[0].children().map_or(0, |c| c.len()), 2);
}
#[test]
fn an_encrypted_pdf_opens_with_its_password_only() {
let bytes = pdf(&[PdfPage::new(200.0, 100.0, "1 0 0 rg 10 10 50 50 re f")], Some("secret"));