- Remove location: export checked only the photo's own EXIF, so an
original HEIC whose GPS was in its thumbnail's EXIF or a second EXIF
item was copied out with it. lightcraft_meta::embeds_location reads
every HEIF EXIF and XMP item, and one it can't read counts as a
location; other formats are checked as before. The refusal offers DNG
only for raws (DNG export of a JPEG or HEIC fails).
- iloc is walked once for all candidate items instead of once per
candidate (a hostile 48 MB iloc with 64 items took over a second).
- EXIF tied to an image the primary is made from (dimg: iden, iovl,
tmap, grid inputs, followed with a seen-set) is the photo's; a pitm
naming no item counts as no primary.
- Full-box versions are read from the box's own payload.
- meta README lists HEIF in embedded and ISO/IEC 14496-12 and 23008-12
in its sources; target-app-parity no longer cites HEIC (#693) as a
file-exchange discount reason.
Tests, failing before: GPS in a thumbnail's EXIF, a second EXIF item or
an XMP item, and the unsure cases; a work bound counting iloc entries
walked; derived primaries and a missing pitm item; a HEIC original
refused under remove location; DNG offered only for raws.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Imported HEIC photos had no camera, capture time or All Metadata rows:
lightcraft_meta::embedded had no HEIF branch, and only the decoder read the
EXIF, while decoding pixels. crates/meta/src/heif.rs reads the primary
image's Exif item and XMP (mime) item from the ISO BMFF item structure
(meta: pitm, iinf, iref cdsc, iloc versions 0 to 2 with construction
methods 0 and 1 and idat) and honours the Exif item's TIFF-header offset.
No picture is decoded, so it works without the heif feature, and AVIF goes
through the same code. Offsets and lengths are checked; boxes, candidates
and item sizes are bounded. The CR3 box walker is reused.
Tests: the issue sweep's synthetic-metadata.heic case, EXIF offsets,
extents and construction methods, primary-image association, malformed
bounds, and a truncation, byte-damage and property sweep (meta); HEIC
import to the catalog and All Metadata, and AVIF export metadata reading
back (engine).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On merge: the HDR histogram is applied in develop_reporting (#712 split develop), is skipped for
draft renders (a second render per drag tick), and HdrBins::bin clamps a hand-built split so it
can't underflow (test hdr_bins_with_any_split_stay_in_range).
photo.editExternal takes the selection (or ids, at most 50): each photo gets its own -Edit copy
stacked on its original, the copies are selected and returned in copies (path / id / original
still name the first). Photo ▸ Edit In opens each copy in the editor.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BKWB9TCnvzJHV6xMLAj2J4
On merge: Lightroom's default profiles (Adobe Standard, Adobe Color), named by nearly every sidecar,
give no camera-profile warning; other profiles still do (test default_lightroom_profiles_are_not_reported).
The import help string keeps main's onDeleted wording and gains warnings.
On merge: the quick preview path orients like the whole-file path (preview_orientation with the
raw's header prefix, so a portrait raw whose embedded JPEG carries no orientation is not sideways),
and its doc links point at preview_window.
Turning on AI Denoise without a model opens Settings > AI Denoise and
keeps a pending request, so the switch showed checked and Settings
offered "Cancel pending action". Closing the setup with no model
download or installation running now drops that request: the switch
goes back off and nothing claims to be pending. A download started
before closing still turns AI Denoise on when it finishes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BKWB9TCnvzJHV6xMLAj2J4
File > Import Profiles & Presets... now builds one toast for both halves
(a second toast replaced the first, so a .cube-only import said "No new
presets"). The preset half is skipped when no preset files were picked,
and profile.import's failures are shown with their reason. profile.import
also reports .cube files and zips it couldn't read instead of dropping
them silently. The command's result carries the profile half under
"profiles".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BKWB9TCnvzJHV6xMLAj2J4
On merge: metadata.downloadPlaceNames accepts plain http only for the loopback address itself
(http://127.0.0.1.example.com/ and http://127.0.0.1@host/ are refused; test
download_base_is_https_or_loopback_only).