Pillow/PIL/IptcImagePlugin.py
Jon Dufresne d244fbb299 Remove unnecessary calls to dict.keys()
iter(dict) is equivalent to iter(dict.keys()), so simply act on the dict
instead of adding the extra call.

Inspired by Lennart Regebro's PyCon 2017 presentation "Prehistoric
Patterns in Python". Available at:

https://www.youtube.com/watch?v=V5-JH23Vk0I
2017-05-28 09:55:38 -07:00

258 lines
6.6 KiB
Python

#
# 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
# print(tag, self.info[tag])
# 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