diff --git a/ROADMAP.md b/ROADMAP.md index d4ff8b6a..fc5ae239 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -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. diff --git a/crates/pdf/src/import.rs b/crates/pdf/src/import.rs index 05f6b8ea..cf464018 100644 --- a/crates/pdf/src/import.rs +++ b/crates/pdf/src/import.rs @@ -111,7 +111,16 @@ pub fn import_with_report(bytes: &[u8], opts: &ImportOptions) -> Result 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> = parts.iter().flat_map(|(_, v)| v.iter().cloned()).collect(); placeholder &= crate::pages::has_private_data(page) && only_text(&children); let mut right = ab.x1; diff --git a/crates/pdf/src/tests_import_options.rs b/crates/pdf/src/tests_import_options.rs index 84e6b396..37c7fecc 100644 --- a/crates/pdf/src/tests_import_options.rs +++ b/crates/pdf/src/tests_import_options.rs @@ -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"));