Files
cadcraft/crates/engine/tests/image_decoder_panic.rs
T
9d4b3bb091 Raster images are drawn on screen and in PNG output (#1756)
An IMAGE whose file exists was drawn as a frame with two diagonals. Now
its pixels are loaded when a drawing opens and drawn on screen and in PNG
output, with rotation, clip boundary, fade and object transparency. A file
that can't be read keeps the crossed-out frame and reports a message.

Ported from the raster commit of #1029 onto the split render and canvas
modules. New here: large images are drawn from a pyramid of levels cut
into tiles uploaded as they come into view, instead of one texture with
pixels skipped, so a large scan stays sharp zoomed in and doesn't shimmer
zoomed out.

Limits are listed in docs/gaps.md (FF10): PDF and SVG output write only
the frame, and on screen images lie under all other objects.

Co-Authored-By: Chanthawat Kiriyadee <enwuft@icloud.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 17:05:02 +01:00

56 lines
2.4 KiB
Rust

//! A raster image decoder that panics on a damaged file fails that image only (#956): the
//! drawing it belongs to still opens, and IMAGEATTACH reports an error.
use cadcraft_engine::Session;
use cadcraft_engine::cmd::file::{IoHooks, base64_encode, set_image_decoder, set_io};
use cadcraft_engine::doc::geom::{Vec2, Vec3};
use cadcraft_engine::doc::{Common, Drawing, EntityKind, Image, Line, Space};
use serde_json::json;
fn hooks() {
set_io(IoHooks {
read: |b, name| cadcraft_io::read(b, name).map_err(|e| e.to_string()),
write: |d, name| cadcraft_io::write(d, name).map_err(|e| e.to_string()),
plot: None,
});
set_image_decoder(|_| panic!("decoder bug"));
}
#[test]
fn a_panicking_decoder_fails_the_image_not_the_drawing() {
hooks();
let dir = std::env::temp_dir().join(format!("cadcraft-image-panic-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
std::fs::write(dir.join("map.png"), b"anything").unwrap();
let mut d = Drawing::new_metric();
d.add(&Space::Model, Common::default(), EntityKind::Line(Line { a: Vec3::ZERO, b: Vec3::new(5.0, 5.0, 0.0) })).unwrap();
let i = Image {
u: Vec3::new(1.0, 0.0, 0.0),
v: Vec3::new(0.0, 1.0, 0.0),
size: Vec2::new(4.0, 2.0),
path: "map.png".into(),
name: "map".into(),
..Image::default()
};
d.add(&Space::Model, Common::default(), EntityKind::Image(i)).unwrap();
let host = dir.join("host.dxf");
std::fs::write(&host, cadcraft_io::write(&d, "host.dxf").unwrap()).unwrap();
let mut s = Session::new();
s.execute("open", &json!({ "path": host.to_string_lossy() })).unwrap();
let doc = s.doc().unwrap();
assert_eq!(doc.model.len(), 2, "the drawing opened with everything in it");
assert!(doc.rasters.is_empty());
assert!(s.log.iter().any(|l| l.contains("map.png") && l.contains("damaged")), "{:?}", s.log);
let before = s.doc().unwrap().model.len();
let e = s.execute("imageattach", &json!({ "path": "other.png", "data": base64_encode(b"bytes") })).unwrap_err().to_string();
assert!(e.contains("damaged"), "{e}");
assert_eq!(s.doc().unwrap().model.len(), before);
// The session still works.
s.execute("line", &json!({ "points": [[0, 0], [1, 1]] })).unwrap();
assert_eq!(s.doc().unwrap().model.len(), before + 1);
let _ = std::fs::remove_dir_all(&dir);
}