hMatoba / piexifjs

Read and modify exif in client-side or server-side JavaScript.
MIT License
573 stars 118 forks source link

Not writing offset of next IFD #70

Open CampbellMG opened 5 years ago

CampbellMG commented 5 years ago

What should be happening:

As per the TIFF documentation, each IFD should end with:

OFFSET - Count TYPE - Description 0002h + "NUM"*12 - 1 dword - Offset of next IFD in file, 0 if none follow

The key part here is the 0 if none follow.

What is actually happening:

Piexifjs is not writing this value at all meaning several EXIF parsers (such as PHP's Pel library) will attempt to read the "four bytes over" information as an offset for the next IFD.

To reproduce the issue:

  1. Load, dump and insert EXIF for any image with GPS EXIF
  2. Open the image with PHP's Pel library, in my case I receive the following error:
#0 vendor\lsolesen\pel\src\PelDataWindow.php(424): lsolesen\pel\PelDataWindow->validateOffset(-277)
#1 vendor\lsolesen\pel\src\PelIfd.php(272): lsolesen\pel\PelDataWindow->getLong(-277)
#2 vendor\lsolesen\pel\src\PelIfd.php(285): lsolesen\pel\PelIfd->load(Object(lsolesen\pel\PelDataWindow), 35)
#3 vendor\lsolesen\pel\src\PelIfd.php(221): lsolesen\pel\PelIfd->load(Object(lsolesen\pel\PelDataWindow), 226)
#4 vendor\lsolesen\pel\src\PelTiff.php(159): lsolesen\pel\PelIfd->load(Object(lsolesen\pel\PelDataWindow), 10)
#5 vendor\lsolesen\pel\src\PelExif.php(108): lsolesen\pel\PelTiff->load(Object(lsolesen\pel\PelDataWindow))
#6 vendor\lsolesen\pel\src\PelJpeg.php(216): lsolesen\pel\PelExif->load(Object(lsolesen\pel\PelDataWindow))
#7 vendor\lsolesen\pel\src\PelJpeg.php(286): lsolesen\pel\PelJpeg->load(Object(lsolesen\pel\PelDataWindow))
#8 vendor\lsolesen\pel\src\PelJpeg.php(123): lsolesen\pel\PelJpeg->loadFile('...')

Alternatively, you can compare the raw binary before and after parsing the image with piexifjs and you will notice it strips out the 00 00 00 00 that separates the entries (key, type, length & value) from the overflow values (greater than 4 bytes).

I have implemented a temporary fix with the following:

function _dict_to_bytes(ifd_dict, ifd, ifd_offset) {
        var TIFF_HEADER_LENGTH = 8;
        var tag_count = Object.keys(ifd_dict).length;
        var entry_header = pack(">H", [tag_count]);
        var entries_length;
        if (["0th", "1st"].indexOf(ifd) > -1) {
            entries_length = 2 + tag_count * 12 + 4;
        } else {
            entries_length = 2 + tag_count * 12;
        }
        var entries = "";
        var values = "\x00\x00\x00\x00"; // Updated this line from an empty string
        var key;

However, this adds two trailing zeros after the 0th IFD as the library seems to be including this value only on the first.

I am very new to TIFF and EXIF standards so take all of this with a grain of salt. Is there a more permanent solution to this issue?

hanoncs commented 4 years ago

Thank you so much! I'm shrinking images clientside and needed to clone the EXIF data to the shrunk image, this bug was causing PHP issues and not allowing me to read the GPS data. This fixed my issue, I know its not a perfect solution but thank you so much!