mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-11-04 01:47:47 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			258 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			258 lines
		
	
	
		
			6.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
 | 
						|
 | 
						|
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 list(self.info.keys()):
 | 
						|
                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
 |