Skip to content

Add EXIF carry-over and embedded thumbnail decoding - #6

Merged
mhrsntrk merged 1 commit into
masterfrom
feat/exif-and-thumbnails
Nov 6, 2026
Merged

mhrsntrk merged 1 commit into
masterfrom
feat/exif-and-thumbnails

Conversation

@mhrsntrk

@mhrsntrk mhrsntrk commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

What

  • convertHeic(file, { exif: 'none' | 'all' | 'no-location' }), default 'none' (unchanged behavior). Copied EXIF has Orientation reset to 1 and pixel dimensions set to the output size, since the pixels are already upright. 'no-location' removes the GPS block and zeroes its bytes. ConvertedImage.exif reports what was actually written.
  • decodeHeicThumbnail(file) decodes the embedded thmb thumbnail with WebCodecs, falling back to a downscaled primary when there is none (common on recent iPhones) or it cannot be decoded.
  • readExif(file) returns the raw EXIF TIFF block.
  • planItem and findThumbnailItemId exported for advanced use.

Minor changeset included, so merging produces the 0.3.0 release PR.

Details worth reviewing

  • Fails closed: a malformed EXIF block is never written in 'no-location' mode.
  • WebKit's JPEG encoder writes its own small Exif segment; ours replaces it instead of adding a second one.
  • EXIF edits are made in place on a copy, never re-serialized, so MakerNote stays byte-identical. Copies use new Uint8Array(view), because .slice() on a Node Buffer returns a view and would mutate the caller's bytes (caught by a test).
  • Core bundle: 14.8 KB to 17.8 KB gzipped; apps that only use decodeHeic can tree-shake the EXIF code.

Testing

  • Unit: 110 passed, incl. GPS byte-level absence check and Buffer non-mutation, with exifr as the independent parser.
  • Browser, chromium/firefox/webkit: 260 passed, 0 failed.
  • Browser, real WebCodecs HEVC (Brave, chrome-hevc): 137 passed, 0 skipped, including thumbnails of 9 real iPhone photos from the local corpus.
  • Converted a rotated real iPhone photo with exif: 'no-location': exiftool shows Orientation normal, 600x800 dims matching the pixels, no GPS (source has GPS), and the image is upright when viewed.

convertHeic takes exif: 'none' | 'all' | 'no-location'. Copied EXIF gets
Orientation reset to 1 and PixelX/YDimension set to the output size, since
the pixels are already upright; leaving the iPhone's Orientation in place
makes viewers rotate a second time. 'no-location' drops the GPS IFD pointer
and zeroes every GPS byte, and fails closed on a block it cannot walk. Any
Exif APP1 the encoder wrote (WebKit adds one) is replaced, not duplicated.

decodeHeicThumbnail decodes the thmb item with WebCodecs and falls back to a
downscaled primary; 4 of 9 recent iPhone photos in the local corpus have no
thumbnail at all. readExif returns the raw TIFF block.

New synthetic fixture exif-thumb.heic (fake GPS, Orientation 6, irot 270,
96 px thumbnail). exifr is a dev dependency so the tests check output with
an independent parser rather than our own reader.
@mhrsntrk
mhrsntrk merged commit 70a7ed1 into master Nov 6, 2026
2 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.

1 participant