mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-24 09:57:08 -06:00
fix(psd): keep preserved TextIndex values when a type layer is added below them
Text indices were assigned in one bottom-up pass, so a new layer took the next free number before the layers above it claimed the numbers their TySh keeps. Each of those was then renumbered onto its neighbour's Txt2 object and took its style (70 of 74 layers on the #7698 file after adding a layer at index 5). Kept numbers are now claimed first and new layers get numbers past them. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
604007490b
commit
78cc36f24a
+20
-13
@@ -1582,26 +1582,33 @@ fn document_to_psd_nested(doc: &Document, opts: &PsdExportOptions, depth: u32, c
|
||||
ex.used_ids = used;
|
||||
// Text objects' number in the generated `Txt2` (#1348): a preserved `TySh` keeps the
|
||||
// number it already names — its `Txt2` object must sit at that slot for `apply_txt2` —
|
||||
// and new layers get the next free number.
|
||||
// and new layers get numbers past every kept one. Kept numbers are claimed first: a layer
|
||||
// added below others must not take a number a layer above it keeps, or each of those
|
||||
// would be renumbered onto its neighbour's object and take its style (#7773).
|
||||
let mut used_txt = std::collections::HashSet::new();
|
||||
let mut next_txt = 0i32;
|
||||
ex.text_index = Default::default();
|
||||
let mut fresh = Vec::new();
|
||||
for (_, _, l) in &walk {
|
||||
let LayerContent::Text(t) = &l.content else {
|
||||
continue;
|
||||
};
|
||||
let kept = t.psd_raw.as_deref().and_then(|d| photocraft_text::psd::text_index(d));
|
||||
let index = match kept {
|
||||
Some(i) if used_txt.insert(i) => i,
|
||||
_ => {
|
||||
while used_txt.contains(&next_txt) {
|
||||
next_txt += 1;
|
||||
}
|
||||
used_txt.insert(next_txt);
|
||||
next_txt
|
||||
match t.psd_raw.as_deref().and_then(|d| photocraft_text::psd::text_index(d)) {
|
||||
Some(i) if used_txt.insert(i) => {
|
||||
ex.text_index.insert(l.id, i);
|
||||
}
|
||||
};
|
||||
ex.text_index.insert(l.id, index);
|
||||
_ => fresh.push(l.id),
|
||||
}
|
||||
}
|
||||
let mut next_txt = used_txt.iter().max().map_or(0, |m| m + 1);
|
||||
if next_txt.saturating_add(fresh.len() as i32) > photocraft_text::psd::MAX_TEXT_INDEX + 1 {
|
||||
next_txt = 0;
|
||||
}
|
||||
for id in fresh {
|
||||
while used_txt.contains(&next_txt) {
|
||||
next_txt += 1;
|
||||
}
|
||||
used_txt.insert(next_txt);
|
||||
ex.text_index.insert(id, next_txt);
|
||||
}
|
||||
}
|
||||
// Progress: one unit per layer record, then the merged composite (#3389).
|
||||
|
||||
@@ -144,6 +144,35 @@ fn duplicate_preserved_text_indices_get_distinct_txt2_objects() {
|
||||
}
|
||||
}
|
||||
|
||||
/// A type layer added below preserved ones takes a number past theirs: claiming the next free
|
||||
/// number on the way up renumbered every layer above it onto its neighbour's `Txt2` object
|
||||
/// (#7773, 70 of 74 layers on the #7698 file).
|
||||
#[test]
|
||||
fn new_text_layer_below_keeps_the_preserved_text_indices() {
|
||||
let mut doc = Document::new("k", photocraft_geom::Size::new(64, 32), photocraft_color::ColorMode::Rgb, photocraft_color::SampleType::U8);
|
||||
for (i, text) in ["AV", "BC"].into_iter().enumerate() {
|
||||
let mut t = layer(text, &[(2, Kerning::Optical, 0.0)]);
|
||||
photocraft_text::TextEngine::new().render_layer(&mut t, doc.resolution_dpi, doc.pixel_format());
|
||||
let raw = photocraft_text::psd::build_tysh(&t, doc.resolution_dpi, None);
|
||||
t.psd_raw = Some(std::sync::Arc::new(photocraft_text::psd::set_text_index(&raw, i as i32).unwrap()));
|
||||
doc.layers.push(Layer::new(text, LayerContent::Text(t)));
|
||||
}
|
||||
let mut t = layer("new", &[(3, Kerning::Metrics, 0.0)]);
|
||||
photocraft_text::TextEngine::new().render_layer(&mut t, doc.resolution_dpi, doc.pixel_format());
|
||||
doc.layers.insert(0, Layer::new("new", LayerContent::Text(t)));
|
||||
let bytes = photocraft_io::export(&doc, "k.psd", &Default::default()).unwrap().bytes;
|
||||
let back = photocraft_io::import("k.psd", &bytes).unwrap().document;
|
||||
let indices: Vec<_> = back
|
||||
.layers
|
||||
.iter()
|
||||
.map(|l| {
|
||||
let LayerContent::Text(t) = &l.content else { panic!("not text") };
|
||||
(l.name.as_str(), t.psd_raw.as_deref().and_then(|d| photocraft_text::psd::text_index(d)))
|
||||
})
|
||||
.collect();
|
||||
assert_eq!(indices, [("new", Some(2)), ("AV", Some(0)), ("BC", Some(1))]);
|
||||
}
|
||||
|
||||
/// A file-controlled `TextIndex` must not size the save: `i32::MAX` is ignored (the regenerated
|
||||
/// `Txt2` is sized by real text objects, never by a file's numbers) and the document still saves
|
||||
/// and reopens with its text.
|
||||
|
||||
Reference in New Issue
Block a user