mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-11-04 01:47:47 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			288 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			288 lines
		
	
	
		
			7.5 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
 | 
						|
 | 
						|
__version__ = "0.3"
 | 
						|
 | 
						|
 | 
						|
from PIL import Image, ImageFile, _binary
 | 
						|
import os, tempfile
 | 
						|
 | 
						|
i8 = _binary.i8
 | 
						|
i16 = _binary.i16be
 | 
						|
i32 = _binary.i32be
 | 
						|
o8 = _binary.o8
 | 
						|
 | 
						|
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 _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 True:
 | 
						|
            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 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):
 | 
						|
            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 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 = 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):
 | 
						|
 | 
						|
    from PIL 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] == "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 = i8(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 = io.BytesIO(data)
 | 
						|
 | 
						|
    try:
 | 
						|
        im._open()
 | 
						|
    except (IndexError, KeyError):
 | 
						|
        pass # expected failure
 | 
						|
 | 
						|
    return im.info
 |