#3087: report a Photoshop layer item as big as neither its layer nor the document

Such an item is shown on the document, which the size does not confirm; it is now reported in unsupported_items, when the opti stores the document's size.
This commit is contained in:
arnav-hiwarkar committed 2026-11-11 22:02:46 +05:30
1 parent a6fba29c3e
commit 14d04536aa
3 files changed
+20 -10

No files matched your search

+8 -1
View File
@@ -353,7 +353,8 @@ fn is_placed_page_path(path: &str) -> bool {
/// The Photoshop layer a footage item shows (#3087): at its own bounds when the item is as big
/// as they are (Retain Layer Sizes), else on the document. A layer without pixels (an adjustment
/// layer) keeps the merged image and is reported. So is a layer as big as the document but not at
/// its corner: the item's size fits both framings, and its bounds are used.
/// its corner (the item's size fits both framings; its bounds are used), and an item as big as
/// neither (the document is used).
fn source_layer(f: &crate::Footage, name: &str, col: &mut Collector) -> Option<SourceLayer> {
let l = f.psd_layer.as_ref()?;
let Some(size) = l.size() else {
@@ -361,6 +362,12 @@ fn source_layer(f: &crate::Footage, name: &str, col: &mut Collector) -> Option<S
return None;
};
let layer_size = size == (f.width, f.height);
if !layer_size && l.document.iter().all(|side| *side > 0) && !l.document_is((f.width, f.height)) {
col.lose(
LossCategory::UnsupportedItems,
format!("footage '{name}': {} x {} is neither the Photoshop layer's size nor the document's, shown on the document", f.width, f.height),
);
}
let [top, left, ..] = l.bounds;
if layer_size && (top, left) != (0, 0) && l.document_is(size) {
col.lose(
+10 -7
View File
@@ -899,19 +899,22 @@ fn a_photoshop_layer_item_shows_its_layer() {
}
/// A layer as big as the document but off its corner fits both framings: shown at its own bounds
/// and reported. One at the corner, or bigger than the document, reads one way and is not.
/// and reported. One at the corner, or bigger than the document, reads one way and is not. An item
/// as big as neither is shown on the document and reported.
#[test]
fn a_photoshop_layer_as_big_as_the_document_off_its_corner_is_reported() {
let framing = |bounds, size| {
let (p, r) = run(&as_psd_layer(3, bounds, "Moved", size), true);
let found = losses(&r.unwrap(), LossCategory::UnsupportedItems);
(the_footage(&p).layer.map(|l| l.layer_size), found.iter().any(|d| d.contains("'Moved/art.psd'") && d.contains("as big as the document")))
let said = |what: &str| found.iter().any(|d| d.contains("'Moved/art.psd'") && d.contains(what));
(the_footage(&p).layer.map(|l| l.layer_size), said("as big as the document"), said("neither the Photoshop layer's size"))
};
assert_eq!(framing([5, 10, 1005, 1010], (1000, 1000)), (Some(true), true));
assert_eq!(framing([0, 0, 1000, 1000], (1000, 1000)), (Some(true), false));
assert_eq!(framing([9, -346, 1083, 1086], (1432, 1074)), (Some(true), false));
// An item of another size frames the layer on the document: no ambiguity.
assert_eq!(framing([5, 10, 1005, 1010], (800, 800)), (Some(false), false));
assert_eq!(framing([5, 10, 1005, 1010], (1000, 1000)), (Some(true), true, false));
assert_eq!(framing([0, 0, 1000, 1000], (1000, 1000)), (Some(true), false, false));
assert_eq!(framing([9, -346, 1083, 1086], (1432, 1074)), (Some(true), false, false));
assert_eq!(framing([749, 800, 764, 848], (1000, 1000)), (Some(false), false, false));
// An item as big as neither the layer nor the document: shown on the document, reported.
assert_eq!(framing([5, 10, 1005, 1010], (800, 800)), (Some(false), false, true));
}
/// #3087: a Photoshop layer without pixels (an adjustment layer) keeps the merged image and is
+2 -2
View File
@@ -70,7 +70,7 @@ outside the sampled ones keeps the current value and is reported as `project set
| Shape layers | groups (nested, with anchor, position, scale, skew, skew axis, rotation, opacity), rectangle (size, position, roundness), ellipse (size, position), polystar (star or polygon, points, position, rotation, radii, roundness), bezier path (vertices and tangents in layer pixels, path keys), fill (colour, opacity, fill rule), stroke (colour, opacity, width, cap, join, miter limit), gradient fill and stroke (linear or radial, start and end point; the default white-to-black colours), trim paths (start, end, offset, trim multiple shapes), round corners (radius), merge paths (mode; 707, 708), offset paths (amount, line join, miter limit, copies, copy offset; 710; After Effects offsets copy *i* by Amount x (*i* + Copy Offset), default 1, EffectCraft by Amount x (*i* + 1 + copyOffset), so the copy offset arrives 1 less, also its keys; an expression on it is kept and reported; 826-828, 828 renders equal to After Effects on every grid point), repeater (copies, offset, composite, transform; 709), in file order, with keyframes and expressions, the operators' defaults as After Effects reads them back (731); the layer anchor in pixels; 238-242, 330-333, 451-484, 519, 705-710, 731 |
| Markers | layer and composition markers: time (layer time for layer markers, exact frames), duration (1/600 s units, 509, 510), comment, chapter, URL, frame target, label, cue point (name, event or navigation, parameters); 122, 123, 249, 347-349, 509, 510 |
| Time Remapping | an enabled remap becomes the `timeRemap` property (source seconds, keys in layer time with their interpolation and ease, expression), first in the layer's properties as Layer > Time > Enable Time Remapping puts it; 319, 354 |
| Footage | a link to the stored path; **missing-footage placeholder** when the path does not exist where the import runs (listed in `missingFootage`), a normal link when it does. The importer never opens footage. **An image sequence** (its alias is the folder) becomes a sequence of its files, named from the stored numbering (`shot_0101.exr` .. `shot_0103.exr` in 603, `seq_001.png` .. in 353), at its native frame rate; the first file is the footage's path and the one checked. Sequences and clips take the stored native frame rate and duration (603, 606). A sequence of more than 100 000 frames, or beyond a million file names in one import, links only its first file and is reported. **One layer of a Photoshop file** shows that layer (`Footage::layer`: its index and name; at its own size with Retain Layer Sizes, else on the document) and is named `<layer>/<file>` unless the item has a name; a layer without pixels (an adjustment layer) keeps the merged image and is reported; so is a layer as big as the document but not at its corner, which the item's size does not tell apart (shown at its own size) |
| Footage | a link to the stored path; **missing-footage placeholder** when the path does not exist where the import runs (listed in `missingFootage`), a normal link when it does. The importer never opens footage. **An image sequence** (its alias is the folder) becomes a sequence of its files, named from the stored numbering (`shot_0101.exr` .. `shot_0103.exr` in 603, `seq_001.png` .. in 353), at its native frame rate; the first file is the footage's path and the one checked. Sequences and clips take the stored native frame rate and duration (603, 606). A sequence of more than 100 000 frames, or beyond a million file names in one import, links only its first file and is reported. **One layer of a Photoshop file** shows that layer (`Footage::layer`: its index and name; at its own size with Retain Layer Sizes, else on the document) and is named `<layer>/<file>` unless the item has a name; a layer without pixels (an adjustment layer) keeps the merged image and is reported; so is a layer as big as the document but not at its corner, which the item's size does not tell apart (shown at its own size), and an item as big as neither the layer nor the document (shown on the document) |
Units, the "absent = default" rule and the keyframe templates are the reader's
([aep-format.md](aep-format.md), sections 3.3–3.6). The importer converts them to EffectCraft's:
@@ -345,7 +345,7 @@ for the `unlisted` ones. The categories:
| `parent_links` | a parent that was not imported (a skipped camera, a missing layer) or that forms a loop |
| `track_mattes` | a track matte of unknown mode, whose matte layer was not imported or does not exist, a layer matted by itself, or a matte of the older "layer above" form on the top layer |
| `unsupported_layers` | an unknown layer type, a layer without a usable source, a precomposition loop (the layer is cut) |
| `unsupported_items` | a project item of an unknown kind; an item made from a Photoshop layer without pixels (it shows the merged image), or from one as big as the document but not at its corner (shown at its own size) |
| `unsupported_items` | a project item of an unknown kind; an item made from a Photoshop layer without pixels (it shows the merged image), or from one as big as the document but not at its corner (shown at its own size), or an item as big as neither the layer nor the document (shown on the document) |
| `damaged_data` | a header, key or value that could not be read, numbers that are not finite or beyond ±1e9 (default used); an unreadable text document (the text layer is imported with empty text), damaged style runs |
`limitations` states what the reader cannot see at all, so it can be neither imported nor counted: