bigflood / dartexif

Dart package to decode Exif data from tiff, jpeg and heic files
https://pub.dev/packages/exif
MIT License
30 stars 29 forks source link

Support AVIF #42

Closed kennylevinsen closed 11 months ago

kennylevinsen commented 11 months ago

Apart from a bugfix to the heic code this simply adds a check for the avif header.

bigflood commented 11 months ago

Thank you!

could you please provide more detailed information about the bug you fixed, and if possible, the steps to reproduce it?

Additionally, it would be very helpful if you could include the relevant test files or test cases to verify the changes made.

kennylevinsen commented 11 months ago

could you please provide more detailed information about the bug you fixed, and if possible, the steps to reproduce it?

The bugfix is described briefly in the message for the first commit, which I edited slightly just now.

To find the EXIF region of a BMF file, one first reads the meta box. Inside there, the iinf box describes which metadata sections are present, and the iloc box has their location in the file.

Here is the beginning of the metadata section from an AVIF file of mine (generated with exiftool FILE -v5):

 + [Meta directory]
  | Handler (SubDirectory) -->
  | - Tag 'hdlr' (25 bytes):
  |     0030: 00 00 00 00 00 00 00 00 70 69 63 74 00 00 00 00 [........pict....]
  |     0040: 00 00 00 00 00 00 00 00 00                      [.........]
  | + [BinaryData directory, 25 bytes]
  | | HandlerClass = 
  | | - Tag 0x0004 (4 bytes, undef[4]):
  | |     0034: 00 00 00 00                                     [....]
  | | HandlerType = pict
  | | - Tag 0x0008 (4 bytes, undef[4]):
  | |     0038: 70 69 63 74                                     [pict]
  | | HandlerVendorID = 
  | | - Tag 0x000c (4 bytes, undef[4]):
  | |     003c: 00 00 00 00                                     [....]
  | | HandlerDescription = 
  | | - Tag 0x0018 (1 bytes, string[1]):
  | |     0048: 00                                              [.]
  | PrimaryItemReference = .
  | - Tag 'pitm' (6 bytes):
  |     0051: 00 00 00 00 00 01                               [......]
  | ItemLocation = D@.....9&.:..><.x...}
  | - Tag 'iloc' (62 bytes):
  |     005f: 00 00 00 00 44 40 00 03 00 01 00 00 00 00 01 99 [....D@..........]
  |     006f: 00 01 00 00 00 00 00 00 39 26 00 02 00 00 00 00 [........9&......]
  |     007f: 3a bf 00 01 00 00 00 00 00 00 3e 3c 00 03 00 00 [:.........><....]
  |     008f: 00 00 78 fb 00 01 00 00 00 00 00 00 0f 7d       [..x..........}]
  |   Item 1: const_meth= base=0x199 offset=0x0 len=0x3926
  |   Item 2: const_meth= base=0x3abf offset=0x0 len=0x3e3c
  |   Item 3: const_meth= base=0x78fb offset=0x0 len=0xf7d
  | ItemInformation (SubDirectory) -->
  | - Tag 'iinf' (90 bytes):
  |     00a5: 00 00 00 00 00 03 00 00 00 15 69 6e 66 65 02 00 [..........infe..]
  |     00b5: 00 00 00 01 00 00 61 76 30 31 00 00 00 00 15 69 [......av01.....i]
  |     00c5: 6e 66 65 02 00 00 01 00 02 00 00 45 78 69 66 00 [nfe........Exif.]
  |     00d5: 00 00 00 2a 69 6e 66 65 02 00 00 01 00 03 00 00 [...*infe........]
  |     00e5: 6d 69 6d 65 00 61 70 70 6c 69 63 61 74 69 6f 6e [mime.application]
  |     00f5: 2f 72 64 66 2b 78 6d 6c 00 00                   [/rdf+xml..]
  | + [ItemInformation directory]
  | | ItemInfoEntry = ..av01
  | | - Tag 'infe' (13 bytes):
  | |     00b3: 02 00 00 00 00 01 00 00 61 76 30 31 00          [........av01.]
  | |   Item 1: Type=av01 Name= ContentType=
  | | ItemInfoEntry = ...Exif
  | | - Tag 'infe' (13 bytes):
  | |     00c8: 02 00 00 01 00 02 00 00 45 78 69 66 00          [........Exif.]
  | |   Item 2: Type=Exif Name= ContentType=
  | | ItemInfoEntry = ...mimeapplication/rdf+xml
  | | - Tag 'infe' (34 bytes):
  | |     00dd: 02 00 00 01 00 03 00 00 6d 69 6d 65 00 61 70 70 [........mime.app]
  | |     00ed: 6c 69 63 61 74 69 6f 6e 2f 72 64 66 2b 78 6d 6c [lication/rdf+xml]
  | |     00fd: 00 00                                           [..]
  | |   Item 3: Type=mime Name= ContentType=application/rdf+xml

There are 3 sections in the iloc, and iinf shows us that "Exif" is the second one:

|   Item 2: const_meth= base=0x3abf offset=0x0 len=0x3e3c

A HEIC file from an iPhone has way more metadata entries. The Exif block in my file is item 53, so let's look at just that:

|   Item 53: const_meth=0 base=0x0 offset=0x5a9c len=0x85c

The key difference here is that the HEIC file is using the offset to indicate the location of each section with a 0 base, while the AVIF file is using the base with a 0 offset. The HEIC code in dartexif only used the offset, so for AVIF it was resetting the file position back to the start and failing parse.

I don't have a test where both are set, but their name suggests they should just be added together.

Additionally, it would be very helpful if you could include the relevant test files or test cases to verify the changes made.

I will have to see if I can generate a usable test image. I used family pictures for the testing, which I do not plan to upload. :)

kennylevinsen commented 11 months ago

I converted the existing HEIC test file to AVIF with vips (but in 240x240 to not waste more space) and added a small read_file test for it.