Merge PR #2257: LR-META-XMP: fewer false "XMP adjustments were not imported" warnings for raws edited elsewhere

Conflicts:
- docs/parity-checklist.md: LR-META-XMP row carries both changes (JPEG embedded XMP, bookkeeping crs fields)
This commit is contained in:
aiongg
2026-10-22 02:40:48 +00:00
3 changed files with 38 additions and 2 deletions
+28
View File
@@ -93,6 +93,17 @@ const NON_ADJUSTMENT: &[&str] = &[
// the file's as-shot white, recorded next to the edit (ours comes from the file itself)
"crs:AsShotTemperature",
"crs:AsShotTint",
// which Lightroom version may read the edit, and checksums of the profiles it names
"crs:CompatibleVersion",
"crs:CameraProfileDigest",
"crs:LensProfileDigest",
// the lens profile Lightroom picked: ours is the file's own, only `LensProfileEnable` carries over
"crs:LensProfileName",
"crs:LensProfileFilename",
"crs:LensProfileIsEmbedded",
"crs:LensProfileSetup",
// a crop tool option (keep the crop inside the photo), not the crop itself
"crs:CropConstrainToUnitSquare",
];
/// True if the packet carries any `crs:` adjustment (not just bookkeeping fields).
@@ -492,6 +503,8 @@ pub fn to_partial_report(props: &Props, values: Option<&crate::crs_masks::Values
|| k == "AutoTone"
|| k == "AutoGrayscaleMix"
|| (k == "PointColors" && value_is(k, &empty_points))
// the lens profile's strength, written at its default (100 %) next to every profile
|| (matches!(k, "LensProfileDistortionScale" | "LensProfileVignettingScale") && value_is(k, &|v| v.iter().all(|s| s.trim() == "100")))
};
let mut unmapped: Vec<String> = props
.keys()
@@ -869,6 +882,21 @@ mod tests {
assert_eq!(got, ["FutureSlider", "PointColors"]);
}
#[test]
fn lightroom_profile_bookkeeping_is_not_reported() {
// what Lightroom Classic writes next to every raw's edit: none of it changes the rendering
let bookkeeping = r#"crs:CompatibleVersion="268435456" crs:CameraProfileDigest="0A1B" crs:LensProfileDigest="2C3D"
crs:LensProfileName="Sony FE 24-70mm" crs:LensProfileFilename="x.lcp" crs:LensProfileIsEmbedded="False"
crs:LensProfileSetup="LensDefaults" crs:LensProfileDistortionScale="100" crs:LensProfileVignettingScale="100"
crs:CropConstrainToUnitSquare="1""#;
let look = r#"<crs:Look rdf:parseType="Resource"><crs:Name>Vivid</crs:Name></crs:Look>"#;
assert_eq!(unmapped(&preset_packet(bookkeeping, "")), Vec::<String>::new());
assert_eq!(unmapped(&preset_packet(bookkeeping, look)), ["Look"], "the Look is a real gap");
// a lens profile's strength away from 100 % is an adjustment we don't carry over
let scaled = preset_packet(r#"crs:LensProfileDistortionScale="60""#, "");
assert_eq!(unmapped(&scaled), ["LensProfileDistortionScale"]);
}
#[test]
fn disabled_lens_profile_switch_keeps_embedded_corrections() {
// Lightroom applies a file's built-in lens corrections regardless of its profile switch,
+3 -2
View File
@@ -1,6 +1,6 @@
# Lightroom parity checklist
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-20 · **Change:** minor (LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict) · **Target:** Adobe Lightroom Classic 15.5 / Lightroom 9.6
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-20 · **Change:** patch (LR-META-XMP: fewer false unimported-XMP warnings) · **Target:** Adobe Lightroom Classic 15.5 / Lightroom 9.6
The measured, row-by-row checklist: one row per Lightroom feature, menu item and shortcut, what we
have and what is missing. It answers *does the feature exist?* (feature breadth). Whether it is good
@@ -170,7 +170,7 @@ differences and missing bindings are in [ui-parity.md](ui-parity.md#shortcuts).
| LR-META-COPYRIGHT-DEFAULT | Default copyright on import | P1 | ✅ | `cmd:library.preferences` (`import.copyright`, `import.creator`), `crates/ui-egui/src/panels/settings.rs` | Settings → Import → Metadata; fills only photos without their own |
| LR-META-LOCATION | Location editing | P2 | 🟡 | `cmd:photo.setMeta` (`location`, `city`, `state`, `country`, `gps`), `cmd:metadata.reverseGeocode` | place fields and GPS (decimal or degrees / minutes / seconds) edited in Info, read/written as IPTC Core / Photoshop / EXIF XMP; Show on Map opens OpenStreetMap; Fill Location fills empty City / State / Country from GPS offline (GeoNames, downloaded on request: `cmd:metadata.downloadPlaceNames`); no embedded map |
| LR-META-COPYPASTE | Copy / paste metadata | P2 | ✅ | `cmd:photo.copyMetadata`, `cmd:photo.pasteMetadata` | title, caption, alt text, extended description, copyright (notice, status, usage terms, info URL), creator, place fields, keywords; `fields` picks a subset |
| LR-META-XMP | XMP read/write | P0 | ✅ | `cmd:photo.saveMetadataToFile`, `cmd:photo.readMetadataFromFile`, `cmd:library.xmpPreferences`, `crates/engine/src/sidecar.rs`, `docs/xmp-interop.md` | manual rereading also reads sidecar-free JPEG embedded XMP (first complete standard APP1 before SOS/EOI, within 16 MiB of headers and 4096 marker/fill steps; no extended assembly or between-scan packets, not strict whole-JPEG validation; reader cases in `crates/engine/src/sidecar.rs`); a discovered unreadable sidecar fails without embedded fallback; procedural refresh/undo, non-UTF-8 sidecar and two explicit malformed fixtures in `crates/engine/src/tests_xmp.rs` (file-write checks use auto-write off); sidecar wins for metadata; a sidecar capture time (`exif:DateTimeOriginal` / `photoshop:DateCreated` / `xmp:CreateDate`) fills in only when the file has none; saving merges into an existing sidecar (`crates/meta/src/xmp_merge.rs`): another app's `crs:`, `xmpMM:History` and unknown namespaces are kept byte for byte, an unreadable sidecar is backed up first; with stem naming, files sharing a stem (raw + JPEG) get separate sidecars (`<file>.xmp` for all but the raw; an import ranks the files it is adding the same way, so a TIFF or JPEG arriving beside its raw reads its own `<file>.xmp` and never the raw's `IMG_0001.xmp`, issue #1905); a `crs:` custom white balance on a relative-WB raw is shifted from `crs:AsShotTemperature` rather than read as Kelvin, or kept As Shot and reported when the packet has no as-shot white (issue #510); heal and clone spots (`crs:RetouchAreas`, legacy `crs:RetouchInfo`) are read into our spots (`crates/engine/src/crs_masks.rs`, see `docs/xmp-interop.md` → Spot removal); radial masks (`Mask/CircularGradient`) keep Lightroom's inside / outside: `crs:Flipped="true"` is the effect-inside flag of current masks and of the legacy Radial Filter, `crs:MaskInverted` the inversion, checked against Lightroom Classic's renders (issue #1815); Read Metadata from File never writes the sidecar back, even with auto-write on, so another app's later `crs:` edits are still read (issue #934) |
| LR-META-XMP | XMP read/write | P0 | ✅ | `cmd:photo.saveMetadataToFile`, `cmd:photo.readMetadataFromFile`, `cmd:library.xmpPreferences`, `crates/engine/src/sidecar.rs`, `docs/xmp-interop.md` | manual rereading also reads sidecar-free JPEG embedded XMP (first complete standard APP1 before SOS/EOI, within 16 MiB of headers and 4096 marker/fill steps; no extended assembly or between-scan packets, not strict whole-JPEG validation; reader cases in `crates/engine/src/sidecar.rs`); a discovered unreadable sidecar fails without embedded fallback; procedural refresh/undo, non-UTF-8 sidecar and two explicit malformed fixtures in `crates/engine/src/tests_xmp.rs` (file-write checks use auto-write off); sidecar wins for metadata; a sidecar capture time (`exif:DateTimeOriginal` / `photoshop:DateCreated` / `xmp:CreateDate`) fills in only when the file has none; saving merges into an existing sidecar (`crates/meta/src/xmp_merge.rs`): another app's `crs:`, `xmpMM:History` and unknown namespaces are kept byte for byte, an unreadable sidecar is backed up first; with stem naming, files sharing a stem (raw + JPEG) get separate sidecars (`<file>.xmp` for all but the raw; an import ranks the files it is adding the same way, so a TIFF or JPEG arriving beside its raw reads its own `<file>.xmp` and never the raw's `IMG_0001.xmp`, issue #1905); a `crs:` custom white balance on a relative-WB raw is shifted from `crs:AsShotTemperature` rather than read as Kelvin, or kept As Shot and reported when the packet has no as-shot white (issue #510); heal and clone spots (`crs:RetouchAreas`, legacy `crs:RetouchInfo`) are read into our spots (`crates/engine/src/crs_masks.rs`, see `docs/xmp-interop.md` → Spot removal); radial masks (`Mask/CircularGradient`) keep Lightroom's inside / outside: `crs:Flipped="true"` is the effect-inside flag of current masks and of the legacy Radial Filter, `crs:MaskInverted` the inversion, checked against Lightroom Classic's renders (issue #1815); Read Metadata from File never writes the sidecar back, even with auto-write on, so another app's later `crs:` edits are still read (issue #934); settings Lightroom writes only for itself (`CompatibleVersion`, the profile digests, the lens profile's name / file / setup, `CropConstrainToUnitSquare`, the lens profile's strength at 100 %) are not reported as unimported (`crates/engine/src/crs.rs`) |
| LR-META-EXIF-FULL | Full EXIF/IPTC [Classic] | P1 | ✅ | `cmd:photo.allMetadata`, `cmd:dialog.allMetadata`, `crates/meta/src/tags.rs`, `crates/meta/src/exif.rs`, `crates/meta/src/iptc.rs` | `photo.allMetadata` reuses loaded original bytes for embedded XMP and keeps sidecar precedence (`crates/engine/src/tests_xmp.rs`); read and written on export; Info ▸ All Metadata…: every TIFF / EXIF / GPS / interop tag (named, common values spelled out) and the XMP fields, searchable; metadata presets |
## F. Edit panel — global adjustments (EDIT)
@@ -799,6 +799,7 @@ Latin stays upright. Advanced Japanese composition remains open. Regression cove
|---|---|---|
| 2026-10-20 | patch | LR-VIEW-ZOOM: the 20 ms rule clears a slow-drag verdict only on a drag timed at full size; a drag that drafts small leaves it unchanged, as the code does; status unchanged |
| 2026-10-19 | patch | LR-VIEW-ZOOM: the slow slider-drag verdict is described as per photo and control, as the code keys it; status unchanged |
| 2026-10-18 | patch | LR-META-XMP: Lightroom's profile and version bookkeeping (`CompatibleVersion`, profile digests, lens profile identity, `CropConstrainToUnitSquare`, default-strength lens profile scales) is no longer reported as unimported XMP; status unchanged (✅) |
| 2026-10-15 | minor | LR-VIEW-ZOOM: a slow slider-drag verdict needs two drags over 20 ms and expires after 30 s (doubling to 240 s); hidden-window frame times are ignored; the slow set forgets its oldest entry, not all (issue #330); status unchanged |
| 2026-10-15 | minor | LR-BEHAV-BG-TASKS: Reload from Disk reads its files on a worker thread with a cancellable activity row instead of freezing the window on a selection of raws; its result is committed through the command wrapper (XMP sidecars auto-written as before) and an older reload finishing late no longer overwrites a newer one; status unchanged (✅) |
| 2026-10-14 | patch | LR-EXP-TYPE: opaque 8-bit PNG/TIFF/WebP use RGB; transparent exports retain alpha (#1670). Statuses and feature counts unchanged. |
+7
View File
@@ -8,6 +8,13 @@
## 2026-10-18
### Fewer false "not imported" warnings for Lightroom raws
- Importing raws with Lightroom XMP sidecars no longer lists settings Lightroom writes only for itself as adjustments
that weren't imported: `CompatibleVersion`, the camera and lens profile checksums, the lens profile's name, file and
setup, the crop's "constrain to image" option, and the lens profile's strength when it is at its default of 100 %.
A raw sidecar used to name all of them for every photo. Settings LightCraft really doesn't apply, such as `Look`,
are still reported.
### Fill Location from GPS: no more neighbouring town in City
- The offline place names list towns of 15,000 people or more, so a photo taken in a smaller one got the nearest larger
town in City, sometimes another municipality (issue #2242). The Fill Location tooltip and the command help now say so,