Skip to content

Normalise non-EXIF orientation metadata when auto-orienting - #4586

Open
thedavidweng wants to merge 1 commit into
lovell:mainfrom
thedavidweng:fix-auto-orient-xmp-4585
Open

Normalise non-EXIF orientation metadata when auto-orienting#4586
thedavidweng wants to merge 1 commit into
lovell:mainfrom
thedavidweng:fix-auto-orient-xmp-4585

Conversation

@thedavidweng

@thedavidweng thedavidweng commented Aug 9, 2026

Copy link
Copy Markdown

Problem

autoOrient() (and the related SetExifOrientation / RemoveExifOrientation helpers) only normalised the EXIF IFD0 Orientation tag. The same value is frequently duplicated in two other places — PNG text-chunk comments (e.g. files produced by macOS sips) and XMP packets (<tiff:Orientation>) — and these were left untouched.

After an auto-orient/bake-in operation the pixels are already rotated, but the stale PNG-comment / XMP orientation survives into the output. Consumers that honour XMP first (or read PNG text chunks) rotate the already-corrected pixels a second time, producing an incorrectly-displayed image.

Resolves #4585.

Solution

Add UpdateOrientationMetadata(), called from both SetExifOrientation and RemoveExifOrientation, which — alongside the existing EXIF IFD0 handling — also:

  • updates PNG text-chunk comments whose keyword is EXIF:Orientation or xmp:tiff:Orientation (libvips exposes these as png-comment-<index>-<keyword> fields), and
  • replaces every <tiff:Orientation>…</tiff:Orientation> element within an embedded XMP packet, tolerating attribute-bearing and self-closing forms and leaving values inside XML comments untouched.

Both writes take ownership of the buffer through glib-owned memory and a libvips free callback, so the metadata outlives the operation.

Explicit user-supplied metadata (e.g. .withXmp(...)) is intentionally left untouched, since it represents deliberate user input rather than inherited orientation.

Verification

  • Reproduced against the released sharp@0.34.5: an input PNG carrying EXIF Orientation 6 plus an XMP <tiff:Orientation>6</tiff:Orientation> produced output whose EXIF Orientation was normalised to 1 but whose XMP orientation remained 6.
  • After this change both the EXIF and the XMP/PNG-comment orientation values are normalised to 1, and the XMP packet survives intact (exiftool parses it without warnings).

Tests

Two unit tests in test/unit/rotate.js, each failing without the corresponding C++ change:

  • Auto-orient normalises orientation duplicated in non-EXIF metadata — PNG text-chunk path (inputPngWithExifXmpOrientation.png).
  • Auto-orient normalises XMP packet orientation and preserves packet integrity — real XMP packet path (inputPngWithXmpOrientation.png); also guards the buffer-lifetime fix.

npm run test-unit passes (the only failure locally — Lossless JPEG in test/unit/metadata.js — is pre-existing and unrelated; it reproduces on pristine main with my libvips build). npm run lint-cpp and biome lint are clean.

(Happy to add a changelog entry if you'd like — left it out since recent external-contributor PRs leave that to you at merge time.)

@thedavidweng
thedavidweng force-pushed the fix-auto-orient-xmp-4585 branch 2 times, most recently from 9b31e30 to 6e0cf0b Compare August 9, 2026 22:52
autoOrient() and SetExifOrientation/RemoveExifOrientation previously only
updated the EXIF IFD0 Orientation tag, leaving the same value duplicated in
PNG text-chunk comments (e.g. macOS sips output) and XMP packets
(<tiff:Orientation>) untouched. Consumers that read XMP first would rotate
the already-corrected pixels a second time.

Also normalise these non-EXIF sources of orientation to match, so that no
stale orientation metadata survives an auto-orient/bake-in operation.

lovell#4585
@thedavidweng
thedavidweng force-pushed the fix-auto-orient-xmp-4585 branch from 6e0cf0b to 48ff637 Compare August 10, 2026 00:53
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.

autoOrient() normalizes EXIF Orientation but leaves XMP 'tiff:Orientation' untouched (PNG input with iTXt XMP)

1 participant