Opened 3 years ago

Closed 2 weeks ago

Last modified 10 days ago

#33240 closed Bug (needsinfo)

get_image_dimensions() raises ValueError on some .ico files.

Reported by: NKSM Owned by: Ath Tripathi
Component: File uploads/storage Version: 2.2
Severity: Normal Keywords: pillow
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by NKSM)

When I try to get the dimensions of the image like below:

def clean_image(self):
   image = self.cleaned_data.get("image")
   if not image:
       raise forms.ValidationError("No image!")
   else:
       w, h = get_image_dimensions(image)
       if w != 100:
           raise forms.ValidationError("The image is %i pixel wide. It's supposed to be 100px" % w)
       if h != 200:
           raise forms.ValidationError("The image is %i pixel high. It's supposed to be 200px" % h)
   return image

I get : ValueError: buffer is not large enough

Error at line: https://github.com/django/django/blob/073b7b5915fdfb89a144e81173176ee13ff92a25/django/core/files/images.py#L62

# Most of the time Pillow only needs a small chunk to parse the image
# and get the dimensions, but with some TIFF files Pillow needs to
# parse the whole file.
chunk_size = 1024
while 1:
    data = file.read(chunk_size)
    if not data:
        break
    try:
        p.feed(data) ⬅️ **HERE**
    except zlib.error as e:

Exception Location: /PIL/Image.py in frombuffer, line 2605

This works well:

from PIL import Image as PillowImage
width, height = PillowImage.open(image).size

Attachments (1)

favicon.ico (1.1 KB ) - added by NKSM 3 years ago.
File to uplad

Download all attachments as: .zip

Change History (8)

by NKSM, 3 years ago

Attachment: favicon.ico added

File to uplad

comment:1 by NKSM, 3 years ago

Description: modified (diff)

comment:2 by Ath Tripathi, 3 years ago

Owner: changed from nobody to Ath Tripathi
Status: newassigned

comment:3 by Mariusz Felisiak, 3 years ago

Keywords: pillow added
Resolution: needsinfo
Status: assignedclosed

Thanks for this report, however it looks like an issue in Pillow not in Django itself. We use ImageFile.Parser() as documented.

First, I would try to report it in the Pillow issue tracker . Please reopen the ticket if you can debug your issue and provide details about why and where Django is at fault.

comment:4 by Mariusz Felisiak, 3 years ago

Summary: get_image_dimensions: ValueError: buffer is not large enoughget_image_dimensions() raises ValueError on some .ico files.

comment:5 by Matthias Kestenholz, 2 weeks ago

Resolution: needsinfo
Status: closednew

I have the same issue with Pillow 11.0.

The trouble is that Pillow's TIFF parser now raises a ValueError when attempting to determine the dimensions of this image here:
https://github.com/matthiask/django-imagefield/blob/main/tests/testapp/media/python-logo.tiff

This produces the following exception:

 ======================================================================
