Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/PIL/IptcImagePlugin.py
diff options
context:
space:
mode:
Diffstat (limited to 'PIL/IptcImagePlugin.py')
-rw-r--r--PIL/IptcImagePlugin.py280
1 files changed, 280 insertions, 0 deletions
diff --git a/PIL/IptcImagePlugin.py b/PIL/IptcImagePlugin.py
new file mode 100644
index 0000000..3535759
--- /dev/null
+++ b/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