Open Knio opened 3 hours ago
Broken case: Exif.load_from_fp
called after _load_libtiff
Good case: Exif loaded before, Image.transpose gets called
Also, I think this explains my confusion in the other thread about not seeing exif tags sometimes
Pillow: 11.1.0.dev0
Could you be more specific about which commit you're building from? Does this happen with the released Pillow 11.0.0?
What did you do?
Load a TIFF with exif rotation info and save as JPG
What did you expect to happen?
Save image
What actually happened?
save() crashes
What are your OS, Python and Pillow versions?
Linux dev 6.8.12-2-pve #1 SMP PREEMPT_DYNAMIC PMX 6.8.12-2 (2024-09-05T10:03Z) x86_64 GNU/Linux
Debugging Notes
I have debugged this, and determined it's caused by:
self.fp: _io.BufferedReader
is corrupted after giving it to libtiffdecode.decode()
load_end()
callsexif_transpose()
callsgetexif()
callsImageFileDirectory_v2.load
fp
is corrupt, no valid tags are read. you can see this in the logs of many unknown tagsImage.transpose()
is not called inload_end()
Workarounds
I've come up with a handful of workarounds:
1) Don't buffer
The buffer can't be corrupted if there isn't one.
2) Pre-read exif tags
This calls
ImageFileDirectory_v2.load
on a virgin fp, so it's not corrupt. Subsequent calls in Image.load are cached.3) Poke the buffer
Add after here https://github.com/python-pillow/Pillow/blob/main/src/PIL/TiffImagePlugin.py#L1385
This gets the buffer to drop its cache. I got the idea from comments and similar code here: https://github.com/python-pillow/Pillow/commit/fd299e36cec4c12a0f201dbbbc1014dfaca7005a
However, I don't think this is a safe idea. This is not a documented behavior and the buffering/caching behavior of this could change at any time. When I tried with just
seek(0)
, it did not help, and looking at the CPython code, it looks like seek() might be optimized if it's a location that's already buffered and won't actually move the file handle.pillow.zip