2010-07-31 06:52:47 +04:00
|
|
|
#
|
|
|
|
# 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.
|
|
|
|
#
|
2014-07-30 20:39:11 +04:00
|
|
|
import os
|
|
|
|
import tempfile
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2019-07-06 23:40:53 +03:00
|
|
|
from . import Image, ImageFile
|
2020-08-07 13:28:33 +03:00
|
|
|
from ._binary import i8
|
|
|
|
from ._binary import i16be as i16
|
|
|
|
from ._binary import i32be as i32
|
|
|
|
from ._binary import o8
|
2019-07-06 23:40:53 +03:00
|
|
|
|
2019-03-21 16:28:20 +03:00
|
|
|
COMPRESSION = {1: "raw", 5: "jpeg"}
|
2010-07-31 06:52:47 +04:00
|
|
|
|
py3k: The big push
There are two main issues fixed with this commit:
* bytes vs. str: All file, image, and palette data are now handled as
bytes. A new _binary module consolidates the hacks needed to do this
across Python versions. tostring/fromstring methods have been renamed to
tobytes/frombytes, but the Python 2.6/2.7 versions alias them to the old
names for compatibility. Users should move to tobytes/frombytes.
One other potentially-breaking change is that text data in image files
(such as tags, comments) are now explicitly handled with a specific
character encoding in mind. This works well with the Unicode str in
Python 3, but may trip up old code expecting a straight byte-for-byte
translation to a Python string. This also required a change to Gohlke's
tags tests (in Tests/test_file_png.py) to expect Unicode strings from
the code.
* True div vs. floor div: Many division operations used the "/" operator
to do floor division, which is now the "//" operator in Python 3. These
were fixed.
As of this commit, on the first pass, I have one failing test (improper
handling of a slice object in a C module, test_imagepath.py) in Python 3,
and three that that I haven't tried running yet (test_imagegl,
test_imagegrab, and test_imageqt). I also haven't tested anything on
Windows. All but the three skipped tests run flawlessly against Pythons
2.6 and 2.7.
2012-10-21 01:01:53 +04:00
|
|
|
PAD = o8(0) * 4
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2014-07-30 20:39:11 +04:00
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
#
|
|
|
|
# Helpers
|
|
|
|
|
2019-03-21 16:28:20 +03:00
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
def i(c):
|
|
|
|
return i32((PAD + c)[-4:])
|
|
|
|
|
2014-07-30 20:39:11 +04:00
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
def dump(c):
|
|
|
|
for i in c:
|
2019-03-21 16:28:20 +03:00
|
|
|
print("%02x" % i8(i), end=" ")
|
2012-10-16 06:27:35 +04:00
|
|
|
print()
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2014-07-30 20:39:11 +04:00
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
##
|
|
|
|
# Image plugin for IPTC/NAA datastreams. To read IPTC/NAA fields
|
|
|
|
# from TIFF and JPEG files, use the <b>getiptcinfo</b> function.
|
|
|
|
|
2019-03-21 16:28:20 +03:00
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
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
|
|
|
|
|
py3k: The big push
There are two main issues fixed with this commit:
* bytes vs. str: All file, image, and palette data are now handled as
bytes. A new _binary module consolidates the hacks needed to do this
across Python versions. tostring/fromstring methods have been renamed to
tobytes/frombytes, but the Python 2.6/2.7 versions alias them to the old
names for compatibility. Users should move to tobytes/frombytes.
One other potentially-breaking change is that text data in image files
(such as tags, comments) are now explicitly handled with a specific
character encoding in mind. This works well with the Unicode str in
Python 3, but may trip up old code expecting a straight byte-for-byte
translation to a Python string. This also required a change to Gohlke's
tags tests (in Tests/test_file_png.py) to expect Unicode strings from
the code.
* True div vs. floor div: Many division operations used the "/" operator
to do floor division, which is now the "//" operator in Python 3. These
were fixed.
As of this commit, on the first pass, I have one failing test (improper
handling of a slice object in a C module, test_imagepath.py) in Python 3,
and three that that I haven't tried running yet (test_imagegl,
test_imagegrab, and test_imageqt). I also haven't tested anything on
Windows. All but the three skipped tests run flawlessly against Pythons
2.6 and 2.7.
2012-10-21 01:01:53 +04:00
|
|
|
tag = i8(s[1]), i8(s[2])
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
# syntax
|
py3k: The big push
There are two main issues fixed with this commit:
* bytes vs. str: All file, image, and palette data are now handled as
bytes. A new _binary module consolidates the hacks needed to do this
across Python versions. tostring/fromstring methods have been renamed to
tobytes/frombytes, but the Python 2.6/2.7 versions alias them to the old
names for compatibility. Users should move to tobytes/frombytes.
One other potentially-breaking change is that text data in image files
(such as tags, comments) are now explicitly handled with a specific
character encoding in mind. This works well with the Unicode str in
Python 3, but may trip up old code expecting a straight byte-for-byte
translation to a Python string. This also required a change to Gohlke's
tags tests (in Tests/test_file_png.py) to expect Unicode strings from
the code.
* True div vs. floor div: Many division operations used the "/" operator
to do floor division, which is now the "//" operator in Python 3. These
were fixed.
As of this commit, on the first pass, I have one failing test (improper
handling of a slice object in a C module, test_imagepath.py) in Python 3,
and three that that I haven't tried running yet (test_imagegl,
test_imagegrab, and test_imageqt). I also haven't tested anything on
Windows. All but the three skipped tests run flawlessly against Pythons
2.6 and 2.7.
2012-10-21 01:01:53 +04:00
|
|
|
if i8(s[0]) != 0x1C or tag[0] < 1 or tag[0] > 9:
|
2012-10-11 07:52:53 +04:00
|
|
|
raise SyntaxError("invalid IPTC/NAA file")
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
# field size
|
py3k: The big push
There are two main issues fixed with this commit:
* bytes vs. str: All file, image, and palette data are now handled as
bytes. A new _binary module consolidates the hacks needed to do this
across Python versions. tostring/fromstring methods have been renamed to
tobytes/frombytes, but the Python 2.6/2.7 versions alias them to the old
names for compatibility. Users should move to tobytes/frombytes.
One other potentially-breaking change is that text data in image files
(such as tags, comments) are now explicitly handled with a specific
character encoding in mind. This works well with the Unicode str in
Python 3, but may trip up old code expecting a straight byte-for-byte
translation to a Python string. This also required a change to Gohlke's
tags tests (in Tests/test_file_png.py) to expect Unicode strings from
the code.
* True div vs. floor div: Many division operations used the "/" operator
to do floor division, which is now the "//" operator in Python 3. These
were fixed.
As of this commit, on the first pass, I have one failing test (improper
handling of a slice object in a C module, test_imagepath.py) in Python 3,
and three that that I haven't tried running yet (test_imagegl,
test_imagegrab, and test_imageqt). I also haven't tested anything on
Windows. All but the three skipped tests run flawlessly against Pythons
2.6 and 2.7.
2012-10-21 01:01:53 +04:00
|
|
|
size = i8(s[3])
|
2010-07-31 06:52:47 +04:00
|
|
|
if size > 132:
|
2019-09-30 17:56:31 +03:00
|
|
|
raise OSError("illegal field length in IPTC/NAA file")
|
2010-07-31 06:52:47 +04:00
|
|
|
elif size == 128:
|
|
|
|
size = 0
|
|
|
|
elif size > 128:
|
2019-03-21 16:28:20 +03:00
|
|
|
size = i(self.fp.read(size - 128))
|
2010-07-31 06:52:47 +04:00
|
|
|
else:
|
|
|
|
size = i16(s[3:])
|
|
|
|
|
|
|
|
return tag, size
|
|
|
|
|
|
|
|
def _open(self):
|
|
|
|
|
|
|
|
# load descriptive fields
|
2012-10-17 07:39:56 +04:00
|
|
|
while True:
|
2010-07-31 06:52:47 +04:00
|
|
|
offset = self.fp.tell()
|
|
|
|
tag, size = self.field()
|
2014-07-30 20:39:11 +04:00
|
|
|
if not tag or tag == (8, 10):
|
2010-07-31 06:52:47 +04:00
|
|
|
break
|
|
|
|
if size:
|
|
|
|
tagdata = self.fp.read(size)
|
|
|
|
else:
|
|
|
|
tagdata = None
|
2017-05-28 19:34:41 +03:00
|
|
|
if tag in self.info:
|
2010-07-31 06:52:47 +04:00
|
|
|
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
|
2014-07-30 20:39:11 +04:00
|
|
|
layers = i8(self.info[(3, 60)][0])
|
|
|
|
component = i8(self.info[(3, 60)][1])
|
|
|
|
if (3, 65) in self.info:
|
2019-03-21 16:28:20 +03:00
|
|
|
id = i8(self.info[(3, 65)][0]) - 1
|
2010-07-31 06:52:47 +04:00
|
|
|
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
|
2018-09-30 05:58:02 +03:00
|
|
|
self._size = self.getint((3, 20)), self.getint((3, 30))
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
# compression
|
|
|
|
try:
|
2014-07-30 20:39:11 +04:00
|
|
|
compression = COMPRESSION[self.getint((3, 120))]
|
2020-06-21 13:13:35 +03:00
|
|
|
except KeyError as e:
|
|
|
|
raise OSError("Unknown IPTC image compression") from e
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
# tile
|
2014-07-30 20:39:11 +04:00
|
|
|
if tag == (8, 10):
|
2019-03-21 16:28:20 +03:00
|
|
|
self.tile = [
|
|
|
|
("iptc", (compression, offset), (0, 0, self.size[0], self.size[1]))
|
|
|
|
]
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
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
|
2014-03-15 02:56:41 +04:00
|
|
|
o_fd, outfile = tempfile.mkstemp(text=False)
|
|
|
|
o = os.fdopen(o_fd)
|
2010-07-31 06:52:47 +04:00
|
|
|
if encoding == "raw":
|
|
|
|
# To simplify access to the extracted file,
|
|
|
|
# prepend a PPM header
|
|
|
|
o.write("P5\n%d %d\n255\n" % self.size)
|
2012-10-17 07:39:56 +04:00
|
|
|
while True:
|
2010-07-31 06:52:47 +04:00
|
|
|
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)
|
2014-05-10 08:36:15 +04:00
|
|
|
size -= len(s)
|
2010-07-31 06:52:47 +04:00
|
|
|
o.close()
|
|
|
|
|
|
|
|
try:
|
2020-02-18 12:49:05 +03:00
|
|
|
with Image.open(outfile) as _im:
|
|
|
|
_im.load()
|
|
|
|
self.im = _im.im
|
2010-07-31 06:52:47 +04:00
|
|
|
finally:
|
2014-07-30 20:39:11 +04:00
|
|
|
try:
|
|
|
|
os.unlink(outfile)
|
2015-12-02 08:23:49 +03:00
|
|
|
except OSError:
|
2014-07-30 20:39:11 +04:00
|
|
|
pass
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
|
2015-07-04 16:29:58 +03:00
|
|
|
Image.register_open(IptcImageFile.format, IptcImageFile)
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2015-07-04 16:29:58 +03:00
|
|
|
Image.register_extension(IptcImageFile.format, ".iim")
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2014-07-30 20:39:11 +04:00
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
def getiptcinfo(im):
|
2016-09-24 12:10:46 +03:00
|
|
|
"""
|
|
|
|
Get IPTC information from TIFF, JPEG, or IPTC file.
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2016-09-24 12:10:46 +03:00
|
|
|
:param im: An image containing IPTC data.
|
|
|
|
:returns: A dictionary containing IPTC information, or None if
|
|
|
|
no IPTC information block was found.
|
|
|
|
"""
|
2012-10-17 07:01:19 +04:00
|
|
|
import io
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2020-08-07 13:28:33 +03:00
|
|
|
from . import JpegImagePlugin, TiffImagePlugin
|
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
data = None
|
|
|
|
|
|
|
|
if isinstance(im, IptcImageFile):
|
|
|
|
# return info dictionary right away
|
|
|
|
return im.info
|
|
|
|
|
|
|
|
elif isinstance(im, JpegImagePlugin.JpegImageFile):
|
|
|
|
# extract the IPTC/NAA resource
|
2019-03-06 02:28:45 +03:00
|
|
|
photoshop = im.info.get("photoshop")
|
|
|
|
if photoshop:
|
|
|
|
data = photoshop.get(0x0404)
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
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:
|
2013-10-08 04:00:54 +04:00
|
|
|
data = im.tag.tagdata[TiffImagePlugin.IPTC_NAA_CHUNK]
|
2010-07-31 06:52:47 +04:00
|
|
|
except (AttributeError, KeyError):
|
|
|
|
pass
|
|
|
|
|
|
|
|
if data is None:
|
2014-07-30 20:39:11 +04:00
|
|
|
return None # no properties
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
# create an IptcImagePlugin object without initializing it
|
2019-09-30 17:56:31 +03:00
|
|
|
class FakeImage:
|
2010-07-31 06:52:47 +04:00
|
|
|
pass
|
2019-03-21 16:28:20 +03:00
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
im = FakeImage()
|
|
|
|
im.__class__ = IptcImageFile
|
|
|
|
|
|
|
|
# parse the IPTC information chunk
|
|
|
|
im.info = {}
|
2012-10-17 07:01:19 +04:00
|
|
|
im.fp = io.BytesIO(data)
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
try:
|
|
|
|
im._open()
|
|
|
|
except (IndexError, KeyError):
|
2014-07-30 20:39:11 +04:00
|
|
|
pass # expected failure
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
return im.info
|