diff options
Diffstat (limited to 'Imaging/PIL/IptcImagePlugin.py')
-rw-r--r-- | Imaging/PIL/IptcImagePlugin.py | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/Imaging/PIL/IptcImagePlugin.py b/Imaging/PIL/IptcImagePlugin.py new file mode 100644 index 0000000..3535759 --- /dev/null +++ b/Imaging/PIL/IptcImagePlugin.py @@ -0,0 +1,280 @@ +# +# The Python Imaging Library. +# $Id: IptcImagePlugin.py 2813 2006-10-07 10:11:35Z fredrik $ +# +# 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. +# + + +__version__ = "0.3" + + +import Image, ImageFile +import os, tempfile + + +COMPRESSION = { + 1: "raw", + 5: "jpeg" +} + +PAD = chr(0) * 4 + +# +# Helpers + +def i16(c): + return ord(c[1]) + (ord(c[0])<<8) + +def i32(c): + return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24) + +def i(c): + return i32((PAD + c)[-4:]) + +def dump(c): + for i in c: + print "%02x" % ord(i), + 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 = ord(s[1]), ord(s[2]) + + # syntax + if ord(s[0]) != 0x1C or tag[0] < 1 or tag[0] > 9: + raise SyntaxError, "invalid IPTC/NAA file" + + # field size + size = ord(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 _is_raw(self, offset, size): + # + # check if the file can be mapped + + # DISABLED: the following only slows things down... + return 0 + + self.fp.seek(offset) + t, sz = self.field() + if sz != size[0]: + return 0 + y = 1 + while 1: + self.fp.seek(sz, 1) + t, s = self.field() + if t != (8, 10): + break + if s != sz: + return 0 + y = y + 1 + return y == size[1] + + def _open(self): + + # load descriptive fields + while 1: + offset = self.fp.tell() + tag, size = self.field() + if not tag or tag == (8,10): + break + if size: + self.info[tag] = self.fp.read(size) + else: + self.info[tag] = None + # print tag, self.info[tag] + + # mode + layers = ord(self.info[(3,60)][0]) + component = ord(self.info[(3,60)][1]) + if self.info.has_key((3,65)): + id = ord(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): + if compression == "raw" and self._is_raw(offset, self.size): + self.tile = [(compression, (offset, size + 5, -1), + (0, 0, self.size[0], self.size[1]))] + else: + 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 + outfile = tempfile.mktemp() + o = open(outfile, "wb") + 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 1: + 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 = size - len(s) + o.close() + + try: + try: + # fast + self.im = Image.core.open_ppm(outfile) + except: + # slightly slower + im = Image.open(outfile) + im.load() + self.im = im.im + finally: + try: os.unlink(outfile) + except: pass + + +Image.register_open("IPTC", IptcImageFile) + +Image.register_extension("IPTC", ".iim") + +## +# Get IPTC information from TIFF, JPEG, or IPTC file. +# +# @param im An image containing IPTC data. +# @return A dictionary containing IPTC information, or None if +# no IPTC information block was found. + +def getiptcinfo(im): + + import TiffImagePlugin, JpegImagePlugin + import StringIO + + 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] == "Photoshop 3.0\x00": + app = app[14:] + # parse the image resource block + offset = 0 + while app[offset:offset+4] == "8BIM": + offset = offset + 4 + # resource code + code = JpegImagePlugin.i16(app, offset) + offset = offset + 2 + # resource name (usually empty) + name_len = ord(app[offset]) + name = app[offset+1:offset+1+name_len] + offset = 1 + offset + name_len + if offset & 1: + offset = offset + 1 + # resource data block + size = JpegImagePlugin.i32(app, offset) + offset = offset + 4 + if code == 0x0404: + # 0x0404 contains IPTC/NAA data + data = app[offset:offset+size] + break + offset = offset + size + if offset & 1: + offset = 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: + type, 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 = StringIO.StringIO(data) + + try: + im._open() + except (IndexError, KeyError): + pass # expected failure + + return im.info |