mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-11-13 13:16:52 +03:00
d50445ff30
Similar to the recent adoption of Black. isort is a Python utility to sort imports alphabetically and automatically separate into sections. By using isort, contributors can quickly and automatically conform to the projects style without thinking. Just let the tool do it. Uses the configuration recommended by the Black to avoid conflicts of style. Rewrite TestImageQt.test_deprecated to no rely on import order.
234 lines
5.7 KiB
Python
234 lines
5.7 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
|
|
|
|
import os
|
|
import tempfile
|
|
|
|
from . import Image, ImageFile
|
|
from ._binary import i8, i16be as i16, i32be as i32, o8
|
|
|
|
# __version__ is deprecated and will be removed in a future version. Use
|
|
# PIL.__version__ instead.
|
|
__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
|
|
photoshop = im.info.get("photoshop")
|
|
if photoshop:
|
|
data = photoshop.get(0x0404)
|
|
|
|
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
|