diff --git a/crates/eps/src/import/ai/read.rs b/crates/eps/src/import/ai/read.rs index 161407d03..432a3dd23 100644 --- a/crates/eps/src/import/ai/read.rs +++ b/crates/eps/src/import/ai/read.rs @@ -2012,9 +2012,15 @@ impl<'a> Reader<'a> { /// An image of operands `vals` and samples `data`. fn image_from(&mut self, vals: Vec, data: &[u8]) -> Result<(), String> { let space = self.raster.as_ref().and_then(|r| r.space.clone()).unwrap_or_default(); - let Some(m) = vals.iter().find_map(|v| if let V::Arr(a) = v { Some(Self::nums(a)) } else { None }) else { return Ok(()) }; + let Some(m) = vals.iter().find_map(|v| if let V::Arr(a) = v { Some(Self::nums(a)) } else { None }) else { + self.warn("an image VectorCraft can't read was left out"); + return Ok(()); + }; let n = Self::nums(&vals); - let [_, _, _, _, w, h, bits, kind, alpha, _, binary, ..] = n.as_slice() else { return Ok(()) }; + let [_, _, _, _, w, h, bits, kind, alpha, _, binary, ..] = n.as_slice() else { + self.warn("an image VectorCraft can't read was left out"); + return Ok(()); + }; let (w, h) = (*w as u64, *h as u64); if w == 0 || h == 0 || w.saturating_mul(h) > MAX_PIXELS || ![1.0, 8.0].contains(bits) { self.warn("an image VectorCraft can't read was left out"); @@ -2080,7 +2086,10 @@ impl<'a> Reader<'a> { rgba.extend_from_slice(&[px[0], px[1], px[2], a]); } } - let Some(img) = image::RgbaImage::from_raw(w as u32, h as u32, rgba) else { return Ok(()) }; + let Some(img) = image::RgbaImage::from_raw(w as u32, h as u32, rgba) else { + self.warn("an image VectorCraft can't read was left out"); + return Ok(()); + }; let mut png = vec![]; if img.write_to(&mut std::io::Cursor::new(&mut png), image::ImageFormat::Png).is_err() { self.warn("an image VectorCraft can't read was left out"); @@ -2096,10 +2105,14 @@ impl<'a> Reader<'a> { // The matrix is an art-space matrix (y up) applied to the pixels with their rows running // downwards, (u, -v): pixels → (u, -v) → art space. Flipping the matrix's result instead // gives the same place only when the image isn't rotated (b = c = 0). - let [a, b, c, d, tx, ty] = m else { return Ok(()) }; + let [a, b, c, d, tx, ty] = m else { + self.warn("an image VectorCraft can't read was left out"); + return Ok(()); + }; let to_doc = self.space(); let xf = to_doc * Affine::new([*a, *b, *c, *d, *tx, *ty]) * FLIP; if !xf.as_coeffs().iter().all(|v| v.is_finite()) || xf.determinant().abs() < 1e-12 { + self.warn("an image VectorCraft can't read was left out"); return Ok(()); } let mask = alpha.and_then(|a| self.alpha_mask(a, w, h, xf)); diff --git a/crates/eps/src/import/ai/tests.rs b/crates/eps/src/import/ai/tests.rs index 72d625077..cb700fbfc 100644 --- a/crates/eps/src/import/ai/tests.rs +++ b/crates/eps/src/import/ai/tests.rs @@ -677,6 +677,40 @@ fn images_with_alpha_channels() { assert!(art(&r.doc).is_empty() && r.warnings.iter().any(|w| w.contains("cut short"))); } +/// Editing data `data` opens without its one image and with the warning "an image VectorCraft can't read was left out" once. +fn image_left_out_with_a_warning(data: &[u8]) { + let r = read(data).unwrap(); + assert!(art(&r.doc).is_empty(), "{:#?}", art(&r.doc)); + let left_out = r.warnings.iter().filter(|w| *w == "an image VectorCraft can't read was left out").count(); + assert_eq!(left_out, 1, "{:?}", r.warnings); +} + +#[test] +fn an_image_without_a_matrix_is_left_out_with_a_warning() { + image_left_out_with_a_warning(&image("", (1, 1), ("DeviceRGB", 3, 0), &[255, 0, 0], 3)); +} + +#[test] +fn an_image_with_too_few_operands_is_left_out_with_a_warning() { + // The operands hold 10 numbers after the matrix, one short of the binary flag. + let text = String::from_utf8(image("[ 1 0 0 1 0 100 ]", (1, 1), ("DeviceRGB", 3, 0), b"abc", 3)).unwrap(); + let data = text.replacen("] 0 0 1 1 1 1 8 3 0 0 1 0 4 4 0 0\n", "] 0 0 1 1 1 1 8 3 0 0\n", 1); + assert_ne!(data, text); + image_left_out_with_a_warning(data.as_bytes()); +} + +#[test] +fn an_image_whose_matrix_isnt_six_numbers_is_left_out_with_a_warning() { + image_left_out_with_a_warning(&image("[ 1 0 0 1 ]", (1, 1), ("DeviceRGB", 3, 0), &[255, 0, 0], 3)); +} + +#[test] +fn an_image_whose_matrix_has_no_area_is_left_out_with_a_warning() { + image_left_out_with_a_warning(&image("[ 0 0 0 0 0 0 ]", (1, 1), ("DeviceRGB", 3, 0), &[255, 0, 0], 3)); + // This matrix maps the image onto a line. + image_left_out_with_a_warning(&image("[ 1 2 2 4 0 100 ]", (1, 1), ("DeviceRGB", 3, 0), &[255, 0, 0], 3)); +} + /// The raster matrix `[a b c d tx ty]` is an art-space matrix (y up) applied to the image's pixels /// with their rows running downwards, `(u, -v)`: art x = a·u − c·v + tx, art y = b·u − d·v + ty. /// Without rotation (b = c = 0) that is the same as flipping the matrix's result; a rotated image