image.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. # Copyright (C) 2001-2006 Python Software Foundation
  2. # Author: Barry Warsaw
  3. # Contact: email-sig@python.org
  4. """Class representing image/* type MIME documents."""
  5. __all__ = ['MIMEImage']
  6. from email import encoders
  7. from email.mime.nonmultipart import MIMENonMultipart
  8. class MIMEImage(MIMENonMultipart):
  9. """Class for generating image/* type MIME documents."""
  10. def __init__(self, _imagedata, _subtype=None,
  11. _encoder=encoders.encode_base64, *, policy=None, **_params):
  12. """Create an image/* type MIME document.
  13. _imagedata contains the bytes for the raw image data. If the data
  14. type can be detected (jpeg, png, gif, tiff, rgb, pbm, pgm, ppm,
  15. rast, xbm, bmp, webp, and exr attempted), then the subtype will be
  16. automatically included in the Content-Type header. Otherwise, you can
  17. specify the specific image subtype via the _subtype parameter.
  18. _encoder is a function which will perform the actual encoding for
  19. transport of the image data. It takes one argument, which is this
  20. Image instance. It should use get_payload() and set_payload() to
  21. change the payload to the encoded form. It should also add any
  22. Content-Transfer-Encoding or other headers to the message as
  23. necessary. The default encoding is Base64.
  24. Any additional keyword arguments are passed to the base class
  25. constructor, which turns them into parameters on the Content-Type
  26. header.
  27. """
  28. _subtype = _what(_imagedata) if _subtype is None else _subtype
  29. if _subtype is None:
  30. raise TypeError('Could not guess image MIME subtype')
  31. MIMENonMultipart.__init__(self, 'image', _subtype, policy=policy,
  32. **_params)
  33. self.set_payload(_imagedata)
  34. _encoder(self)
  35. _rules = []
  36. # Originally from the imghdr module.
  37. def _what(data):
  38. for rule in _rules:
  39. if res := rule(data):
  40. return res
  41. else:
  42. return None
  43. def rule(rulefunc):
  44. _rules.append(rulefunc)
  45. return rulefunc
  46. @rule
  47. def _jpeg(h):
  48. """JPEG data with JFIF or Exif markers; and raw JPEG"""
  49. if h[6:10] in (b'JFIF', b'Exif'):
  50. return 'jpeg'
  51. elif h[:4] == b'\xff\xd8\xff\xdb':
  52. return 'jpeg'
  53. @rule
  54. def _png(h):
  55. if h.startswith(b'\211PNG\r\n\032\n'):
  56. return 'png'
  57. @rule
  58. def _gif(h):
  59. """GIF ('87 and '89 variants)"""
  60. if h[:6] in (b'GIF87a', b'GIF89a'):
  61. return 'gif'
  62. @rule
  63. def _tiff(h):
  64. """TIFF (can be in Motorola or Intel byte order)"""
  65. if h[:2] in (b'MM', b'II'):
  66. return 'tiff'
  67. @rule
  68. def _rgb(h):
  69. """SGI image library"""
  70. if h.startswith(b'\001\332'):
  71. return 'rgb'
  72. @rule
  73. def _pbm(h):
  74. """PBM (portable bitmap)"""
  75. if len(h) >= 3 and \
  76. h[0] == ord(b'P') and h[1] in b'14' and h[2] in b' \t\n\r':
  77. return 'pbm'
  78. @rule
  79. def _pgm(h):
  80. """PGM (portable graymap)"""
  81. if len(h) >= 3 and \
  82. h[0] == ord(b'P') and h[1] in b'25' and h[2] in b' \t\n\r':
  83. return 'pgm'
  84. @rule
  85. def _ppm(h):
  86. """PPM (portable pixmap)"""
  87. if len(h) >= 3 and \
  88. h[0] == ord(b'P') and h[1] in b'36' and h[2] in b' \t\n\r':
  89. return 'ppm'
  90. @rule
  91. def _rast(h):
  92. """Sun raster file"""
  93. if h.startswith(b'\x59\xA6\x6A\x95'):
  94. return 'rast'
  95. @rule
  96. def _xbm(h):
  97. """X bitmap (X10 or X11)"""
  98. if h.startswith(b'#define '):
  99. return 'xbm'
  100. @rule
  101. def _bmp(h):
  102. if h.startswith(b'BM'):
  103. return 'bmp'
  104. @rule
  105. def _webp(h):
  106. if h.startswith(b'RIFF') and h[8:12] == b'WEBP':
  107. return 'webp'
  108. @rule
  109. def _exr(h):
  110. if h.startswith(b'\x76\x2f\x31\x01'):
  111. return 'exr'