ERROR: test_websafe_versatileimageproxy (testapp.test_imagefield.Test.test_websafe_versatileimageproxy)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/runner/work/django-imagefield/django-imagefield/.tox/py312-djmain/lib/python3.12/site-packages/django/test/utils.py", line 446, in inner
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/django-imagefield/django-imagefield/tests/testapp/test_imagefield.py", line 401, in test_websafe_versatileimageproxy
    m = WebsafeImage.objects.create(image="python-logo.tiff")
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/django-imagefield/django-imagefield/.tox/py312-djmain/lib/python3.12/site-packages/django/db/models/manager.py", line 87, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/django-imagefield/django-imagefield/.tox/py312-djmain/lib/python3.12/site-packages/django/db/models/query.py", line 658, in create
    obj = self.model(**kwargs)
          ^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/django-imagefield/django-imagefield/.tox/py312-djmain/lib/python3.12/site-packages/django/db/models/base.py", line 572, in __init__
    post_init.send(sender=cls, instance=self)
  File "/home/runner/work/django-imagefield/django-imagefield/.tox/py312-djmain/lib/python3.12/site-packages/django/dispatch/dispatcher.py", line 189, in send
    response = receiver(signal=self, sender=sender, **named)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/django-imagefield/django-imagefield/.tox/py312-djmain/lib/python3.12/site-packages/django/db/models/fields/files.py", line 519, in update_dimension_fields
    width = file.width
            ^^^^^^^^^^
  File "/home/runner/work/django-imagefield/django-imagefield/.tox/py312-djmain/lib/python3.12/site-packages/django/core/files/images.py", line 21, in width
    return self._get_image_dimensions()[0]
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/django-imagefield/django-imagefield/.tox/py312-djmain/lib/python3.12/site-packages/django/core/files/images.py", line 31, in _get_image_dimensions
    self._dimensions_cache = get_image_dimensions(self, close=close)
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/django-imagefield/django-imagefield/.tox/py312-djmain/lib/python3.12/site-packages/django/core/files/images.py", line 64, in get_image_dimensions
    p.feed(data)
  File "/home/runner/work/django-imagefield/django-imagefield/.tox/py312-djmain/lib/python3.12/site-packages/PIL/ImageFile.py", line 471, in feed
    im = Image.open(fp)
         ^^^^^^^^^^^^^^
  File "/home/runner/work/django-imagefield/django-imagefield/.tox/py312-djmain/lib/python3.12/site-packages/PIL/Image.py", line 3515, in open
    im = _open_core(fp, filename, prefix, formats)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/django-imagefield/django-imagefield/.tox/py312-djmain/lib/python3.12/site-packages/PIL/Image.py", line 3503, in _open_core
    im = factory(fp, filename)
         ^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/django-imagefield/django-imagefield/.tox/py312-djmain/lib/python3.12/site-packages/PIL/TiffImagePlugin.py", line 1153, in __init__
    super().__init__(fp, filename)
  File "/home/runner/work/django-imagefield/django-imagefield/.tox/py312-djmain/lib/python3.12/site-packages/PIL/ImageFile.py", line 144, in __init__
    self._open()
  File "/home/runner/work/django-imagefield/django-imagefield/.tox/py312-djmain/lib/python3.12/site-packages/PIL/TiffImagePlugin.py", line 1177, in _open
    self._seek(0)
  File "/home/runner/work/django-imagefield/django-imagefield/.tox/py312-djmain/lib/python3.12/site-packages/PIL/TiffImagePlugin.py", line 1248, in _seek
    self._setup()
  File "/home/runner/work/django-imagefield/django-imagefield/.tox/py312-djmain/lib/python3.12/site-packages/PIL/TiffImagePlugin.py", line 1426, in _setup
    raise ValueError(msg)
ValueError: Invalid dimensions

If I modify get_image_dimensions to also ignore ValueError exceptions and try again with a larger chunk size the tests pass.

So, it's not exactly a small self-contained example but the following shows the breakage:

git clone https://github.com/matthiask/django-imagefield.git
cd django-imagefield
tox -e py312-dj51

Downgrading to Pillow<11 works also, but since opening the file with PIL.Image works I'd argue it's not really a Pillow issue, but an issue with get_image_dimensions wanting to do interesting* things with partially available image files. The variety of exceptions which are ignored already show that the code tries to guard against various problems, but it's incomplete.

I don't really have a good idea how to work around this issue since the exception raised by Pillow is so unspecific.

*: I know the reasons and they make sense to me.

comment:6 by Sarah Boyce, 2 weeks ago

Resolution: needsinfo
Status: newclosed

The error looks like a new error raised after type hints were added to Pillow 11 see: https://github.com/python-pillow/Pillow/commit/e6e5ef5c5fbd83ac5dd63301e4d7d6860a7b2d09
I agree with Mariusz here, what we are doing is in the Pillow docs and so should be supported by Pillow (rather than we are doing anything too interesting/exotic here) - I think first raise an issue to Pillow and see what they say

comment:7 by Sarah Boyce, 10 days ago

See #35884 which was marked as a duplicate

There is an issue raised against Pillow. I have commented there how the error is raised following their documented example: https://github.com/python-pillow/Pillow/issues/8530#issuecomment-2456436638. We will wait for the response to that before pursuing a change in Django.

Also, thank you to Matthais for sharing a file I could test with and comment on the issue

Note: See TracTickets for help on using tickets.
Back to Top