Files
vectorcraft/crates/ui-egui/src/dialogs/tests_psd.rs
bflatasticandClaude Opus 5.5 c06c13f577 M4.70: PSD export, flat or with layers, groups for sublayers and text layers named after their text
document.export {format: "psd"} writes layered bitmaps (8 bits per channel),
written from the format's public specification: RGB, CMYK (ink amounts) or
grayscale, the resolution and an embedded profile, PackBits channels, and the
merged image (the flat render). layers (default true) draws each top-level
layer alone as a pixel layer, cropped to what it paints, with its name,
opacity and blend mode; a background colour becomes a Background layer.
maxEditability turns layers and sublayers into groups and every object into a
layer named as the Layers panel names it (text by its text); clipping, masked,
knockout or appearance-carrying layers stay one layer. hiddenLayers writes
hidden layers hidden instead of leaving them out; layers: false writes a flat
image on white. At most 30000 pixels a side.

Export As offers PSD with the psdOptions dialog (the PNG Options rows, then
Color Model, Flat Image / Write Layers with Maximum Editability and Include
Hidden Layers, Embed ICC Profile). Tests read the files back with the psd
crate (MIT OR Apache-2.0, a dev-dependency).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 16:12:03 +07:00

50 lines
2.1 KiB
Rust

//! PSD Options through Export As, drawn headlessly.
use serde_json::json;
use super::tests_export::{app, frame, kind, set};
use super::*;
#[test]
fn psd_options_pick_flat_or_layers_and_the_colour_model() {
let (mut app, written) = app(1);
app.run("file.exportAs", json!({"format": "psd"})).unwrap();
confirm(&mut app).unwrap();
assert_eq!(kind(&app), Some("psdOptions"));
let d = app.ui.dialog.as_ref().unwrap();
assert_eq!((spec(&d.kind).heading)(d), "PSD Options");
assert_eq!(
(d.str("colorModel"), d.bool("layers"), d.bool("maxEditability"), d.bool("hiddenLayers"), d.bool("embedIcc")),
("rgb".into(), true, false, false, true)
);
assert!(tiff_bmp_tga::keeps_alpha("psd", d), "layers keep transparency");
frame(&mut app);
set(&mut app, "maxEditability", json!(true));
frame(&mut app);
confirm(&mut app).unwrap();
let (path, psd) = written.borrow()[0].clone();
assert_eq!(path, "/out/Untitled-1.psd");
assert_eq!(&psd[..4], b"8BPS");
// A group with the ellipse: three records, the merged image's alpha their transparency.
let at = 26 + 4 + 4 + u32::from_be_bytes(psd[30..34].try_into().unwrap()) as usize;
assert_eq!(i16::from_be_bytes([psd[at + 8], psd[at + 9]]), -3);
assert!(app.ui.dialog.is_none());
// Flat: flattened on white, so the background list starts at White.
app.run("file.exportAs", json!({"format": "psd"})).unwrap();
confirm(&mut app).unwrap();
set(&mut app, "layers", json!(false));
set(&mut app, "colorModel", json!("gray"));
assert!(!tiff_bmp_tga::keeps_alpha("psd", app.ui.dialog.as_ref().unwrap()));
frame(&mut app);
confirm(&mut app).unwrap();
let psd = written.borrow()[1].1.clone();
assert_eq!((u16::from_be_bytes([psd[12], psd[13]]), u16::from_be_bytes([psd[24], psd[25]])), (1, 1), "one grey channel");
// A CMYK document exports CMYK by default.
app.run("object.convertDocumentColorMode", json!({"mode": "cmyk"})).unwrap();
app.run("file.exportAs", json!({"format": "psd"})).unwrap();
confirm(&mut app).unwrap();
assert_eq!(app.ui.dialog.as_ref().unwrap().str("colorModel"), "cmyk");
}