Merge pull request #4658 from mockydevs/fix/ai-page-cropped-to-art

.ai import: a PDF part cropped to the art no longer hides the layers
This commit is contained in:
BFlat
2026-10-30 08:42:10 +07:00
committed by GitHub
5 changed files with 55 additions and 8 deletions
@@ -535,6 +535,27 @@ fn an_ai_file_whose_pdf_part_is_only_text_still_opens_from_editing_data_that_rea
check_sample(&s);
}
#[test]
fn an_ai_file_whose_pdf_part_is_cropped_to_its_art_opens_from_its_layers() {
// A 400 × 200 pt artboard with two red bars across (100, 50)–(300, 150). The PDF part's page
// is that art's box, 200 × 100 pt (the editing data's bounding box), not the artboard.
let red = "0 1 1 0 1 0 0 Xa\n";
let art = ai::layer("Art", &format!("{red}{}f\n{red}{}f\n", ai::rect(100.0, 50.0, 180.0, 150.0), ai::rect(220.0, 50.0, 300.0, 150.0)));
let data = ai::editing_data_in([100.0, 50.0, 300.0, 150.0], [0.0, 0.0, 400.0, 200.0], &art);
let page = "1 0 0 rg 0 0 80 100 re f 120 0 80 100 re f";
let mut s = Session::new();
let r = open(&mut s, "cropped.ai", &ai::ai(&compressed(data.as_bytes()), page), json!({})).unwrap();
assert!(!r["warnings"].to_string().contains("weren't read"), "{r}");
let doc = &s.doc().unwrap().doc;
assert_eq!(names(&doc.layers), ["Art"]);
assert_eq!(doc.artboards.len(), 1);
assert_eq!(doc.artboards[0].rect, vectorcraft_geom::Rect::new(0.0, 0.0, 400.0, 200.0), "the file's artboard, not its page");
let bars = doc.layers[0].children().unwrap();
assert_eq!(bars.len(), 2);
let b = bars[0].geometric_bounds().unwrap();
assert!((b.x0 - 100.0).abs() < 0.01 && (b.y0 - 50.0).abs() < 0.01, "where the artboard has it: {b:?}");
}
#[test]
fn editing_data_false_opens_only_what_the_page_prints() {
let mut s = Session::new();
+12 -2
View File
@@ -79,8 +79,18 @@ pub(super) fn build_from(
let page_rect = visible.artboards.first().map(|a| a.rect).ok_or("it has no page")?;
// The layers' art in the document's terms: a Large Canvas editing copy is written at 1/N
// scale, which `to_doc` carries, so its art and the page reach the same size (#2031).
let rect = to_doc.transform_rect_bbox(art_box);
if (rect.width() - page_rect.width()).abs() > 0.5 || (rect.height() - page_rect.height()).abs() > 0.5 {
let fits = |r: &Rect| (r.width() - page_rect.width()).abs() <= 0.5 && (r.height() - page_rect.height()).abs() <= 0.5;
let mut rect = to_doc.transform_rect_bbox(art_box);
// A `.ai` whose PDF part's pages are cropped to its art (each page the box of all of it,
// the editing data's bounding box) rather than to its artboards: the layers are compared
// with the page on that box, and keep their artboards.
if matches!(page, Page::Artboard)
&& !fits(&rect)
&& let Some(art) = bbox.map(|[a, b, c, d]| to_doc.transform_rect_bbox(Rect::new(a, b, c, d))).filter(fits)
{
rect = art;
}
if !fits(&rect) {
// Both sizes in points as the fields show them, and the editing data's page to the
// file's as a scale (1:10 is a tenth of its size) when its sides are in proportion and
// the scale doesn't round to 1:1.
+9 -2
View File
@@ -381,11 +381,18 @@ fn an_ai_file_that_cant_be_read_through_its_editing_data_keeps_its_pdf_part() {
let (d, w, read) = crate::layered_ai(b"nothing", pdf.clone(), vec![], false, false, 1.0);
assert_eq!(d.layers.len(), pdf.layers.len());
assert!(w.is_empty() && !read);
// Editing data that isn't for this page.
let other = native(&layer("Jig", true, &square(10, 10))).replace("%AI3_Cropmarks: 0 0 100 100", "%AI3_Cropmarks: 0 0 50 50");
// Editing data that isn't for this page: neither its artboard nor its art's box is the page.
let other = native(&layer("Jig", true, &square(10, 10))).replace(" 0 0 100 100", " 0 0 50 50");
let (d, w, _) = crate::layered_ai(&private(&other), pdf.clone(), vec![], false, false, 1.0);
assert_eq!(names(&d), names(&pdf));
assert!(w.iter().any(|w| w.contains("for a 50 × 50 pt page, the file's 100 × 100 pt page at 1:2")), "{w:?}");
// A page cropped to the art's box, not the artboard, is that art's page: the layers are read,
// with their artboard.
let cropped = native(&layer("Jig", true, &square(10, 10))).replace("%AI3_Cropmarks: 0 0 100 100", "%AI3_Cropmarks: 0 0 50 50");
let (d, w, read) = crate::layered_ai(&private(&cropped), pdf.clone(), vec![], false, false, 1.0);
assert!(read, "{w:?}");
assert_eq!(names(&d), ["Jig"]);
assert_eq!(d.artboards[0].rect.size(), vectorcraft_geom::Size::new(50.0, 50.0));
}
/// #2034: editing data for a page of another size gives both sizes in points, in an EPS's note and
+10 -2
View File
@@ -5,9 +5,17 @@
/// Editing data of a `width` × `height` page: header comments (the art's bounding box and the crop
/// marks are the page), `body` (setup and layers), the trailer.
pub fn editing_data(width: f64, height: f64, body: &str) -> String {
editing_data_in([0.0, 0.0, width, height], [0.0, 0.0, width, height], body)
}
/// [`editing_data`] whose art's bounding box (`x0 y0 x1 y1`, art space) is `art` and whose
/// artboard (the crop marks) is `board`.
pub fn editing_data_in(art: [f64; 4], board: [f64; 4], body: &str) -> String {
let [a0, a1, a2, a3] = art;
let [b0, b1, b2, b3] = board;
format!(
"{PS_HEADER}\n%%Creator: VectorCraft tests\n%%BoundingBox: 0 0 {width} {height}\n%%HiResBoundingBox: 0 0 {width} {height}\n\
%AI5_FileFormat 14.0\n%AI3_Cropmarks: 0 0 {width} {height}\n%%EndComments\n\
"{PS_HEADER}\n%%Creator: VectorCraft tests\n%%BoundingBox: {a0} {a1} {a2} {a3}\n%%HiResBoundingBox: {a0} {a1} {a2} {a3}\n\
%AI5_FileFormat 14.0\n%AI3_Cropmarks: {b0} {b1} {b2} {b3}\n%%EndComments\n\
%%BeginProlog\n%%EndProlog\n%%BeginSetup\n%%EndSetup\n{body}\n%%PageTrailer\ngsave annotatepage grestore showpage\n%%Trailer\n%%EOF\n"
)
}
File diff suppressed because one or more lines are too long