hMatoba / Piexif

Exif manipulation with pure python script.
MIT License
372 stars 83 forks source link

Crash related to this image #30

Closed jaddison closed 7 years ago

jaddison commented 7 years ago

I'm seeing a crash with the following image something related to saving a resized version of the image (and updating the exif info):

ice-cream-sandwiches-wedding-reception-food-truck original 1

Snippet of code that works for most images, but not the above:

        # ...
        if "exif" in image_info:
            exif = {}
            try:
                # image_info is the `info` from the above PILlow-open image
                exif = piexif.load(image_info["exif"])
            except Exception as e:
                logger.info("'piexif' loading error: %s", str(e))

            if exif:
                # update the exif dimensions as the width/height changed
                exif["Exif"][piexif.ExifIFD.PixelXDimension], exif["Exif"][piexif.ExifIFD.PixelYDimension] = image.size

                # blows up on this line
                options["info"]["exif"] = piexif.dump(exif)

Traceback:

Traceback (most recent call last):
  File "/Users/me/.virtualenvs/proj/lib/python3.5/site-packages/gunicorn/workers/async.py", line 52, in handle
    self.handle_request(listener_name, req, client, addr)
  File "/Users/me/.virtualenvs/proj/lib/python3.5/site-packages/gunicorn/workers/ggevent.py", line 152, in handle_request
    super(GeventWorker, self).handle_request(*args)
  File "/Users/me/.virtualenvs/proj/lib/python3.5/site-packages/gunicorn/workers/async.py", line 103, in handle_request
    respiter = self.wsgi(environ, resp.start_response)
  File "/Users/me/.virtualenvs/proj/lib/python3.5/site-packages/falcon/api.py", line 209, in __call__
    responder(req, resp, **params)
  File "/Users/me/projects/proj_server/proj_server/pipeline.py", line 269, in handler
    derivative = self.engine.process(data, operations, request_data)
  File "/Users/me/projects/proj_pil_engine/proj_pil_engine/engine.py", line 248, in process
    return self.image_processing_pool.apply(super(PILEngine, self).process, args=args, kwds=kwargs)
  File "/Users/me/.virtualenvs/proj/lib/python3.5/site-packages/gevent/pool.py", line 326, in apply
    return self.spawn(func, *args, **kwds).get()
  File "/Users/me/.virtualenvs/proj/lib/python3.5/site-packages/gevent/event.py", line 385, in get
    return self.get(block=False)
  File "/Users/me/.virtualenvs/proj/lib/python3.5/site-packages/gevent/event.py", line 375, in get
    return self._raise_exception()
  File "/Users/me/.virtualenvs/proj/lib/python3.5/site-packages/gevent/event.py", line 355, in _raise_exception
    reraise(*self.exc_info)
  File "/Users/me/.virtualenvs/proj/lib/python3.5/site-packages/gevent/_compat.py", line 33, in reraise
    raise value.with_traceback(tb)
  File "/Users/me/.virtualenvs/proj/lib/python3.5/site-packages/gevent/threadpool.py", line 211, in _worker
    value = func(*args, **kwargs)
  File "/Users/me/projects/proj_server/proj_server/core.py", line 167, in process
    image = self.operations[op_name].process(image, request_data, **op_params)
  File "/Users/me/projects/proj_pil_engine/proj_pil_engine/operations/resize.py", line 243, in process
    options["info"]["exif"] = piexif.dump(exif)
  File "/Users/me/.virtualenvs/proj/lib/python3.5/site-packages/piexif/_dump.py", line 93, in dump
    thumbnail = _get_thumbnail(exif_dict["thumbnail"])
  File "/Users/me/.virtualenvs/proj/lib/python3.5/site-packages/piexif/_dump.py", line 156, in _get_thumbnail
    segments = split_into_segments(jpeg)
  File "/Users/me/.virtualenvs/proj/lib/python3.5/site-packages/piexif/_common.py", line 8, in split_into_segments
    raise ValueError("Given data isn't JPEG.")
ValueError: Given data isn't JPEG.

After a quick debug, it looks like exif_dict["thumbnail"] is b"".

hMatoba commented 7 years ago

Thumbnail information is wrong. If a jpeg has thumbnail, it must have 1st IFD and JPEGInterchangeFormatLength in Exif IFD(exif["Exif"][piexif.ExifIFD.JPEGInterchangeFormatLength]) value. JPEGInterchangeFormatLength means byte size of thumbnail. Your image has thumbnail's information, but it shows thumbnail's size as byte is zero, so remove thumbnail's information.

del exif["1st"]
del exif["thumbnail"]
piexif.dump(exif)
jaddison commented 7 years ago

Shouldn't this kind of resiliency be built into this library?

Not everyone is going to know the intricacies of exif and all the special cases surrounding it?

hMatoba commented 7 years ago

It doesn't need to built in it into this library. In this case, zero byte data was passed and this library said "It's not JPEG". It's correct.

jaddison commented 7 years ago

I understand what you're saying, but there's no easy way for someone to know that was the problem.

Maybe the library could raise an InvalidEXIFThumbnailError exception instead. Or even simpler, change the error text to say "EXIF thumbnail is not JPEG". Then the user of the library could at least know what the issue is.

Also if this exception was mentioned in the docs and what to do on this situation - ie remove the thumbnail from the exif dictionary - then that would help users.

Would you like me to create a PR?

hMatoba commented 7 years ago

PullRequest welcome. I want simpler solution.

hMatoba commented 7 years ago

Thank you : )

jaddison commented 7 years ago

You're welcome!

jaddison commented 7 years ago

Are you able to push a new version to pypi?

satya-tsky commented 3 years ago

Thumbnail information is wrong. If a jpeg has thumbnail, it must have 1st IFD and JPEGInterchangeFormatLength in Exif IFD(exif["Exif"][piexif.ExifIFD.JPEGInterchangeFormatLength]) value. JPEGInterchangeFormatLength means byte size of thumbnail. Your image has thumbnail's information, but it shows thumbnail's size as byte is zero, so remove thumbnail's information.

del exif["1st"]
del exif["thumbnail"]
piexif.dump(exif)

I was getting the same error and this helped! Thanks! I ended up deleting all the keys in the exif dictionary other than those that I required

lakuhana commented 1 year ago

Thumbnail information is wrong. If a jpeg has thumbnail, it must have 1st IFD and JPEGInterchangeFormatLength in Exif IFD(exif["Exif"][piexif.ExifIFD.JPEGInterchangeFormatLength]) value. JPEGInterchangeFormatLength means byte size of thumbnail. Your image has thumbnail's information, but it shows thumbnail's size as byte is zero, so remove thumbnail's information.

del exif["1st"]
del exif["thumbnail"]
piexif.dump(exif)

This is the right solution! thanks