images.py 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. """
  2. Utility functions for handling images.
  3. Requires Pillow (or PIL), as you might imagine.
  4. """
  5. import zlib
  6. from django.core.files import File
  7. class ImageFile(File):
  8. """
  9. A mixin for use alongside django.core.files.base.File, which provides
  10. additional features for dealing with images.
  11. """
  12. def _get_width(self):
  13. return self._get_image_dimensions()[0]
  14. width = property(_get_width)
  15. def _get_height(self):
  16. return self._get_image_dimensions()[1]
  17. height = property(_get_height)
  18. def _get_image_dimensions(self):
  19. if not hasattr(self, '_dimensions_cache'):
  20. close = self.closed
  21. self.open()
  22. self._dimensions_cache = get_image_dimensions(self, close=close)
  23. return self._dimensions_cache
  24. def get_image_dimensions(file_or_path, close=False):
  25. """
  26. Returns the (width, height) of an image, given an open file or a path. Set
  27. 'close' to True to close the file at the end if it is initially in an open
  28. state.
  29. """
  30. from django.utils.image import ImageFile as PILImageFile
  31. p = PILImageFile.Parser()
  32. if hasattr(file_or_path, 'read'):
  33. file = file_or_path
  34. file_pos = file.tell()
  35. file.seek(0)
  36. else:
  37. file = open(file_or_path, 'rb')
  38. close = True
  39. try:
  40. # Most of the time PIL only needs a small chunk to parse the image and
  41. # get the dimensions, but with some TIFF files PIL needs to parse the
  42. # whole file.
  43. chunk_size = 1024
  44. while 1:
  45. data = file.read(chunk_size)
  46. if not data:
  47. break
  48. try:
  49. p.feed(data)
  50. except zlib.error as e:
  51. # ignore zlib complaining on truncated stream, just feed more
  52. # data to parser (ticket #19457).
  53. if e.args[0].startswith("Error -5"):
  54. pass
  55. else:
  56. raise
  57. if p.image:
  58. return p.image.size
  59. chunk_size *= 2
  60. return None
  61. finally:
  62. if close:
  63. file.close()
  64. else:
  65. file.seek(file_pos)