diff --git a/crates/engine/src/tests_legacy_ai.rs b/crates/engine/src/tests_legacy_ai.rs index fef2b44cf..e38e0f2fa 100644 --- a/crates/engine/src/tests_legacy_ai.rs +++ b/crates/engine/src/tests_legacy_ai.rs @@ -250,6 +250,16 @@ fn a_transparent_image_is_flattened_with_the_art_under_it_which_stays() { } assert_eq!(colour_at(d, 20.0, 30.0), [255, 0, 0], "the square outside the image"); assert_eq!(colour_at(d, 80.0, 30.0), [255, 255, 255], "nothing beyond the image"); + // Run as a PostScript program (the file's prolog draws it), it prints the same: two images + // and the square, on a page that is the art's box (10 pt in from the artboard's top left). + s.execute("document.open", &json!({"name": "t.ai", "dataBase64": r["dataBase64"], "editingData": false})).unwrap(); + let page = &s.doc().unwrap().doc; + assert_eq!(images(page).0, 2); + let printed = [colour_at(page, 30.0, 20.0), colour_at(page, 50.0, 20.0)]; + for (a, b) in before.iter().zip(&printed) { + assert!(a.iter().zip(b).all(|(a, b)| a.abs_diff(*b) <= 3), "{before:?} {printed:?}"); + } + assert_eq!(colour_at(page, 10.0, 20.0), [255, 0, 0], "the square outside the image"); } #[test] diff --git a/crates/eps/src/import/mod.rs b/crates/eps/src/import/mod.rs index 276cc9816..b59d2dda7 100644 --- a/crates/eps/src/import/mod.rs +++ b/crates/eps/src/import/mod.rs @@ -18,7 +18,9 @@ //! `%%Creator`) and these operators: Adobe Illustrator File Format Specification, version 7.0 //! (1998), listed by PRONOM at https://www.nationalarchives.gov.uk/PRONOM/fmt/423. A file in that //! format whose program has its layers (`%AI5_BeginLayer`) comes in through them, as `native` reads -//! an Illustrator EPS's editing copy (#1027). +//! an Illustrator EPS's editing copy (#1027). One VectorCraft wrote (its prolog the procedure set +//! [`crate::legacy_ai::PROCSET`], which prints its gradients flat and leaves its type out) comes in +//! from those layers alone, without its page. //! //! The page is the file's `%%BoundingBox` (`%%HiResBoundingBox` when it has one), the first page //! of a PostScript file without one. A file whose program can't be read (an operator the @@ -92,6 +94,9 @@ struct Dsc { /// In the legacy Illustrator format: its header has `%AI…` comments or names the app as the /// creator (see the module docs for the specification). illustrator: bool, + /// Its prolog is the one VectorCraft's legacy `.ai` writer includes + /// ([`crate::legacy_ai::PROCSET`] in `%%DocumentSuppliedResources`). + own_legacy: bool, } /// The four numbers of a bounding box comment's value, when it has them and they make a box. @@ -126,6 +131,9 @@ impl Dsc { if header && (line.starts_with("%AI") || line.starts_with("%%AI") || creator) { d.illustrator = true; } + if header && value("%%DocumentSuppliedResources:").is_some_and(|v| v.strip_prefix("procset ") == Some(crate::legacy_ai::PROCSET)) { + d.own_legacy = true; + } if let Some(v) = value("%%HiResBoundingBox:") { if header || atend { hires = box_of(v).or(hires); @@ -185,6 +193,15 @@ pub fn import_with(bytes: &[u8], editing_data: bool) -> Result return Err("this is not a PostScript file".into()); } let dsc = Dsc::read(ps); + // A legacy `.ai` VectorCraft wrote: its prolog prints the art plainly (gradients flat, no + // type), and its program has all of it, which is read as the format (as before the file + // printed). + if editing_data + && dsc.own_legacy + && let Some(Ok((document, warnings))) = native::program_alone(ps) + { + return Ok(Imported { document, warnings, preview: false }); + } let [llx, lly, urx, ury] = dsc.bbox.unwrap_or(LETTER); let frame = Rect::new(0.0, 0.0, urx - llx, ury - lly); let mut doc = Document::new(frame.width(), frame.height()); diff --git a/crates/eps/src/legacy_ai.rs b/crates/eps/src/legacy_ai.rs index 4b774c2f7..96a9ff259 100644 --- a/crates/eps/src/legacy_ai.rs +++ b/crates/eps/src/legacy_ai.rs @@ -51,10 +51,9 @@ //! Windows-1252, vertical type, inline graphics, gradient or pattern fills) is written as its //! outlines, and all type with [`LegacyAiOptions::outline_text`]. //! -//! The prolog defines the one procedure the script calls that isn't an operator of the format, -//! the page trailer's `annotatepage` ([`PROLOG`]); the format's operators are left to the app that -//! reads the file, so PostScript printers can't print it, as the `.ai` files other apps write in -//! this format. +//! The prolog is a procedure set of the writer's own ([`PROLOG`]) that gives the operators their +//! meaning in PostScript, so the file also prints and previews as a PostScript program (its +//! gradients flat, without its type, which apps that read the format draw). use std::fmt::Write; use std::sync::Arc; @@ -198,7 +197,7 @@ pub fn export_legacy_ai(doc: &Document, o: &LegacyAiOptions) -> Result