|
|
- #
- # 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.
- #
-
- from __future__ import print_function
-
- from . import Image, ImageFile
- from ._binary import i8, i16be as i16, i32be as i32, o8
- import os
- import tempfile
-
- __version__ = "0.3"
-
- 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 len(s):
- return None, 0
-
- tag = i8(s[1]), i8(s[2])
-
- # syntax
- if i8(s[0]) != 0x1C or tag[0] < 1 or tag[0] > 9:
- raise SyntaxError("invalid IPTC/NAA file")
-
- # field size
- size = i8(s[3])
- if size > 132:
- raise IOError("illegal field length in IPTC/NAA file")
- 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:
- raise IOError("Unknown IPTC image compression")
-
- # 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:
- _im = Image.open(outfile)
- _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.
- """
- from . import TiffImagePlugin, JpegImagePlugin
- import io
-
- data = None
-
- if isinstance(im, IptcImageFile):
- # return info dictionary right away
- return im.info
-
- elif isinstance(im, JpegImagePlugin.JpegImageFile):
- # extract the IPTC/NAA resource
- try:
- app = im.app["APP13"]
- if app[:14] == b"Photoshop 3.0\x00":
- app = app[14:]
- # parse the image resource block
- offset = 0
- while app[offset:offset+4] == b"8BIM":
- offset += 4
- # resource code
- code = i16(app, offset)
- offset += 2
- # resource name (usually empty)
- name_len = i8(app[offset])
- # name = app[offset+1:offset+1+name_len]
- offset = 1 + offset + name_len
- if offset & 1:
- offset += 1
- # resource data block
- size = i32(app, offset)
- offset += 4
- if code == 0x0404:
- # 0x0404 contains IPTC/NAA data
- data = app[offset:offset+size]
- break
- offset = offset + size
- if offset & 1:
- offset += 1
- except (AttributeError, KeyError):
- pass
-
- 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(object):
- 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
|