diff --git a/crates/engine/src/cmd/fileio/legacy_ai.rs b/crates/engine/src/cmd/fileio/legacy_ai.rs index 5d1df1493..8dbd5ffde 100644 --- a/crates/engine/src/cmd/fileio/legacy_ai.rs +++ b/crates/engine/src/cmd/fileio/legacy_ai.rs @@ -15,7 +15,9 @@ //! blending mode written opaque is a box hiding the art under it (version 7 images have no alpha //! channel, ยง8.1). With `flatten` (the default) each becomes an opaque image of itself and the art //! under it, rendered from the document as it is, clipped to the area where it changes what shows; -//! the art under it stays as it was. With `omit` they are left out. +//! the art under it stays as it was. With `omit` they are left out. An image that can't be decoded +//! is missing from the images that flattening and expanding raster effects draw, and the warnings +//! then carry the raster exports' note once. //! //! The format can't hide objects, only layers (`hiddenObjects`): with `layer` (the default) each //! layer's hidden objects go to a hidden layer of their own just above it, with `omit` they are @@ -104,6 +106,7 @@ pub(super) fn encode(cmd: &str, f: &Format, doc: &Document, p: &Value) -> Result enc.warnings.push(SEPARATE.to_string()); } let created = vectorcraft_doc::metadata::now_unix(); + let failed = vectorcraft_render::decode_failures(); for b in boards { let one = super::save::artboard_doc(doc, b); let (full, placed) = crate::cmd::place::document::full_documents(&one); @@ -127,6 +130,11 @@ pub(super) fn encode(cmd: &str, f: &Format, doc: &Document, p: &Value) -> Result } enc.files.push((Some(b), out.bytes)); } + // Expanding raster effects and flattening images draw the art, and an image the renderer + // couldn't decode drew nothing there. + if let Some(w) = super::super::rasterfx::undecoded_warning(failed) { + note(&mut enc.warnings, w); + } Ok(enc) } diff --git a/crates/engine/src/tests_legacy_ai.rs b/crates/engine/src/tests_legacy_ai.rs index e38e0f2fa..aa6f82f08 100644 --- a/crates/engine/src/tests_legacy_ai.rs +++ b/crates/engine/src/tests_legacy_ai.rs @@ -3,6 +3,7 @@ use serde_json::{Value, json}; +use super::tests_menucmds::place_undecodable_png; use super::*; use crate::tests_links::Folder; @@ -262,6 +263,47 @@ fn a_transparent_image_is_flattened_with_the_art_under_it_which_stays() { assert_eq!(colour_at(page, 10.0, 20.0), [255, 0, 0], "the square outside the image"); } +/// How many of reply `r`'s warnings say that an image couldn't be decoded, as raster exports do. +fn undecoded_notes(r: &Value) -> usize { + warnings(r).iter().filter(|w| *w == "an image couldn't be decoded and is missing from the picture").count() +} + +#[test] +fn a_flattened_image_that_cant_be_decoded_is_noted_once() { + let mut s = Session::new(); + s.execute("file.new", &json!({"width": 200, "height": 100})).unwrap(); + s.execute("artboard.new", &json!({"x": 0, "y": 0, "width": 200, "height": 100, "name": "B"})).unwrap(); + let cut = place_undecodable_png(&mut s); + s.execute("transparency.set", &json!({"ids": [cut.0], "opacity": 50})).unwrap(); + // Both artboards flatten it. + let r = s.execute("file.saveCopy", &json!({"format": "ai", "aiVersion": "7"})).unwrap(); + assert_eq!(r["files"].as_array().map(Vec::len), Some(2), "{r}"); + assert!(warnings(&r).iter().any(|w| w.contains("flattened with the art under them")), "{r}"); + assert_eq!(undecoded_notes(&r), 1, "{r}"); +} + +#[test] +fn an_image_that_cant_be_decoded_with_an_expanded_effect_is_noted() { + let mut s = Session::new(); + s.execute("file.new", &json!({"width": 200, "height": 100})).unwrap(); + let cut = place_undecodable_png(&mut s); + s.execute("effect.apply", &json!({"effect": "blur.gaussian", "params": {"radius": 3}, "ids": [cut.0]})).unwrap(); + // Left out rather than flattened: only expanding the blur draws it. + let r = s.execute("file.saveCopy", &json!({"format": "ai", "aiVersion": "7", "transparentImages": "omit"})).unwrap(); + assert_eq!(undecoded_notes(&r), 1, "{r}"); +} + +#[test] +fn images_that_decode_give_no_decode_note() { + let mut s = shadow_over_square(); + s.execute("effect.apply", &json!({"effect": "blur.gaussian", "params": {"radius": 3}})).unwrap(); // the blue image + for mode in ["flatten", "omit"] { + let r = s.execute("file.saveCopy", &json!({"format": "ai", "aiVersion": "7", "transparentImages": mode})).unwrap(); + assert!(warnings(&r).iter().any(|w| w.contains("transparent images")), "{r}"); + assert_eq!(undecoded_notes(&r), 0, "{r}"); + } +} + #[test] fn transparent_images_can_be_left_out_and_opacity_makes_one_transparent() { let mut s = shadow_over_square(); diff --git a/docs/mcp.md b/docs/mcp.md index fa29dba2b..e7342acab 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -690,7 +690,9 @@ PostScript form, which keeps layers, groups, compound paths and clipping groups of `range` (default all; `{name}-{artboard}.ai` when several, listed in the result's `files`), with `transparentImages: "flatten"` (default) or `"omit"` for images with see-through pixels, opacity or a blending mode, `hiddenObjects: "layer"` (default: a hidden layer of each layer's hidden objects), `"omit"` or `"visible"`, and -`outlineText: true` to write type as outlines instead of text objects. From the CLI: `vectorcraft-cli convert in.svg +`outlineText: true` to write type as outlines instead of text objects. When an image in art that is flattened or whose +raster effects are expanded can't be decoded, the result does not show it, and `warnings` carries "an image couldn't +be decoded and is missing from the picture" once. From the CLI: `vectorcraft-cli convert in.svg out.ai --ai-version 7 [--hidden-objects layer|omit|visible] [--transparent-images flatten|omit] [--outline-text] [--range R]`.