mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-26 10:47:11 -06:00
Export: EPS, legacy .ai, EMF/WMF and DXF give one note for images that can't be decoded
EPS, EMF, WMF and DXF exports gave each image they left out because it couldn't be decoded its own warning, "image '<key>' could not be decoded and was left out", which names the internal key the document stores the image under. A version 7 .ai said "an image that can't be decoded or is too large was left out". The four writers now give the note raster exports, PDF and the legacy .ai flattening give, "an image couldn't be decoded and is missing from the picture", once per export. In a version 7 .ai it is one line with the note for flattened art. The other image notes in the same functions no longer name the key: EPS's note for an image too large to write, and DXF's notes for an image with no pixels (its rows in 23 catalogs are reworded) and one that could not be written. Legacy .ai gives a separate note for an image that is empty or too large. Docs: docs/mcp.md (EPS, DXF export, EMF and WMF, aiVersion "7").
This commit is contained in:
@@ -125,6 +125,24 @@ fn placed_images_come_back_as_linked_files() {
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
/// Two placed images whose pixel data can't be decoded are left out of the drawing and get no
|
||||
/// linked file, and `warnings` holds the note raster exports give, once, without naming either
|
||||
/// image's key.
|
||||
#[test]
|
||||
fn images_that_cant_be_decoded_give_the_raster_note_once() {
|
||||
const NOTE: &str = "an image couldn't be decoded and is missing from the picture";
|
||||
let mut s = session(200.0, 100.0, 1);
|
||||
crate::tests_menucmds::place_undecodable_png(&mut s);
|
||||
crate::tests_menucmds::place_undecodable_png_sized(&mut s, 32, 16);
|
||||
let keys: Vec<String> = s.doc().unwrap().doc.images.keys().cloned().collect();
|
||||
assert_eq!(keys.len(), 2);
|
||||
let r = s.execute("document.export", &json!({"format": "dxf"})).unwrap();
|
||||
let w: Vec<&str> = r["warnings"].as_array().unwrap().iter().filter_map(Value::as_str).collect();
|
||||
assert_eq!(w.iter().filter(|w| **w == NOTE).count(), 1, "{w:?}");
|
||||
assert!(!w.iter().any(|w| keys.iter().any(|k| w.contains(k.as_str()))), "{w:?}");
|
||||
assert!(entities(&text(&r)).is_empty() && r.get("linked").is_none(), "{r}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bad_dxf_options_are_refused() {
|
||||
let mut s = two_boards();
|
||||
|
||||
@@ -156,6 +156,23 @@ fn placed_images_are_written_and_previews_are_black_and_white_on_request() {
|
||||
assert!(l3.contains("/FlateDecode filter >> image"));
|
||||
}
|
||||
|
||||
/// Two placed images whose pixel data can't be decoded are left out of the EPS, and `warnings`
|
||||
/// holds the note raster exports give, once, without naming either image's key.
|
||||
#[test]
|
||||
fn images_that_cant_be_decoded_give_the_raster_note_once() {
|
||||
const NOTE: &str = "an image couldn't be decoded and is missing from the picture";
|
||||
let mut s = session(200.0, 100.0, 1);
|
||||
crate::tests_menucmds::place_undecodable_png(&mut s);
|
||||
crate::tests_menucmds::place_undecodable_png_sized(&mut s, 32, 16);
|
||||
let keys: Vec<String> = s.doc().unwrap().doc.images.keys().cloned().collect();
|
||||
assert_eq!(keys.len(), 2);
|
||||
let r = s.execute("document.exportEps", &json!({"previewFormat": "none"})).unwrap();
|
||||
let w = warnings(&r);
|
||||
assert_eq!(w.iter().filter(|w| *w == NOTE).count(), 1, "{w:?}");
|
||||
assert!(!w.iter().any(|w| keys.iter().any(|k| w.contains(k.as_str()))), "{w:?}");
|
||||
assert!(!postscript(&b64(&r)).contains("filter >> image"), "no image is written");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bad_eps_options_are_refused_and_formats_list_eps() {
|
||||
let mut s = session(100.0, 100.0, 1);
|
||||
|
||||
@@ -95,6 +95,24 @@ fn raster_effects_are_written_as_images() {
|
||||
assert!(kinds.contains(&114), "the shadow is an image with transparency");
|
||||
}
|
||||
|
||||
/// Two placed images whose pixel data can't be decoded are left out of an EMF or WMF, and
|
||||
/// `warnings` holds the note raster exports give, once, without naming either image's key.
|
||||
#[test]
|
||||
fn images_that_cant_be_decoded_give_the_raster_note_once() {
|
||||
const NOTE: &str = "an image couldn't be decoded and is missing from the picture";
|
||||
let mut s = session(200.0, 100.0, 1);
|
||||
crate::tests_menucmds::place_undecodable_png(&mut s);
|
||||
crate::tests_menucmds::place_undecodable_png_sized(&mut s, 32, 16);
|
||||
let keys: Vec<String> = s.doc().unwrap().doc.images.keys().cloned().collect();
|
||||
assert_eq!(keys.len(), 2);
|
||||
for format in ["emf", "wmf"] {
|
||||
let r = s.execute("document.export", &json!({"format": format})).unwrap();
|
||||
let w: Vec<&str> = r["warnings"].as_array().unwrap().iter().filter_map(Value::as_str).collect();
|
||||
assert_eq!(w.iter().filter(|w| **w == NOTE).count(), 1, "{format}: {w:?}");
|
||||
assert!(!w.iter().any(|w| keys.iter().any(|k| w.contains(k.as_str()))), "{format}: {w:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn metafiles_open_and_place() {
|
||||
let mut s = two_boards();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::tests_menucmds::place_undecodable_png;
|
||||
use super::tests_menucmds::{place_undecodable_png, place_undecodable_png_sized};
|
||||
use super::*;
|
||||
use crate::tests_links::Folder;
|
||||
|
||||
@@ -362,6 +362,29 @@ fn a_flattened_image_that_cant_be_decoded_is_noted_once() {
|
||||
assert_eq!(undecoded_notes(&r), 1, "{r}");
|
||||
}
|
||||
|
||||
/// Images that can't be decoded and are written as they are get the same note as one that is
|
||||
/// flattened. All of them give one note, and the note names no image's key.
|
||||
#[test]
|
||||
fn images_written_or_flattened_that_cant_be_decoded_are_noted_once() {
|
||||
let mut s = Session::new();
|
||||
s.execute("file.new", &json!({"width": 200, "height": 100})).unwrap();
|
||||
place_undecodable_png(&mut s);
|
||||
place_undecodable_png_sized(&mut s, 48, 24);
|
||||
let keys: Vec<String> = s.doc().unwrap().doc.images.keys().cloned().collect();
|
||||
assert_eq!(keys.len(), 2);
|
||||
let decode_notes = |r: &Value| warnings(r).iter().filter(|w| w.contains("decoded")).count();
|
||||
let named = |r: &Value| warnings(r).iter().any(|w| keys.iter().any(|k| w.contains(k.as_str())));
|
||||
let r = s.execute("file.saveCopy", &json!({"format": "ai", "aiVersion": "7"})).unwrap();
|
||||
assert_eq!((undecoded_notes(&r), decode_notes(&r), named(&r)), (1, 1, false), "{r}");
|
||||
// A third image, at 50% opacity, is flattened. The engine's note and the writer's note come out
|
||||
// as one warning.
|
||||
let cut = place_undecodable_png_sized(&mut s, 32, 16);
|
||||
s.execute("transparency.set", &json!({"ids": [cut.0], "opacity": 50})).unwrap();
|
||||
let r = s.execute("file.saveCopy", &json!({"format": "ai", "aiVersion": "7"})).unwrap();
|
||||
assert!(warnings(&r).iter().any(|w| w.contains("flattened with the art under them")), "{r}");
|
||||
assert_eq!((undecoded_notes(&r), decode_notes(&r), named(&r)), (1, 1, false), "{r}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_image_that_cant_be_decoded_with_an_expanded_effect_is_noted() {
|
||||
let mut s = Session::new();
|
||||
|
||||
@@ -41,8 +41,14 @@ fn sel(s: &mut Session, ids: &[NodeId]) {
|
||||
/// A placed 64 × 32 px PNG whose header reads (so it places at its size) but whose pixel data is
|
||||
/// cut short: the renderer can't decode it and draws nothing.
|
||||
pub(crate) fn place_undecodable_png(s: &mut Session) -> NodeId {
|
||||
place_undecodable_png_sized(s, 64, 32)
|
||||
}
|
||||
|
||||
/// Places a PNG like [`place_undecodable_png`] does, at `w` × `h` px. PNGs of different sizes are
|
||||
/// different images, each stored under its own key.
|
||||
pub(crate) fn place_undecodable_png_sized(s: &mut Session, w: u32, h: u32) -> NodeId {
|
||||
let mut png = Vec::new();
|
||||
image::RgbaImage::from_pixel(64, 32, image::Rgba([200, 30, 30, 255]))
|
||||
image::RgbaImage::from_pixel(w, h, image::Rgba([200, 30, 30, 255]))
|
||||
.write_to(&mut std::io::Cursor::new(&mut png), image::ImageFormat::Png)
|
||||
.unwrap();
|
||||
let idat = png.windows(4).position(|w| w == b"IDAT").unwrap();
|
||||
|
||||
Reference in New Issue
Block a user