Thanks to visit codestin.com
Credit goes to github.com

Skip to content

HEIC parser rejects iOS 18 adaptive-HDR files due to ftyp size > 50 check #138

@notoriouslab

Description

@notoriouslab

Summary

HeifFileParser.canHandle() rejects any file whose ftyp box is larger than 50 bytes. iOS 18 writes a 52-byte ftyp on adaptive-HDR HEIC photos (e.g. from iPhone 15 Pro), so exifr no longer recognizes them as HEIC even though the EXIF payload is completely intact.

Repro

  1. Take a photo on an iPhone 15 Pro (or any iOS 18 device with HDR on).
  2. const result = await exifr.parse(file, { gps: true })
  3. Expected: { latitude, longitude, DateTimeOriginal, ... }
  4. Actual: no HeifFileParser.canHandle() returns true, so exifr falls through all parsers and the caller ends up with "Unknown file format" / empty metadata.

Root cause

In src/file-parsers/heif.mjs:

let ftypLength = file.getUint16(2)
if (ftypLength > 50) return false

iOS 17 and earlier HEIC files have a ftyp of around 28 bytes, so the heuristic was safe. iOS 18 adds compatible brands to signal adaptive-HDR tone-mapping (notably tmap, plus heic hevc unif on top of the usual heic mif1 miaf MiHB), which pushes the ftyp box to 52 bytes.

Example byte layout

iPhone 14 / iOS 17:   ftyp size=28   brands: heic mif1 miaf MiHB
iPhone 15 Pro / 18:   ftyp size=52   brands: heic mif1 miaf MiHB
                                             tmap heic hevc unif

Proposed fix

Three options, in increasing invasiveness:

  1. Raise the ceiling to something that still rejects obviously-bogus files but tolerates current and near-future Apple output — e.g. 128 or 256 bytes.
  2. Remove the length cap entirely and let the major_brand / compatible-brand check in the following step do the work.
  3. Derive the cap from the number of brands observed rather than a fixed byte count.

Option (1) is the smallest change and would unblock iOS 18 without touching the rest of the canHandle logic. Happy to send a PR if you have a preference.

Impact

Every iOS 18 HEIC photo from iPhones with adaptive HDR on (iPhone 15 Pro and later shoot adaptive HDR by default). The rejection is silent from the caller's perspective — exifr just returns nothing — so this is hard to diagnose without reading the parser source.

Workaround I'm currently using

In TrailPaint I keep exifr as the primary parser and dynamically import exifreader as a fallback only when exifr returns no GPS and no metadata on a HEIC file. Short write-up. Upstream fix would let me drop the ~34 KB fallback for most users.
https://jacobmei.com/blog/2026/0421-2yh1pu/
https://jacobmei.com/blog/2026/0421-49ofw6/

Thanks for exifr — it's been the default EXIF reader in my projects for years.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions