Merge pull request #6579 from dotCooCoo/fix/legacy-ai-flatten-side-limit

Legacy .ai export: a long transparent image is flattened whole
This commit is contained in:
BFlat
2026-11-11 19:15:04 +07:00
committed by GitHub
2 changed files with 28 additions and 1 deletions
+3 -1
View File
@@ -270,7 +270,9 @@ fn flattened(src: &Document, at: usize, id: NodeId) -> Result<Option<Flat>> {
let pt = im.xf.determinant().abs().sqrt();
let ppi = if pt.is_finite() && pt > 0.0 { (72.0 / pt).clamp(MIN_PPI, MAX_PPI) } else { MIN_PPI };
let area = (bounds.width() * bounds.height()).max(1.0);
let scale = (ppi / 72.0).min((super::super::menucmds::MAX_PIXELS / area).sqrt());
// No side longer than the renderer draws (rounded up to whole pixels below).
let longest = (f64::from(vectorcraft_render::MAX_RASTER_SIDE) - 2.0) / bounds.width().max(bounds.height()).max(1.0);
let scale = (ppi / 72.0).min((super::super::menucmds::MAX_PIXELS / area).sqrt()).min(longest);
if !(scale.is_finite() && scale > 0.0) {
return Ok(None);
}
+25
View File
@@ -368,6 +368,31 @@ 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");
}
/// A transparent image longer than the renderer draws at its own resolution (32,768 pixels a
/// side) is flattened whole, at a lower resolution.
#[test]
fn a_long_transparent_image_is_flattened_whole() {
let mut s = Session::new();
s.execute("file.new", &json!({"width": 100, "height": 4600})).unwrap();
s.execute("shape.rectangle", &json!({"x": 0, "y": 0, "width": 100, "height": 4600})).unwrap();
s.execute("paint.setFill", &json!({"color": "#ff0000"})).unwrap();
s.execute("paint.setStroke", &json!({"none": true})).unwrap();
let p = json!({"name": "i.png", "dataBase64": vectorcraft_format::base64_encode(&png(2000, 1600, [0, 0, 0, 128])), "at": [50, 50]});
s.execute("file.place", &p).unwrap();
// 10 × 4500 pt: about 600 ppi, so 37,500 pixels long.
s.execute("object.setBounds", &json!({"x": 10, "y": 10, "width": 10, "height": 4500, "reference": 0})).unwrap();
let before = [colour_at(&s.doc().unwrap().doc, 15.0, 100.0), colour_at(&s.doc().unwrap().doc, 15.0, 4400.0)];
assert!(before.iter().all(|c| c[0] < 200), "the image darkens the red: {before:?}");
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}");
s.execute("document.open", &json!({"name": "t.ai", "dataBase64": r["dataBase64"]})).unwrap();
let d = &s.doc().unwrap().doc;
let after = [colour_at(d, 15.0, 100.0), colour_at(d, 15.0, 4400.0)];
for (a, b) in before.iter().zip(&after) {
assert!(a.iter().zip(b).all(|(a, b)| a.abs_diff(*b) <= 3), "{before:?} {after:?}");
}
}
/// A `size`×`size` PNG of a black disk of radius `r` pixels with an anti-aliased edge, clear
/// around it, 72 ppi.
fn disk(size: u32, r: f64) -> Vec<u8> {