Skip to content

fix(export): embed keywords, rating and label as XMP - #1896

Merged
CyberTimon merged 2 commits into
CyberTimon:mainfrom
lalibertemarc:fix/export-keywords
Oct 11, 2026
Merged

CyberTimon merged 2 commits into
CyberTimon:mainfrom
lalibertemarc:fix/export-keywords

Conversation

@lalibertemarc

@lalibertemarc lalibertemarc commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Description

Fixes #1887 (and the keyword half of the closed #1618), fixes #1728, and fixes the WebP part of #1322.

Tags set in RapidRAW never reached the exported file. With "Save with Metadata" on, exports now carry an XMP packet with the image's keywords, star rating and color label, so Lightroom, Bridge, digiKam, Immich and Windows Explorer pick them up.

This overlaps with #1626 by @chuckhenrich, which tackled the same bug first by injecting dc:subject keywords. Credit to them for that. The differences here:

  • The user: / color: prefixes are stripped (in fix: inject XMP dc:subject keywords into exported images #1626, keywords come out as user:landscape, color:red). Color tags become xmp:Label, and the rating is written as xmp:Rating.
  • Tags are read from the exported image's own sidecar, so virtual copies export their own tags.
  • In JPEG, the XMP segment goes after the existing APPn segments, so JFIF stays first.
  • When the source already has embedded XMP, it is merged instead of dropped (see below).

Source XMP (#1728): for non-RAW sources such as DJI panoramas, the source's own XMP is kept, so GPano:* and similar tags survive the export. RapidRAW's values are merged in: keywords are combined, and the rating and label replace the source's only when they are set. A few source properties that would be wrong after export are removed:

  • tiff:Orientation, because the export resets orientation and this would rotate the photo twice
  • the pixel dimensions
  • Lightroom's crs: develop settings, because Lightroom would apply the edits again
  • the extended-XMP pointer

When "Remove GPS Data" is on, the source XMP is skipped entirely, because drone XMP also carries location (drone-dji:GpsLatitude, etc.).

WebP had no EXIF at all: libwebp's lossy output is a simple-format (VP8 ) file. little_exif can't convert it to the extended format, so write_to_vec failed and every WebP export lost its metadata, with only a log warning. The file is now converted to VP8X before the EXIF and XMP are written.

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Performance improvement
  • Code refactoring
  • Documentation update
  • UI/UX improvement
  • Build/CI or Dependency update

Changes Made

  • exif_processing.rs:
    • write_image_with_metadata resolves the output format once, then runs the existing EXIF step (write_exif_metadata) followed by a new XMP step.
    • ExportXmp::from_metadata collects the keywords, rating and label from the sidecar.
    • The packet is merged into the source XMP with quick-xml, which is already a dependency.
    • The packet is embedded into JPEG (APP1), PNG (iTXt) or WebP (XMP chunk + VP8X flag).
    • convert_webp_to_extended adds the VP8X header for lossy WebP.
  • file_management.rs: xmp_rating, xmp_label and xmp_subject_bag were pulled out of sync_metadata_to_xmp so the sidecar sync and the export share them. The sidecar sync's output is unchanged.
  • export_processing.rs: the sidecar is loaded once per image, and the XMP info is passed to the main and mask exports.
  • ExportPanel.tsx: the Metadata section (Save with Metadata / Remove GPS Data) now also shows for PNG and WebP, which already used these settings without showing them.
  • Cargo.toml: crc32fast for the PNG chunk CRC. It's already in the lock file through png, so nothing new is compiled.

Screenshots/Videos

image

Testing

  • These changes were tested locally by a human and confirmed to work.
  • I haven't added any automated tests to the code because the codebase currently lacks a test suite.

Exports were checked for:

  • JPEG/PNG/WebP files that decode with both the image crate and ImageMagick/libwebp
  • well-formed XMP with escaped special characters (A&B <x>)
  • keywords, rating and label read back by other apps
  • a source JPEG carrying GPano, tiff:Orientation, crs: and its own keywords, which came out merged as described above
  • "Remove GPS Data", which ignores the source XMP
  • an image with no tags, rating or label, whose output is untouched

Test Configuration:

  • OS: Windows 10 Home (19045)
  • Hardware: AMD Ryzen 5 3600X, NVIDIA GeForce RTX 2070

Checklist

  • My code follows the project's code style
  • I haven't added unnecessary AI-generated code comments
  • My changes generate no new warnings or errors

Additional Notes

AI Disclaimer:

Please state the involvement of AI in this PR:

  • This PR is created by an AI agent
  • This PR is mostly AI-generated but edited/merged together by a human
  • This PR was handwritten with AI assistance (spell check, logic suggestions, error resolving)
  • This PR contains only blood, sweat, and coffee (AI-free)

🤖 Generated with Claude Code

lalibertemarc and others added 2 commits October 11, 2026 01:16
Exports dropped RapidRAW tags, so other apps saw no keywords (CyberTimon#1887).
Write an XMP packet with dc:subject keywords (user: prefix stripped,
colour tags excluded), xmp:Rating and xmp:Label into JPEG, PNG and WebP
exports when "Save with Metadata" is on, reading tags from the image's
own sidecar so virtual copies keep theirs.

When the source is a non-RAW image with embedded XMP (e.g. DJI GPano
panoramas, CyberTimon#1728), merge into that packet instead of discarding it,
dropping properties the export makes stale (tiff:Orientation, pixel
dimensions, crs: develop settings, extended-XMP pointer). Source XMP is
skipped when "Remove GPS Data" is on since it can carry location.

libwebp's lossy output is a simple-format file that little_exif cannot
convert, so WebP exports silently lost all EXIF; convert it to the
extended format first. Show the metadata options for PNG and WebP too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@CyberTimon

Copy link
Copy Markdown
Owner

Amazing!! Thanks so much for your hard work and testing 💯

@CyberTimon
CyberTimon merged commit 86df8b6 into CyberTimon:main Oct 11, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BUG: Export Tags (Keywords) to jpeg BUG: "Save with metadata" do not save all metadata BUG: JPEGs exporting with date 1970-01-01, no keywords/tags

2 participants