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.

255 lines
6.6 KiB

4 years ago
  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 print_function
  18. from . import Image, ImageFile
  19. from ._binary import i8, i16be as i16, i32be as i32, o8
  20. import os
  21. import tempfile
  22. __version__ = "0.3"
  23. COMPRESSION = {
  24. 1: "raw",
  25. 5: "jpeg"
  26. }
  27. PAD = o8(0) * 4
  28. #
  29. # Helpers
  30. def i(c):
  31. return i32((PAD + c)[-4:])
  32. def dump(c):
  33. for i in c:
  34. print("%02x" % i8(i), end=' ')
  35. print()
  36. ##
  37. # Image plugin for IPTC/NAA datastreams. To read IPTC/NAA fields
  38. # from TIFF and JPEG files, use the <b>getiptcinfo</b> function.
  39. class IptcImageFile(ImageFile.ImageFile):
  40. format = "IPTC"
  41. format_description = "IPTC/NAA"
  42. def getint(self, key):
  43. return i(self.info[key])
  44. def field(self):
  45. #
  46. # get a IPTC field header
  47. s = self.fp.read(5)
  48. if not len(s):
  49. return None, 0
  50. tag = i8(s[1]), i8(s[2])
  51. # syntax
  52. if i8(s[0]) != 0x1C or tag[0] < 1 or tag[0] > 9:
  53. raise SyntaxError("invalid IPTC/NAA file")
  54. # field size
  55. size = i8(s[3])
  56. if size > 132:
  57. raise IOError("illegal field length in IPTC/NAA file")
  58. elif size == 128:
  59. size = 0
  60. elif size > 128:
  61. size = i(self.fp.read(size-128))
  62. else:
  63. size = i16(s[3:])
  64. return tag, size
  65. def _open(self):
  66. # load descriptive fields
  67. while True:
  68. offset = self.fp.tell()
  69. tag, size = self.field()
  70. if not tag or tag == (8, 10):
  71. break
  72. if size:
  73. tagdata = self.fp.read(size)
  74. else:
  75. tagdata = None
  76. if tag in self.info:
  77. if isinstance(self.info[tag], list):
  78. self.info[tag].append(tagdata)
  79. else:
  80. self.info[tag] = [self.info[tag], tagdata]
  81. else:
  82. self.info[tag] = tagdata
  83. # mode
  84. layers = i8(self.info[(3, 60)][0])
  85. component = i8(self.info[(3, 60)][1])
  86. if (3, 65) in self.info:
  87. id = i8(self.info[(3, 65)][0])-1
  88. else:
  89. id = 0
  90. if layers == 1 and not component:
  91. self.mode = "L"
  92. elif layers == 3 and component:
  93. self.mode = "RGB"[id]
  94. elif layers == 4 and component:
  95. self.mode = "CMYK"[id]
  96. # size
  97. self._size = self.getint((3, 20)), self.getint((3, 30))
  98. # compression
  99. try:
  100. compression = COMPRESSION[self.getint((3, 120))]
  101. except KeyError:
  102. raise IOError("Unknown IPTC image compression")
  103. # tile
  104. if tag == (8, 10):
  105. self.tile = [("iptc", (compression, offset),
  106. (0, 0, self.size[0], self.size[1]))]
  107. def load(self):
  108. if len(self.tile) != 1 or self.tile[0][0] != "iptc":
  109. return ImageFile.ImageFile.load(self)
  110. type, tile, box = self.tile[0]
  111. encoding, offset = tile
  112. self.fp.seek(offset)
  113. # Copy image data to temporary file
  114. o_fd, outfile = tempfile.mkstemp(text=False)
  115. o = os.fdopen(o_fd)
  116. if encoding == "raw":
  117. # To simplify access to the extracted file,
  118. # prepend a PPM header
  119. o.write("P5\n%d %d\n255\n" % self.size)
  120. while True:
  121. type, size = self.field()
  122. if type != (8, 10):
  123. break
  124. while size > 0:
  125. s = self.fp.read(min(size, 8192))
  126. if not s:
  127. break
  128. o.write(s)
  129. size -= len(s)
  130. o.close()
  131. try:
  132. _im = Image.open(outfile)
  133. _im.load()
  134. self.im = _im.im
  135. finally:
  136. try:
  137. os.unlink(outfile)
  138. except OSError:
  139. pass
  140. Image.register_open(IptcImageFile.format, IptcImageFile)
  141. Image.register_extension(IptcImageFile.format, ".iim")
  142. def getiptcinfo(im):
  143. """
  144. Get IPTC information from TIFF, JPEG, or IPTC file.
  145. :param im: An image containing IPTC data.
  146. :returns: A dictionary containing IPTC information, or None if
  147. no IPTC information block was found.
  148. """
  149. from . import TiffImagePlugin, JpegImagePlugin
  150. import io
  151. data = None
  152. if isinstance(im, IptcImageFile):
  153. # return info dictionary right away
  154. return im.info
  155. elif isinstance(im, JpegImagePlugin.JpegImageFile):
  156. # extract the IPTC/NAA resource
  157. try:
  158. app = im.app["APP13"]
  159. if app[:14] == b"Photoshop 3.0\x00":
  160. app = app[14:]
  161. # parse the image resource block
  162. offset = 0
  163. while app[offset:offset+4] == b"8BIM":
  164. offset += 4
  165. # resource code
  166. code = i16(app, offset)
  167. offset += 2
  168. # resource name (usually empty)
  169. name_len = i8(app[offset])
  170. # name = app[offset+1:offset+1+name_len]
  171. offset = 1 + offset + name_len
  172. if offset & 1:
  173. offset += 1
  174. # resource data block
  175. size = i32(app, offset)
  176. offset += 4
  177. if code == 0x0404:
  178. # 0x0404 contains IPTC/NAA data
  179. data = app[offset:offset+size]
  180. break
  181. offset = offset + size
  182. if offset & 1:
  183. offset += 1
  184. except (AttributeError, KeyError):
  185. pass
  186. elif isinstance(im, TiffImagePlugin.TiffImageFile):
  187. # get raw data from the IPTC/NAA tag (PhotoShop tags the data
  188. # as 4-byte integers, so we cannot use the get method...)
  189. try:
  190. data = im.tag.tagdata[TiffImagePlugin.IPTC_NAA_CHUNK]
  191. except (AttributeError, KeyError):
  192. pass
  193. if data is None:
  194. return None # no properties
  195. # create an IptcImagePlugin object without initializing it
  196. class FakeImage(object):
  197. pass
  198. im = FakeImage()
  199. im.__class__ = IptcImageFile
  200. # parse the IPTC information chunk
  201. im.info = {}
  202. im.fp = io.BytesIO(data)
  203. try:
  204. im._open()
  205. except (IndexError, KeyError):
  206. pass # expected failure
  207. return im.info