123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230 |
- #
- # The Python Imaging Library.
- # $Id$
- #
- # IPTC/NAA file handling
- #
- # history:
- # 1995-10-01 fl Created
- # 1998-03-09 fl Cleaned up and added to PIL
- # 2002-06-18 fl Added getiptcinfo helper
- #
- # Copyright (c) Secret Labs AB 1997-2002.
- # Copyright (c) Fredrik Lundh 1995.
- #
- # See the README file for information on usage and redistribution.
- #
- import os
- import tempfile
- from . import Image, ImageFile
- from ._binary import i8
- from ._binary import i16be as i16
- from ._binary import i32be as i32
- from ._binary import o8
- COMPRESSION = {1: "raw", 5: "jpeg"}
- PAD = o8(0) * 4
- #
- # Helpers
- def i(c):
- return i32((PAD + c)[-4:])
- def dump(c):
- for i in c:
- print("%02x" % i8(i), end=" ")
- print()
- ##
- # Image plugin for IPTC/NAA datastreams. To read IPTC/NAA fields
- # from TIFF and JPEG files, use the <b>getiptcinfo</b> function.
- class IptcImageFile(ImageFile.ImageFile):
- format = "IPTC"
- format_description = "IPTC/NAA"
- def getint(self, key):
- return i(self.info[key])
- def field(self):
- #
- # get a IPTC field header
- s = self.fp.read(5)
- if not s.strip(b"\x00"):
- return None, 0
- tag = s[1], s[2]
- # syntax
- if s[0] != 0x1C or tag[0] not in [1, 2, 3, 4, 5, 6, 7, 8, 9, 240]:
- msg = "invalid IPTC/NAA file"
- raise SyntaxError(msg)
- # field size
- size = s[3]
- if size > 132:
- msg = "illegal field length in IPTC/NAA file"
- raise OSError(msg)
- elif size == 128:
- size = 0
- elif size > 128:
- size = i(self.fp.read(size - 128))
- else:
- size = i16(s, 3)
- return tag, size
- def _open(self):
- # load descriptive fields
- while True:
- offset = self.fp.tell()
- tag, size = self.field()
- if not tag or tag == (8, 10):
- break
- if size:
- tagdata = self.fp.read(size)
- else:
- tagdata = None
- if tag in self.info:
- if isinstance(self.info[tag], list):
- self.info[tag].append(tagdata)
- else:
- self.info[tag] = [self.info[tag], tagdata]
- else:
- self.info[tag] = tagdata
- # mode
- layers = i8(self.info[(3, 60)][0])
- component = i8(self.info[(3, 60)][1])
- if (3, 65) in self.info:
- id = i8(self.info[(3, 65)][0]) - 1
- else:
- id = 0
- if layers == 1 and not component:
- self._mode = "L"
- elif layers == 3 and component:
- self._mode = "RGB"[id]
- elif layers == 4 and component:
- self._mode = "CMYK"[id]
- # size
- self._size = self.getint((3, 20)), self.getint((3, 30))
- # compression
- try:
- compression = COMPRESSION[self.getint((3, 120))]
- except KeyError as e:
- msg = "Unknown IPTC image compression"
- raise OSError(msg) from e
- # tile
- if tag == (8, 10):
- self.tile = [
- ("iptc", (compression, offset), (0, 0, self.size[0], self.size[1]))
- ]
- def load(self):
- if len(self.tile) != 1 or self.tile[0][0] != "iptc":
- return ImageFile.ImageFile.load(self)
- type, tile, box = self.tile[0]
- encoding, offset = tile
- self.fp.seek(offset)
- # Copy image data to temporary file
- o_fd, outfile = tempfile.mkstemp(text=False)
- o = os.fdopen(o_fd)
- if encoding == "raw":
- # To simplify access to the extracted file,
- # prepend a PPM header
- o.write("P5\n%d %d\n255\n" % self.size)
- while True:
- type, size = self.field()
- if type != (8, 10):
- break
- while size > 0:
- s = self.fp.read(min(size, 8192))
- if not s:
- break
- o.write(s)
- size -= len(s)
- o.close()
- try:
- with Image.open(outfile) as _im:
- _im.load()
- self.im = _im.im
- finally:
- try:
- os.unlink(outfile)
- except OSError:
- pass
- Image.register_open(IptcImageFile.format, IptcImageFile)
- Image.register_extension(IptcImageFile.format, ".iim")
- def getiptcinfo(im):
- """
- Get IPTC information from TIFF, JPEG, or IPTC file.
- :param im: An image containing IPTC data.
- :returns: A dictionary containing IPTC information, or None if
- no IPTC information block was found.
- """
- import io
- from . import JpegImagePlugin, TiffImagePlugin
- data = None
- if isinstance(im, IptcImageFile):
- # return info dictionary right away
- return im.info
- elif isinstance(im, JpegImagePlugin.JpegImageFile):
- # extract the IPTC/NAA resource
- photoshop = im.info.get("photoshop")
- if photoshop:
- data = photoshop.get(0x0404)
- elif isinstance(im, TiffImagePlugin.TiffImageFile):
- # get raw data from the IPTC/NAA tag (PhotoShop tags the data
- # as 4-byte integers, so we cannot use the get method...)
- try:
- data = im.tag.tagdata[TiffImagePlugin.IPTC_NAA_CHUNK]
- except (AttributeError, KeyError):
- pass
- if data is None:
- return None # no properties
- # create an IptcImagePlugin object without initializing it
- class FakeImage:
- pass
- im = FakeImage()
- im.__class__ = IptcImageFile
- # parse the IPTC information chunk
- im.info = {}
- im.fp = io.BytesIO(data)
- try:
- im._open()
- except (IndexError, KeyError):
- pass # expected failure
- return im.info
|