mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-28 03:47:00 -06:00
Image > Analysis (ruler, count, measurement scale/log/record, scale marker), notes (PSD Anno round trip, Notes panel, import), Modifier Keys panel, Proof Setup plates / Legacy Mac RGB / colour blindness (Vienot), 32-bit preview options, custom pixel aspect, extras options, New/Delete/ Lock Workspace. Parity 600/625, FLOOR 595. Checkpoint: also contains compiling in-progress work from the parallel panels, type styles, File menu, photo, filters, UI-parity and fidelity agents, plus photocraft-cli serve. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
62 lines
2.8 KiB
Rust
62 lines
2.8 KiB
Rust
//! Notes (`Anno`) and the measurement scale (resource 1074) through PSD: verbatim while
|
|
//! unchanged, regenerated after edits.
|
|
|
|
use std::path::PathBuf;
|
|
|
|
use photocraft_color::{ColorMode, SampleType};
|
|
use photocraft_doc::{Document, MeasurementScale, Note};
|
|
use photocraft_geom::Size;
|
|
use photocraft_io::annotations_map::MEASUREMENT_SCALE;
|
|
use photocraft_io::{ExportOptions, export, import};
|
|
use photocraft_psd::PsdFile;
|
|
|
|
fn corpus(rel: &str) -> Option<Vec<u8>> {
|
|
std::fs::read(PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../corpus/psd").join(rel)).ok()
|
|
}
|
|
|
|
fn to_psd(doc: &Document) -> Vec<u8> {
|
|
export(doc, "x.psd", &ExportOptions::default()).expect("export").bytes
|
|
}
|
|
|
|
#[test]
|
|
fn corpus_notes_import_and_verbatim_export() {
|
|
let Some(bytes) = corpus("ag-psd/read-write/annotations/src.psd") else { return };
|
|
let src = PsdFile::from_bytes(&bytes).unwrap();
|
|
let mut doc = import("src.psd", &bytes).unwrap().document;
|
|
assert_eq!(doc.notes.len(), 2);
|
|
assert_eq!(doc.notes[1].text, "open note");
|
|
let out = to_psd(&doc);
|
|
let f = PsdFile::from_bytes(&out).unwrap();
|
|
assert_eq!(f.global_block(b"Anno").unwrap().data, src.global_block(b"Anno").unwrap().data);
|
|
|
|
// Edit a note: the block is regenerated and reads back with the change.
|
|
doc.notes[0].text = "changed".into();
|
|
doc.notes.push(Note { author: "agent".into(), text: "third".into(), position: [5.0, 6.0], ..Default::default() });
|
|
let back = import("x.psd", &to_psd(&doc)).unwrap().document;
|
|
assert_eq!(back.notes.len(), 3);
|
|
assert_eq!(back.notes[0].text, "changed");
|
|
assert_eq!(back.notes[2].position, [5.0, 6.0]);
|
|
// Deleting every note drops the block.
|
|
doc.notes.clear();
|
|
let f = PsdFile::from_bytes(&to_psd(&doc)).unwrap();
|
|
assert!(f.global_block(b"Anno").is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn measurement_scale_roundtrips_through_psd() {
|
|
for depth in [SampleType::U8, SampleType::U16, SampleType::F32] {
|
|
let mut doc = Document::with_background("m", Size::new(16, 8), ColorMode::Rgb, depth, photocraft_color::Color::rgb(1.0, 1.0, 1.0));
|
|
let f = PsdFile::from_bytes(&to_psd(&doc)).unwrap();
|
|
assert!(f.resource(MEASUREMENT_SCALE).is_none());
|
|
doc.measurement.scale = MeasurementScale { pixel_length: 120.0, logical_length: 3.0, units: "cm".into() };
|
|
doc.notes.push(Note { text: "n".into(), ..Default::default() });
|
|
let back = import("x.psd", &to_psd(&doc)).unwrap().document;
|
|
assert_eq!(back.measurement.scale, doc.measurement.scale);
|
|
assert_eq!(back.notes.len(), 1);
|
|
assert_eq!((back.notes[0].text.as_str(), back.notes[0].position), ("n", [0.0, 0.0]));
|
|
// Unchanged → the preserved resource is written byte-exact (once).
|
|
let again = PsdFile::from_bytes(&to_psd(&back)).unwrap();
|
|
assert_eq!(again.resources.iter().filter(|r| r.id == MEASUREMENT_SCALE).count(), 1);
|
|
}
|
|
}
|