.ai import: the editing data reader warns about each image it can't read

An image in a .ai's editing data whose operands had no matrix, fewer
than 11 numbers, or a matrix of other than six numbers was left out
with no warning. So was one whose matrix maps it onto a line or a
point. The reader's warnings did not mention such an image, while the
reader warned "an image VectorCraft can't read was left out" for an
image whose size or bit depth it can't use.

Each of those exits now adds the same warning, as does the one where
the decoded pixels don't make an image.
This commit is contained in:
dotCooCoo
2026-10-19 00:03:51 +07:00
committed by bflatastic
parent f0c2d8c890
commit ba32ef5b09
2 changed files with 51 additions and 4 deletions
+17 -4
View File
@@ -2012,9 +2012,15 @@ impl<'a> Reader<'a> {
/// An image of operands `vals` and samples `data`.
fn image_from(&mut self, vals: Vec<V>, 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));
+34
View File
@@ -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