Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

XMP <?xpacket> wrapper tags causes exception in ImageOps.exif_transpose() #8790

Closed
gbmerrall opened this issue Mar 3, 2025 · 1 comment · Fixed by #8792
Closed

XMP <?xpacket> wrapper tags causes exception in ImageOps.exif_transpose() #8790

gbmerrall opened this issue Mar 3, 2025 · 1 comment · Fixed by #8792

Comments

@gbmerrall
Copy link

gbmerrall commented Mar 3, 2025

What did you do?

When converting a TIF to a JPG, if the image contains XMP data that uses a <?xpacket> wrapper, conversion fails with an exception bubbled up from re module.

What did you expect to happen?

Conversion should happen

What actually happened?

Exception generated

TypeError ("expected string or bytes-like object, got 'tuple'",)
Traceback (most recent call last):
  File "<ipython-input-64-92aa2f62a20c>", line 5, in <module>
    jpeg_image = img.convert("RGB")
                 ^^^^^^^^^^^^^^^^^^
  File "/Users/graeme/.local/share/virtualenvs/sandbox-0NfWatfK/lib/python3.12/site-packages/PIL/Image.py", line 984, in convert
    self.load()
  File "/Users/graeme/.local/share/virtualenvs/sandbox-0NfWatfK/lib/python3.12/site-packages/PIL/TiffImagePlugin.py", line 1292, in load
    return self._load_libtiff()
           ^^^^^^^^^^^^^^^^^^^^
  File "/Users/graeme/.local/share/virtualenvs/sandbox-0NfWatfK/lib/python3.12/site-packages/PIL/TiffImagePlugin.py", line 1402, in _load_libtiff
    self.load_end()
  File "/Users/graeme/.local/share/virtualenvs/sandbox-0NfWatfK/lib/python3.12/site-packages/PIL/TiffImagePlugin.py", line 1314, in load_end
    ImageOps.exif_transpose(self, in_place=True)
  File "/Users/graeme/.local/share/virtualenvs/sandbox-0NfWatfK/lib/python3.12/site-packages/PIL/ImageOps.py", line 725, in exif_transpose
    else re.sub(pattern.encode(), b"", value)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/[email protected]/3.12.8/Frameworks/Python.framework/Versions/3.12/lib/python3.12/re/__init__.py", line 186, in sub
    return _compile(pattern, flags).sub(repl, string, count)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: expected string or bytes-like object, got 'tuple'

What are your OS, Python and Pillow versions?

  • OS: MacOS Sequoia
  • Python: 3.12.8
  • Pillow: 11.1.0 (via PIL.version)
% pipenv run python3 -m PIL.report
--------------------------------------------------------------------
Pillow 11.1.0
Python 3.12.8 (main, Dec  3 2024, 18:42:41) [Clang 16.0.0 (clang-1600.0.26.4)]
--------------------------------------------------------------------
Python executable is /Users/graeme/.local/share/virtualenvs/sandbox-0NfWatfK/bin/python3
Environment Python files loaded from /Users/graeme/.local/share/virtualenvs/sandbox-0NfWatfK
System Python files loaded from /opt/homebrew/opt/[email protected]/Frameworks/Python.framework/Versions/3.12
--------------------------------------------------------------------
Python Pillow modules loaded from /Users/graeme/.local/share/virtualenvs/sandbox-0NfWatfK/lib/python3.12/site-packages/PIL
Binary Pillow modules loaded from /Users/graeme/.local/share/virtualenvs/sandbox-0NfWatfK/lib/python3.12/site-packages/PIL
--------------------------------------------------------------------
--- PIL CORE support ok, compiled for 11.1.0
*** TKINTER support not installed
--- FREETYPE2 support ok, loaded 2.13.2
--- LITTLECMS2 support ok, loaded 2.16
--- WEBP support ok, loaded 1.5.0
--- JPEG support ok, compiled for libjpeg-turbo 3.1.0
--- OPENJPEG (JPEG2000) support ok, loaded 2.5.3
--- ZLIB (PNG/ZIP) support ok, loaded 1.3.1.zlib-ng, compiled for zlib-ng 2.2.2
--- LIBTIFF support ok, loaded 4.6.0
--- RAQM (Bidirectional Text) support ok, loaded 0.10.1, fribidi 1.0.16, harfbuzz 10.1.0
*** LIBIMAGEQUANT (Quantization method) support not installed
--- XCB (X protocol) support ok
--------------------------------------------------------------------

Reproduction steps

I encountered this issue on a Mac. I've been struggling to isolate a more independant example. It's probably me struggling with exiftool

I can't attach tif files. Please obtain from this Google Drive folder

  1. Download no_xmp.tif and xmp.tif and run the following
from PIL import Image
img = Image.open('xmp.tif')
new_img = img.convert("RGB")

Conversion should be successful

  1. Repeat with xmp_xpacket.tif. Exception will be produced

  2. We can break out the specific logic causing the exception from ImageOps.exif_transpose()

def test_xmp(img):
    if 'xmp' in img.info:
        problematic_value = img.info['xmp']
        pattern = r'tiff:Orientation="([0-9])'
        if isinstance(problematic_value, str):
            re.sub(pattern, "", problematic_value)
        else:
            if isinstance(problematic_value, tuple):
                print("Tuple found. Not good")
            else:
                print("No tuple found. Good")            
                re.sub(pattern.encode(), b"", problematic_value)
    else:
        print("Didn't find xmp info")

img_noxmp = Image.open("no_xmp.tif")
test_xml(img_noxmp)

Will return "Didn't find xmp info"

img_xmp = Image.open("xmp.tif")
test_xml(img_xmp)

Will return "No tuple found. Good"

Finally

img_xpacket = Image.open("xmp_xpacket.tif")
test_xml(img_xpacket)

Will return "Tuple found. Not good"

  1. To preview the XMP in these images run exiftool -XMP -b xmp_xpacket.tif and you'll observe the <?xpacket> field

How did xpacket get in there?

I discovered that this is due to image rotation on a Mac. Specifically Quick Actions vs Preview app

  1. Start with no_xmp.tif. Run exiftool -XMP -b no_xmp.tif
  2. Open 'Preview' and rotate the image (cmd-L for shortcut)
  3. Run exiftool again and observe some XMP data has been added to the image
  4. In the Mac finder, select 'no_xmp.tif' and either
    • Tap space to open quicklook and press cmd-L, or click the 'rotate left' button
    • or right click on the image and select "Quick Actions -> Rotate left'

This will insert the xpacket tag

Summary

So one could say there's an inconsistency in Mac on how XMP fragments are added depending on how you rotate the image.

Looking at part 1 of the XMP spec, specifically section 7.3.2 it does appear that the XMP is valid if those xpacket wrappers are present.

I suspect that ImageOps.exif_transpose() needs to be adjusted

@radarhere
Copy link
Member

I've created #8792 to resolve this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants