You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

images.py 2.5KB

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