IptcImagePlugin.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. #
  2. # The Python Imaging Library.
  3. # $Id$
  4. #
  5. # IPTC/NAA file handling
  6. #
  7. # history:
  8. # 1995-10-01 fl Created
  9. # 1998-03-09 fl Cleaned up and added to PIL
  10. # 2002-06-18 fl Added getiptcinfo helper
  11. #
  12. # Copyright (c) Secret Labs AB 1997-2002.
  13. # Copyright (c) Fredrik Lundh 1995.
  14. #
  15. # See the README file for information on usage and redistribution.
  16. #
  17. from __future__ import annotations
  18. from io import BytesIO
  19. from typing import Sequence
  20. from . import Image, ImageFile
  21. from ._binary import i16be as i16
  22. from ._binary import i32be as i32
  23. from ._deprecate import deprecate
  24. COMPRESSION = {1: "raw", 5: "jpeg"}
  25. def __getattr__(name: str) -> bytes:
  26. if name == "PAD":
  27. deprecate("IptcImagePlugin.PAD", 12)
  28. return b"\0\0\0\0"
  29. msg = f"module '{__name__}' has no attribute '{name}'"
  30. raise AttributeError(msg)
  31. #
  32. # Helpers
  33. def _i(c: bytes) -> int:
  34. return i32((b"\0\0\0\0" + c)[-4:])
  35. def _i8(c: int | bytes) -> int:
  36. return c if isinstance(c, int) else c[0]
  37. def i(c: bytes) -> int:
  38. """.. deprecated:: 10.2.0"""
  39. deprecate("IptcImagePlugin.i", 12)
  40. return _i(c)
  41. def dump(c: Sequence[int | bytes]) -> None:
  42. """.. deprecated:: 10.2.0"""
  43. deprecate("IptcImagePlugin.dump", 12)
  44. for i in c:
  45. print("%02x" % _i8(i), end=" ")
  46. print()
  47. ##
  48. # Image plugin for IPTC/NAA datastreams. To read IPTC/NAA fields
  49. # from TIFF and JPEG files, use the <b>getiptcinfo</b> function.
  50. class IptcImageFile(ImageFile.ImageFile):
  51. format = "IPTC"
  52. format_description = "IPTC/NAA"
  53. def getint(self, key: tuple[int, int]) -> int:
  54. return _i(self.info[key])
  55. def field(self) -> tuple[tuple[int, int] | None, int]:
  56. #
  57. # get a IPTC field header
  58. s = self.fp.read(5)
  59. if not s.strip(b"\x00"):
  60. return None, 0
  61. tag = s[1], s[2]
  62. # syntax
  63. if s[0] != 0x1C or tag[0] not in [1, 2, 3, 4, 5, 6, 7, 8, 9, 240]:
  64. msg = "invalid IPTC/NAA file"
  65. raise SyntaxError(msg)
  66. # field size
  67. size = s[3]
  68. if size > 132:
  69. msg = "illegal field length in IPTC/NAA file"
  70. raise OSError(msg)
  71. elif size == 128:
  72. size = 0
  73. elif size > 128:
  74. size = _i(self.fp.read(size - 128))
  75. else:
  76. size = i16(s, 3)
  77. return tag, size
  78. def _open(self) -> None:
  79. # load descriptive fields
  80. while True:
  81. offset = self.fp.tell()
  82. tag, size = self.field()
  83. if not tag or tag == (8, 10):
  84. break
  85. if size:
  86. tagdata = self.fp.read(size)
  87. else:
  88. tagdata = None
  89. if tag in self.info:
  90. if isinstance(self.info[tag], list):
  91. self.info[tag].append(tagdata)
  92. else:
  93. self.info[tag] = [self.info[tag], tagdata]
  94. else:
  95. self.info[tag] = tagdata
  96. # mode
  97. layers = self.info[(3, 60)][0]
  98. component = self.info[(3, 60)][1]
  99. if (3, 65) in self.info:
  100. id = self.info[(3, 65)][0] - 1
  101. else:
  102. id = 0
  103. if layers == 1 and not component:
  104. self._mode = "L"
  105. elif layers == 3 and component:
  106. self._mode = "RGB"[id]
  107. elif layers == 4 and component:
  108. self._mode = "CMYK"[id]
  109. # size
  110. self._size = self.getint((3, 20)), self.getint((3, 30))
  111. # compression
  112. try:
  113. compression = COMPRESSION[self.getint((3, 120))]
  114. except KeyError as e:
  115. msg = "Unknown IPTC image compression"
  116. raise OSError(msg) from e
  117. # tile
  118. if tag == (8, 10):
  119. self.tile = [("iptc", (0, 0) + self.size, offset, compression)]
  120. def load(self):
  121. if len(self.tile) != 1 or self.tile[0][0] != "iptc":
  122. return ImageFile.ImageFile.load(self)
  123. offset, compression = self.tile[0][2:]
  124. self.fp.seek(offset)
  125. # Copy image data to temporary file
  126. o = BytesIO()
  127. if compression == "raw":
  128. # To simplify access to the extracted file,
  129. # prepend a PPM header
  130. o.write(b"P5\n%d %d\n255\n" % self.size)
  131. while True:
  132. type, size = self.field()
  133. if type != (8, 10):
  134. break
  135. while size > 0:
  136. s = self.fp.read(min(size, 8192))
  137. if not s:
  138. break
  139. o.write(s)
  140. size -= len(s)
  141. with Image.open(o) as _im:
  142. _im.load()
  143. self.im = _im.im
  144. Image.register_open(IptcImageFile.format, IptcImageFile)
  145. Image.register_extension(IptcImageFile.format, ".iim")
  146. def getiptcinfo(im):
  147. """
  148. Get IPTC information from TIFF, JPEG, or IPTC file.
  149. :param im: An image containing IPTC data.
  150. :returns: A dictionary containing IPTC information, or None if
  151. no IPTC information block was found.
  152. """
  153. from . import JpegImagePlugin, TiffImagePlugin
  154. data = None
  155. if isinstance(im, IptcImageFile):
  156. # return info dictionary right away
  157. return im.info
  158. elif isinstance(im, JpegImagePlugin.JpegImageFile):
  159. # extract the IPTC/NAA resource
  160. photoshop = im.info.get("photoshop")
  161. if photoshop:
  162. data = photoshop.get(0x0404)
  163. elif isinstance(im, TiffImagePlugin.TiffImageFile):
  164. # get raw data from the IPTC/NAA tag (PhotoShop tags the data
  165. # as 4-byte integers, so we cannot use the get method...)
  166. try:
  167. data = im.tag.tagdata[TiffImagePlugin.IPTC_NAA_CHUNK]
  168. except (AttributeError, KeyError):
  169. pass
  170. if data is None:
  171. return None # no properties
  172. # create an IptcImagePlugin object without initializing it
  173. class FakeImage:
  174. pass
  175. im = FakeImage()
  176. im.__class__ = IptcImageFile
  177. # parse the IPTC information chunk
  178. im.info = {}
  179. im.fp = BytesIO(data)
  180. try:
  181. im._open()
  182. except (IndexError, KeyError):
  183. pass # expected failure
  184. return im.info