From 38b247e394ef9f19ccdac5688b65a74bf3f2f7cf Mon Sep 17 00:00:00 2001 From: Matthew James Sivak Date: Thu, 15 Oct 2026 15:47:58 +0200 Subject: [PATCH 1/2] LR-META-XMP: Lightroom's profile and version bookkeeping is not reported as unimported XMP CompatibleVersion, the profile digests, the lens profile's identity and its default-strength scales, and CropConstrainToUnitSquare don't change the rendering; a Lightroom raw sidecar listed all of them for every photo. Look and unknown fields are still reported. Split out of #2008. Co-Authored-By: Claude Opus 5.5 --- crates/engine/src/crs.rs | 28 ++++++++++++++++++++++++++++ docs/parity-checklist.md | 5 +++-- docs/whats-new.md | 9 +++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/crates/engine/src/crs.rs b/crates/engine/src/crs.rs index 9de8c3d64..093f3a84f 100644 --- a/crates/engine/src/crs.rs +++ b/crates/engine/src/crs.rs @@ -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 = 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#"Vivid"#; + assert_eq!(unmapped(&preset_packet(bookkeeping, "")), Vec::::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, diff --git a/docs/parity-checklist.md b/docs/parity-checklist.md index 4e0531793..744eb0ede 100644 --- a/docs/parity-checklist.md +++ b/docs/parity-checklist.md @@ -1,6 +1,6 @@ # Lightroom parity checklist -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-15 · **Change:** minor (LRC-PRINT-PAGE: Print Page Options) · **Target:** Adobe Lightroom Classic 15.5 / Lightroom 9.6 +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-18 · **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` | 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 (`.xmp` for all but the raw); 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` | 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 (`.xmp` for all but the raw); 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` | 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) @@ -797,6 +797,7 @@ Latin stays upright. Advanced Japanese composition remains open. Regression cove | Date | Change | Summary | |---|---|---| +| 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-14 | patch | LR-EXP-TYPE: opaque 8-bit PNG/TIFF/WebP use RGB; transparent exports retain alpha (#1670). Statuses and feature counts unchanged. | | 2026-10-15 | minor | LRC-PRINT-PAGE: Page Options (Page Numbers, Page Info, Crop Marks) on Single Image and Contact Sheet pages, in the Print dialog, `print.set` and templates; still 🟡 (no identity plate, watermark, photo info or Cut Guides) | | 2026-10-15 | minor | LR-IMP-FORMATS: Hasselblad 3FR and FFF show their uncompressed RGB preview (no JPEG in these files) in the grid and loupe until developed, and as the stand-in when a file fails to decode; status unchanged (🟡) | diff --git a/docs/whats-new.md b/docs/whats-new.md index 2f02ffb91..b4b2a0b90 100644 --- a/docs/whats-new.md +++ b/docs/whats-new.md @@ -1,5 +1,14 @@ # What's new in LightCraft +## 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. + ## 2026-10-14 ### Model downloads time out on a name that doesn't resolve From 86bf39dc4e5bc7f2bde60ce50b42295f374c542c Mon Sep 17 00:00:00 2001 From: Matthew James Sivak Date: Mon, 19 Oct 2026 12:15:56 +0200 Subject: [PATCH 2/2] LR-META-XMP: re-run CI (FreeBSD aarch64 runner setup failed) Co-Authored-By: Claude Opus 5.5