mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-11-04 09:57:43 +03:00 
			
		
		
		
	
						commit
						a986fed5b4
					
				| 
						 | 
				
			
			@ -2,6 +2,7 @@
 | 
			
		|||
-e .
 | 
			
		||||
alabaster
 | 
			
		||||
Babel
 | 
			
		||||
black; python_version >= '3.6'
 | 
			
		||||
check-manifest
 | 
			
		||||
cov-core
 | 
			
		||||
coverage
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,4 +2,5 @@
 | 
			
		|||
test=pytest
 | 
			
		||||
 | 
			
		||||
[flake8]
 | 
			
		||||
extend-ignore = E203, W503
 | 
			
		||||
max-line-length = 88
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,14 +32,10 @@ bdf_slant = {
 | 
			
		|||
    "O": "Oblique",
 | 
			
		||||
    "RI": "Reverse Italic",
 | 
			
		||||
    "RO": "Reverse Oblique",
 | 
			
		||||
    "OT": "Other"
 | 
			
		||||
    "OT": "Other",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bdf_spacing = {
 | 
			
		||||
    "P": "Proportional",
 | 
			
		||||
    "M": "Monospaced",
 | 
			
		||||
    "C": "Cell"
 | 
			
		||||
}
 | 
			
		||||
bdf_spacing = {"P": "Proportional", "M": "Monospaced", "C": "Cell"}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def bdf_char(f):
 | 
			
		||||
| 
						 | 
				
			
			@ -50,7 +46,7 @@ def bdf_char(f):
 | 
			
		|||
            return None
 | 
			
		||||
        if s[:9] == b"STARTCHAR":
 | 
			
		||||
            break
 | 
			
		||||
    id = s[9:].strip().decode('ascii')
 | 
			
		||||
    id = s[9:].strip().decode("ascii")
 | 
			
		||||
 | 
			
		||||
    # load symbol properties
 | 
			
		||||
    props = {}
 | 
			
		||||
| 
						 | 
				
			
			@ -59,7 +55,7 @@ def bdf_char(f):
 | 
			
		|||
        if not s or s[:6] == b"BITMAP":
 | 
			
		||||
            break
 | 
			
		||||
        i = s.find(b" ")
 | 
			
		||||
        props[s[:i].decode('ascii')] = s[i+1:-1].decode('ascii')
 | 
			
		||||
        props[s[:i].decode("ascii")] = s[i + 1 : -1].decode("ascii")
 | 
			
		||||
 | 
			
		||||
    # load bitmap
 | 
			
		||||
    bitmap = []
 | 
			
		||||
| 
						 | 
				
			
			@ -73,7 +69,7 @@ def bdf_char(f):
 | 
			
		|||
    [x, y, l, d] = [int(p) for p in props["BBX"].split()]
 | 
			
		||||
    [dx, dy] = [int(p) for p in props["DWIDTH"].split()]
 | 
			
		||||
 | 
			
		||||
    bbox = (dx, dy), (l, -d-y, x+l, -d), (0, 0, x, y)
 | 
			
		||||
    bbox = (dx, dy), (l, -d - y, x + l, -d), (0, 0, x, y)
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        im = Image.frombytes("1", (x, y), bitmap, "hex", "1")
 | 
			
		||||
| 
						 | 
				
			
			@ -87,8 +83,8 @@ def bdf_char(f):
 | 
			
		|||
##
 | 
			
		||||
# Font file plugin for the X11 BDF format.
 | 
			
		||||
 | 
			
		||||
class BdfFontFile(FontFile.FontFile):
 | 
			
		||||
 | 
			
		||||
class BdfFontFile(FontFile.FontFile):
 | 
			
		||||
    def __init__(self, fp):
 | 
			
		||||
 | 
			
		||||
        FontFile.FontFile.__init__(self)
 | 
			
		||||
| 
						 | 
				
			
			@ -105,10 +101,10 @@ class BdfFontFile(FontFile.FontFile):
 | 
			
		|||
            if not s or s[:13] == b"ENDPROPERTIES":
 | 
			
		||||
                break
 | 
			
		||||
            i = s.find(b" ")
 | 
			
		||||
            props[s[:i].decode('ascii')] = s[i+1:-1].decode('ascii')
 | 
			
		||||
            props[s[:i].decode("ascii")] = s[i + 1 : -1].decode("ascii")
 | 
			
		||||
            if s[:i] in [b"COMMENT", b"COPYRIGHT"]:
 | 
			
		||||
                if s.find(b"LogicalFontDescription") < 0:
 | 
			
		||||
                    comments.append(s[i+1:-1].decode('ascii'))
 | 
			
		||||
                    comments.append(s[i + 1 : -1].decode("ascii"))
 | 
			
		||||
 | 
			
		||||
        while True:
 | 
			
		||||
            c = bdf_char(fp)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -47,11 +47,7 @@ BLP_ALPHA_ENCODING_DXT5 = 7
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
def unpack_565(i):
 | 
			
		||||
    return (
 | 
			
		||||
        ((i >> 11) & 0x1f) << 3,
 | 
			
		||||
        ((i >> 5) & 0x3f) << 2,
 | 
			
		||||
        (i & 0x1f) << 3
 | 
			
		||||
    )
 | 
			
		||||
    return (((i >> 11) & 0x1F) << 3, ((i >> 5) & 0x3F) << 2, (i & 0x1F) << 3)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def decode_dxt1(data, alpha=False):
 | 
			
		||||
| 
						 | 
				
			
			@ -119,7 +115,7 @@ def decode_dxt3(data):
 | 
			
		|||
 | 
			
		||||
    for block in range(blocks):
 | 
			
		||||
        idx = block * 16
 | 
			
		||||
        block = data[idx:idx + 16]
 | 
			
		||||
        block = data[idx : idx + 16]
 | 
			
		||||
        # Decode next 16-byte block.
 | 
			
		||||
        bits = struct.unpack_from("<8B", block)
 | 
			
		||||
        color0, color1 = struct.unpack_from("<HH", block, 8)
 | 
			
		||||
| 
						 | 
				
			
			@ -139,7 +135,7 @@ def decode_dxt3(data):
 | 
			
		|||
                    a >>= 4
 | 
			
		||||
                else:
 | 
			
		||||
                    high = True
 | 
			
		||||
                    a &= 0xf
 | 
			
		||||
                    a &= 0xF
 | 
			
		||||
                a *= 17  # We get a value between 0 and 15
 | 
			
		||||
 | 
			
		||||
                color_code = (code >> 2 * (4 * j + i)) & 0x03
 | 
			
		||||
| 
						 | 
				
			
			@ -172,14 +168,12 @@ def decode_dxt5(data):
 | 
			
		|||
 | 
			
		||||
    for block in range(blocks):
 | 
			
		||||
        idx = block * 16
 | 
			
		||||
        block = data[idx:idx + 16]
 | 
			
		||||
        block = data[idx : idx + 16]
 | 
			
		||||
        # Decode next 16-byte block.
 | 
			
		||||
        a0, a1 = struct.unpack_from("<BB", block)
 | 
			
		||||
 | 
			
		||||
        bits = struct.unpack_from("<6B", block, 2)
 | 
			
		||||
        alphacode1 = (
 | 
			
		||||
            bits[2] | (bits[3] << 8) | (bits[4] << 16) | (bits[5] << 24)
 | 
			
		||||
        )
 | 
			
		||||
        alphacode1 = bits[2] | (bits[3] << 8) | (bits[4] << 16) | (bits[5] << 24)
 | 
			
		||||
        alphacode2 = bits[0] | (bits[1] << 8)
 | 
			
		||||
 | 
			
		||||
        color0, color1 = struct.unpack_from("<HH", block, 8)
 | 
			
		||||
| 
						 | 
				
			
			@ -242,6 +236,7 @@ class BlpImageFile(ImageFile.ImageFile):
 | 
			
		|||
    """
 | 
			
		||||
    Blizzard Mipmap Format
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    format = "BLP"
 | 
			
		||||
    format_description = "Blizzard Mipmap Format"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -258,9 +253,7 @@ class BlpImageFile(ImageFile.ImageFile):
 | 
			
		|||
        else:
 | 
			
		||||
            raise BLPFormatError("Bad BLP magic %r" % (self.magic))
 | 
			
		||||
 | 
			
		||||
        self.tile = [
 | 
			
		||||
            (decoder, (0, 0) + self.size, 0, (self.mode, 0, 1))
 | 
			
		||||
        ]
 | 
			
		||||
        self.tile = [(decoder, (0, 0) + self.size, 0, (self.mode, 0, 1))]
 | 
			
		||||
 | 
			
		||||
    def _read_blp_header(self):
 | 
			
		||||
        self._blp_compression, = struct.unpack("<i", self.fp.read(4))
 | 
			
		||||
| 
						 | 
				
			
			@ -324,7 +317,6 @@ class _BLPBaseDecoder(ImageFile.PyDecoder):
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
class BLP1Decoder(_BLPBaseDecoder):
 | 
			
		||||
 | 
			
		||||
    def _load(self):
 | 
			
		||||
        if self._blp_compression == BLP_FORMAT_JPEG:
 | 
			
		||||
            self._decode_jpeg_stream()
 | 
			
		||||
| 
						 | 
				
			
			@ -368,7 +360,6 @@ class BLP1Decoder(_BLPBaseDecoder):
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
class BLP2Decoder(_BLPBaseDecoder):
 | 
			
		||||
 | 
			
		||||
    def _load(self):
 | 
			
		||||
        palette = self._read_palette()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -393,8 +384,7 @@ class BLP2Decoder(_BLPBaseDecoder):
 | 
			
		|||
                    linesize = (self.size[0] + 3) // 4 * 8
 | 
			
		||||
                    for yb in range((self.size[1] + 3) // 4):
 | 
			
		||||
                        for d in decode_dxt1(
 | 
			
		||||
                            self.fd.read(linesize),
 | 
			
		||||
                            alpha=bool(self._blp_alpha_depth)
 | 
			
		||||
                            self.fd.read(linesize), alpha=bool(self._blp_alpha_depth)
 | 
			
		||||
                        ):
 | 
			
		||||
                            data += d
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -409,19 +399,15 @@ class BLP2Decoder(_BLPBaseDecoder):
 | 
			
		|||
                    for yb in range((self.size[1] + 3) // 4):
 | 
			
		||||
                        for d in decode_dxt5(self.fd.read(linesize)):
 | 
			
		||||
                            data += d
 | 
			
		||||
                else:
 | 
			
		||||
                    raise BLPFormatError("Unsupported alpha encoding %r" % (
 | 
			
		||||
                        self._blp_alpha_encoding
 | 
			
		||||
                    ))
 | 
			
		||||
                else:
 | 
			
		||||
                    raise BLPFormatError(
 | 
			
		||||
                    "Unknown BLP encoding %r" % (self._blp_encoding)
 | 
			
		||||
                        "Unsupported alpha encoding %r" % (self._blp_alpha_encoding)
 | 
			
		||||
                    )
 | 
			
		||||
            else:
 | 
			
		||||
                raise BLPFormatError("Unknown BLP encoding %r" % (self._blp_encoding))
 | 
			
		||||
 | 
			
		||||
        else:
 | 
			
		||||
            raise BLPFormatError(
 | 
			
		||||
                "Unknown BLP compression %r" % (self._blp_compression)
 | 
			
		||||
            )
 | 
			
		||||
            raise BLPFormatError("Unknown BLP compression %r" % (self._blp_compression))
 | 
			
		||||
 | 
			
		||||
        self.set_as_raw(bytes(data))
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,8 +25,7 @@
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
from . import Image, ImageFile, ImagePalette
 | 
			
		||||
from ._binary import i8, i16le as i16, i32le as i32, \
 | 
			
		||||
                     o8, o16le as o16, o32le as o32
 | 
			
		||||
from ._binary import i8, i16le as i16, i32le as i32, o8, o16le as o16, o32le as o32
 | 
			
		||||
 | 
			
		||||
# __version__ is deprecated and will be removed in a future version. Use
 | 
			
		||||
# PIL.__version__ instead.
 | 
			
		||||
| 
						 | 
				
			
			@ -66,14 +65,7 @@ class BmpImageFile(ImageFile.ImageFile):
 | 
			
		|||
    format = "BMP"
 | 
			
		||||
 | 
			
		||||
    # -------------------------------------------------- BMP Compression values
 | 
			
		||||
    COMPRESSIONS = {
 | 
			
		||||
        'RAW': 0,
 | 
			
		||||
        'RLE8': 1,
 | 
			
		||||
        'RLE4': 2,
 | 
			
		||||
        'BITFIELDS': 3,
 | 
			
		||||
        'JPEG': 4,
 | 
			
		||||
        'PNG': 5
 | 
			
		||||
    }
 | 
			
		||||
    COMPRESSIONS = {"RAW": 0, "RLE8": 1, "RLE4": 2, "BITFIELDS": 3, "JPEG": 4, "PNG": 5}
 | 
			
		||||
    RAW, RLE8, RLE4, BITFIELDS, JPEG, PNG = 0, 1, 2, 3, 4, 5
 | 
			
		||||
 | 
			
		||||
    def _bitmap(self, header=0, offset=0):
 | 
			
		||||
| 
						 | 
				
			
			@ -83,53 +75,54 @@ class BmpImageFile(ImageFile.ImageFile):
 | 
			
		|||
            seek(header)
 | 
			
		||||
        file_info = {}
 | 
			
		||||
        # read bmp header size @offset 14 (this is part of the header size)
 | 
			
		||||
        file_info['header_size'] = i32(read(4))
 | 
			
		||||
        file_info['direction'] = -1
 | 
			
		||||
        file_info["header_size"] = i32(read(4))
 | 
			
		||||
        file_info["direction"] = -1
 | 
			
		||||
 | 
			
		||||
        # -------------------- If requested, read header at a specific position
 | 
			
		||||
        # read the rest of the bmp header, without its size
 | 
			
		||||
        header_data = ImageFile._safe_read(self.fp,
 | 
			
		||||
                                           file_info['header_size'] - 4)
 | 
			
		||||
        header_data = ImageFile._safe_read(self.fp, file_info["header_size"] - 4)
 | 
			
		||||
 | 
			
		||||
        # -------------------------------------------------- IBM OS/2 Bitmap v1
 | 
			
		||||
        # ----- This format has different offsets because of width/height types
 | 
			
		||||
        if file_info['header_size'] == 12:
 | 
			
		||||
            file_info['width'] = i16(header_data[0:2])
 | 
			
		||||
            file_info['height'] = i16(header_data[2:4])
 | 
			
		||||
            file_info['planes'] = i16(header_data[4:6])
 | 
			
		||||
            file_info['bits'] = i16(header_data[6:8])
 | 
			
		||||
            file_info['compression'] = self.RAW
 | 
			
		||||
            file_info['palette_padding'] = 3
 | 
			
		||||
        if file_info["header_size"] == 12:
 | 
			
		||||
            file_info["width"] = i16(header_data[0:2])
 | 
			
		||||
            file_info["height"] = i16(header_data[2:4])
 | 
			
		||||
            file_info["planes"] = i16(header_data[4:6])
 | 
			
		||||
            file_info["bits"] = i16(header_data[6:8])
 | 
			
		||||
            file_info["compression"] = self.RAW
 | 
			
		||||
            file_info["palette_padding"] = 3
 | 
			
		||||
 | 
			
		||||
        # --------------------------------------------- Windows Bitmap v2 to v5
 | 
			
		||||
        # v3, OS/2 v2, v4, v5
 | 
			
		||||
        elif file_info['header_size'] in (40, 64, 108, 124):
 | 
			
		||||
            file_info['y_flip'] = i8(header_data[7]) == 0xff
 | 
			
		||||
            file_info['direction'] = 1 if file_info['y_flip'] else -1
 | 
			
		||||
            file_info['width'] = i32(header_data[0:4])
 | 
			
		||||
            file_info['height'] = (i32(header_data[4:8])
 | 
			
		||||
                                   if not file_info['y_flip']
 | 
			
		||||
                                   else 2**32 - i32(header_data[4:8]))
 | 
			
		||||
            file_info['planes'] = i16(header_data[8:10])
 | 
			
		||||
            file_info['bits'] = i16(header_data[10:12])
 | 
			
		||||
            file_info['compression'] = i32(header_data[12:16])
 | 
			
		||||
            # byte size of pixel data
 | 
			
		||||
            file_info['data_size'] = i32(header_data[16:20])
 | 
			
		||||
            file_info['pixels_per_meter'] = (i32(header_data[20:24]),
 | 
			
		||||
                                             i32(header_data[24:28]))
 | 
			
		||||
            file_info['colors'] = i32(header_data[28:32])
 | 
			
		||||
            file_info['palette_padding'] = 4
 | 
			
		||||
            self.info["dpi"] = tuple(
 | 
			
		||||
                int(x / 39.3701 + 0.5) for x in file_info['pixels_per_meter'])
 | 
			
		||||
            if file_info['compression'] == self.BITFIELDS:
 | 
			
		||||
                if len(header_data) >= 52:
 | 
			
		||||
                    for idx, mask in enumerate(['r_mask',
 | 
			
		||||
                                                'g_mask',
 | 
			
		||||
                                                'b_mask',
 | 
			
		||||
                                                'a_mask']):
 | 
			
		||||
                        file_info[mask] = i32(
 | 
			
		||||
                            header_data[36 + idx * 4:40 + idx * 4]
 | 
			
		||||
        elif file_info["header_size"] in (40, 64, 108, 124):
 | 
			
		||||
            file_info["y_flip"] = i8(header_data[7]) == 0xFF
 | 
			
		||||
            file_info["direction"] = 1 if file_info["y_flip"] else -1
 | 
			
		||||
            file_info["width"] = i32(header_data[0:4])
 | 
			
		||||
            file_info["height"] = (
 | 
			
		||||
                i32(header_data[4:8])
 | 
			
		||||
                if not file_info["y_flip"]
 | 
			
		||||
                else 2 ** 32 - i32(header_data[4:8])
 | 
			
		||||
            )
 | 
			
		||||
            file_info["planes"] = i16(header_data[8:10])
 | 
			
		||||
            file_info["bits"] = i16(header_data[10:12])
 | 
			
		||||
            file_info["compression"] = i32(header_data[12:16])
 | 
			
		||||
            # byte size of pixel data
 | 
			
		||||
            file_info["data_size"] = i32(header_data[16:20])
 | 
			
		||||
            file_info["pixels_per_meter"] = (
 | 
			
		||||
                i32(header_data[20:24]),
 | 
			
		||||
                i32(header_data[24:28]),
 | 
			
		||||
            )
 | 
			
		||||
            file_info["colors"] = i32(header_data[28:32])
 | 
			
		||||
            file_info["palette_padding"] = 4
 | 
			
		||||
            self.info["dpi"] = tuple(
 | 
			
		||||
                int(x / 39.3701 + 0.5) for x in file_info["pixels_per_meter"]
 | 
			
		||||
            )
 | 
			
		||||
            if file_info["compression"] == self.BITFIELDS:
 | 
			
		||||
                if len(header_data) >= 52:
 | 
			
		||||
                    for idx, mask in enumerate(
 | 
			
		||||
                        ["r_mask", "g_mask", "b_mask", "a_mask"]
 | 
			
		||||
                    ):
 | 
			
		||||
                        file_info[mask] = i32(header_data[36 + idx * 4 : 40 + idx * 4])
 | 
			
		||||
                else:
 | 
			
		||||
                    # 40 byte headers only have the three components in the
 | 
			
		||||
                    # bitfields masks, ref:
 | 
			
		||||
| 
						 | 
				
			
			@ -139,121 +132,133 @@ class BmpImageFile(ImageFile.ImageFile):
 | 
			
		|||
                    # There is a 4th component in the RGBQuad, in the alpha
 | 
			
		||||
                    # location, but it is listed as a reserved component,
 | 
			
		||||
                    # and it is not generally an alpha channel
 | 
			
		||||
                    file_info['a_mask'] = 0x0
 | 
			
		||||
                    for mask in ['r_mask', 'g_mask', 'b_mask']:
 | 
			
		||||
                    file_info["a_mask"] = 0x0
 | 
			
		||||
                    for mask in ["r_mask", "g_mask", "b_mask"]:
 | 
			
		||||
                        file_info[mask] = i32(read(4))
 | 
			
		||||
                file_info['rgb_mask'] = (file_info['r_mask'],
 | 
			
		||||
                                         file_info['g_mask'],
 | 
			
		||||
                                         file_info['b_mask'])
 | 
			
		||||
                file_info['rgba_mask'] = (file_info['r_mask'],
 | 
			
		||||
                                          file_info['g_mask'],
 | 
			
		||||
                                          file_info['b_mask'],
 | 
			
		||||
                                          file_info['a_mask'])
 | 
			
		||||
                file_info["rgb_mask"] = (
 | 
			
		||||
                    file_info["r_mask"],
 | 
			
		||||
                    file_info["g_mask"],
 | 
			
		||||
                    file_info["b_mask"],
 | 
			
		||||
                )
 | 
			
		||||
                file_info["rgba_mask"] = (
 | 
			
		||||
                    file_info["r_mask"],
 | 
			
		||||
                    file_info["g_mask"],
 | 
			
		||||
                    file_info["b_mask"],
 | 
			
		||||
                    file_info["a_mask"],
 | 
			
		||||
                )
 | 
			
		||||
        else:
 | 
			
		||||
            raise IOError("Unsupported BMP header type (%d)" %
 | 
			
		||||
                          file_info['header_size'])
 | 
			
		||||
            raise IOError("Unsupported BMP header type (%d)" % file_info["header_size"])
 | 
			
		||||
 | 
			
		||||
        # ------------------ Special case : header is reported 40, which
 | 
			
		||||
        # ---------------------- is shorter than real size for bpp >= 16
 | 
			
		||||
        self._size = file_info['width'], file_info['height']
 | 
			
		||||
        self._size = file_info["width"], file_info["height"]
 | 
			
		||||
 | 
			
		||||
        # ------- If color count was not found in the header, compute from bits
 | 
			
		||||
        file_info["colors"] = (file_info["colors"]
 | 
			
		||||
        file_info["colors"] = (
 | 
			
		||||
            file_info["colors"]
 | 
			
		||||
            if file_info.get("colors", 0)
 | 
			
		||||
                               else (1 << file_info["bits"]))
 | 
			
		||||
            else (1 << file_info["bits"])
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # ------------------------------- Check abnormal values for DOS attacks
 | 
			
		||||
        if file_info['width'] * file_info['height'] > 2**31:
 | 
			
		||||
        if file_info["width"] * file_info["height"] > 2 ** 31:
 | 
			
		||||
            raise IOError("Unsupported BMP Size: (%dx%d)" % self.size)
 | 
			
		||||
 | 
			
		||||
        # ---------------------- Check bit depth for unusual unsupported values
 | 
			
		||||
        self.mode, raw_mode = BIT2MODE.get(file_info['bits'], (None, None))
 | 
			
		||||
        self.mode, raw_mode = BIT2MODE.get(file_info["bits"], (None, None))
 | 
			
		||||
        if self.mode is None:
 | 
			
		||||
            raise IOError("Unsupported BMP pixel depth (%d)"
 | 
			
		||||
                          % file_info['bits'])
 | 
			
		||||
            raise IOError("Unsupported BMP pixel depth (%d)" % file_info["bits"])
 | 
			
		||||
 | 
			
		||||
        # ---------------- Process BMP with Bitfields compression (not palette)
 | 
			
		||||
        if file_info['compression'] == self.BITFIELDS:
 | 
			
		||||
        if file_info["compression"] == self.BITFIELDS:
 | 
			
		||||
            SUPPORTED = {
 | 
			
		||||
                32: [(0xff0000, 0xff00, 0xff, 0x0),
 | 
			
		||||
                     (0xff0000, 0xff00, 0xff, 0xff000000),
 | 
			
		||||
                     (0xff, 0xff00, 0xff0000, 0xff000000),
 | 
			
		||||
                32: [
 | 
			
		||||
                    (0xFF0000, 0xFF00, 0xFF, 0x0),
 | 
			
		||||
                    (0xFF0000, 0xFF00, 0xFF, 0xFF000000),
 | 
			
		||||
                    (0xFF, 0xFF00, 0xFF0000, 0xFF000000),
 | 
			
		||||
                    (0x0, 0x0, 0x0, 0x0),
 | 
			
		||||
                     (0xff000000, 0xff0000, 0xff00, 0x0)],
 | 
			
		||||
                24: [(0xff0000, 0xff00, 0xff)],
 | 
			
		||||
                16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)]
 | 
			
		||||
                    (0xFF000000, 0xFF0000, 0xFF00, 0x0),
 | 
			
		||||
                ],
 | 
			
		||||
                24: [(0xFF0000, 0xFF00, 0xFF)],
 | 
			
		||||
                16: [(0xF800, 0x7E0, 0x1F), (0x7C00, 0x3E0, 0x1F)],
 | 
			
		||||
            }
 | 
			
		||||
            MASK_MODES = {
 | 
			
		||||
                (32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX",
 | 
			
		||||
                (32, (0xff000000, 0xff0000, 0xff00, 0x0)): "XBGR",
 | 
			
		||||
                (32, (0xff, 0xff00, 0xff0000, 0xff000000)): "RGBA",
 | 
			
		||||
                (32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA",
 | 
			
		||||
                (32, (0xFF0000, 0xFF00, 0xFF, 0x0)): "BGRX",
 | 
			
		||||
                (32, (0xFF000000, 0xFF0000, 0xFF00, 0x0)): "XBGR",
 | 
			
		||||
                (32, (0xFF, 0xFF00, 0xFF0000, 0xFF000000)): "RGBA",
 | 
			
		||||
                (32, (0xFF0000, 0xFF00, 0xFF, 0xFF000000)): "BGRA",
 | 
			
		||||
                (32, (0x0, 0x0, 0x0, 0x0)): "BGRA",
 | 
			
		||||
                (24, (0xff0000, 0xff00, 0xff)): "BGR",
 | 
			
		||||
                (16, (0xf800, 0x7e0, 0x1f)): "BGR;16",
 | 
			
		||||
                (16, (0x7c00, 0x3e0, 0x1f)): "BGR;15"
 | 
			
		||||
                (24, (0xFF0000, 0xFF00, 0xFF)): "BGR",
 | 
			
		||||
                (16, (0xF800, 0x7E0, 0x1F)): "BGR;16",
 | 
			
		||||
                (16, (0x7C00, 0x3E0, 0x1F)): "BGR;15",
 | 
			
		||||
            }
 | 
			
		||||
            if file_info['bits'] in SUPPORTED:
 | 
			
		||||
                if file_info['bits'] == 32 and \
 | 
			
		||||
                   file_info['rgba_mask'] in SUPPORTED[file_info['bits']]:
 | 
			
		||||
                    raw_mode = MASK_MODES[
 | 
			
		||||
                        (file_info["bits"], file_info["rgba_mask"])
 | 
			
		||||
                    ]
 | 
			
		||||
            if file_info["bits"] in SUPPORTED:
 | 
			
		||||
                if (
 | 
			
		||||
                    file_info["bits"] == 32
 | 
			
		||||
                    and file_info["rgba_mask"] in SUPPORTED[file_info["bits"]]
 | 
			
		||||
                ):
 | 
			
		||||
                    raw_mode = MASK_MODES[(file_info["bits"], file_info["rgba_mask"])]
 | 
			
		||||
                    self.mode = "RGBA" if "A" in raw_mode else self.mode
 | 
			
		||||
                elif (file_info['bits'] in (24, 16) and
 | 
			
		||||
                      file_info['rgb_mask'] in SUPPORTED[file_info['bits']]):
 | 
			
		||||
                    raw_mode = MASK_MODES[
 | 
			
		||||
                        (file_info['bits'], file_info['rgb_mask'])
 | 
			
		||||
                    ]
 | 
			
		||||
                elif (
 | 
			
		||||
                    file_info["bits"] in (24, 16)
 | 
			
		||||
                    and file_info["rgb_mask"] in SUPPORTED[file_info["bits"]]
 | 
			
		||||
                ):
 | 
			
		||||
                    raw_mode = MASK_MODES[(file_info["bits"], file_info["rgb_mask"])]
 | 
			
		||||
                else:
 | 
			
		||||
                    raise IOError("Unsupported BMP bitfields layout")
 | 
			
		||||
            else:
 | 
			
		||||
                raise IOError("Unsupported BMP bitfields layout")
 | 
			
		||||
        elif file_info['compression'] == self.RAW:
 | 
			
		||||
            if file_info['bits'] == 32 and header == 22:  # 32-bit .cur offset
 | 
			
		||||
        elif file_info["compression"] == self.RAW:
 | 
			
		||||
            if file_info["bits"] == 32 and header == 22:  # 32-bit .cur offset
 | 
			
		||||
                raw_mode, self.mode = "BGRA", "RGBA"
 | 
			
		||||
        else:
 | 
			
		||||
            raise IOError("Unsupported BMP compression (%d)" %
 | 
			
		||||
                          file_info['compression'])
 | 
			
		||||
            raise IOError("Unsupported BMP compression (%d)" % file_info["compression"])
 | 
			
		||||
 | 
			
		||||
        # --------------- Once the header is processed, process the palette/LUT
 | 
			
		||||
        if self.mode == "P":  # Paletted for 1, 4 and 8 bit images
 | 
			
		||||
 | 
			
		||||
            # ---------------------------------------------------- 1-bit images
 | 
			
		||||
            if not (0 < file_info['colors'] <= 65536):
 | 
			
		||||
                raise IOError("Unsupported BMP Palette size (%d)" %
 | 
			
		||||
                              file_info['colors'])
 | 
			
		||||
            if not (0 < file_info["colors"] <= 65536):
 | 
			
		||||
                raise IOError("Unsupported BMP Palette size (%d)" % file_info["colors"])
 | 
			
		||||
            else:
 | 
			
		||||
                padding = file_info['palette_padding']
 | 
			
		||||
                palette = read(padding * file_info['colors'])
 | 
			
		||||
                padding = file_info["palette_padding"]
 | 
			
		||||
                palette = read(padding * file_info["colors"])
 | 
			
		||||
                greyscale = True
 | 
			
		||||
                indices = (0, 255) if file_info['colors'] == 2 else \
 | 
			
		||||
                    list(range(file_info['colors']))
 | 
			
		||||
                indices = (
 | 
			
		||||
                    (0, 255)
 | 
			
		||||
                    if file_info["colors"] == 2
 | 
			
		||||
                    else list(range(file_info["colors"]))
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
                # ----------------- Check if greyscale and ignore palette if so
 | 
			
		||||
                for ind, val in enumerate(indices):
 | 
			
		||||
                    rgb = palette[ind*padding:ind*padding + 3]
 | 
			
		||||
                    rgb = palette[ind * padding : ind * padding + 3]
 | 
			
		||||
                    if rgb != o8(val) * 3:
 | 
			
		||||
                        greyscale = False
 | 
			
		||||
 | 
			
		||||
                # ------- If all colors are grey, white or black, ditch palette
 | 
			
		||||
                if greyscale:
 | 
			
		||||
                    self.mode = "1" if file_info['colors'] == 2 else "L"
 | 
			
		||||
                    self.mode = "1" if file_info["colors"] == 2 else "L"
 | 
			
		||||
                    raw_mode = self.mode
 | 
			
		||||
                else:
 | 
			
		||||
                    self.mode = "P"
 | 
			
		||||
                    self.palette = ImagePalette.raw(
 | 
			
		||||
                        "BGRX" if padding == 4 else "BGR", palette)
 | 
			
		||||
                        "BGRX" if padding == 4 else "BGR", palette
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
        # ---------------------------- Finally set the tile data for the plugin
 | 
			
		||||
        self.info['compression'] = file_info['compression']
 | 
			
		||||
        self.info["compression"] = file_info["compression"]
 | 
			
		||||
        self.tile = [
 | 
			
		||||
            ('raw',
 | 
			
		||||
             (0, 0, file_info['width'], file_info['height']),
 | 
			
		||||
            (
 | 
			
		||||
                "raw",
 | 
			
		||||
                (0, 0, file_info["width"], file_info["height"]),
 | 
			
		||||
                offset or self.fp.tell(),
 | 
			
		||||
             (raw_mode,
 | 
			
		||||
              ((file_info['width'] * file_info['bits'] + 31) >> 3) & (~3),
 | 
			
		||||
              file_info['direction']))
 | 
			
		||||
                (
 | 
			
		||||
                    raw_mode,
 | 
			
		||||
                    ((file_info["width"] * file_info["bits"] + 31) >> 3) & (~3),
 | 
			
		||||
                    file_info["direction"],
 | 
			
		||||
                ),
 | 
			
		||||
            )
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
    def _open(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -280,6 +285,7 @@ class DibImageFile(BmpImageFile):
 | 
			
		|||
    def _open(self):
 | 
			
		||||
        self._bitmap()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# --------------------------------------------------------------------
 | 
			
		||||
# Write BMP file
 | 
			
		||||
| 
						 | 
				
			
			@ -311,29 +317,34 @@ def _save(im, fp, filename, bitmap_header=True):
 | 
			
		|||
    # 1 meter == 39.3701 inches
 | 
			
		||||
    ppm = tuple(map(lambda x: int(x * 39.3701 + 0.5), dpi))
 | 
			
		||||
 | 
			
		||||
    stride = ((im.size[0]*bits+7)//8+3) & (~3)
 | 
			
		||||
    stride = ((im.size[0] * bits + 7) // 8 + 3) & (~3)
 | 
			
		||||
    header = 40  # or 64 for OS/2 version 2
 | 
			
		||||
    image = stride * im.size[1]
 | 
			
		||||
 | 
			
		||||
    # bitmap header
 | 
			
		||||
    if bitmap_header:
 | 
			
		||||
        offset = 14 + header + colors * 4
 | 
			
		||||
        fp.write(b"BM" +                      # file type (magic)
 | 
			
		||||
                 o32(offset+image) +          # file size
 | 
			
		||||
                 o32(0) +                     # reserved
 | 
			
		||||
                 o32(offset))                 # image data offset
 | 
			
		||||
        fp.write(
 | 
			
		||||
            b"BM"
 | 
			
		||||
            + o32(offset + image)  # file type (magic)
 | 
			
		||||
            + o32(0)  # file size
 | 
			
		||||
            + o32(offset)  # reserved
 | 
			
		||||
        )  # image data offset
 | 
			
		||||
 | 
			
		||||
    # bitmap info header
 | 
			
		||||
    fp.write(o32(header) +                # info header size
 | 
			
		||||
             o32(im.size[0]) +            # width
 | 
			
		||||
             o32(im.size[1]) +            # height
 | 
			
		||||
             o16(1) +                     # planes
 | 
			
		||||
             o16(bits) +                  # depth
 | 
			
		||||
             o32(0) +                     # compression (0=uncompressed)
 | 
			
		||||
             o32(image) +                 # size of bitmap
 | 
			
		||||
             o32(ppm[0]) + o32(ppm[1]) +  # resolution
 | 
			
		||||
             o32(colors) +                # colors used
 | 
			
		||||
             o32(colors))                 # colors important
 | 
			
		||||
    fp.write(
 | 
			
		||||
        o32(header)  # info header size
 | 
			
		||||
        + o32(im.size[0])  # width
 | 
			
		||||
        + o32(im.size[1])  # height
 | 
			
		||||
        + o16(1)  # planes
 | 
			
		||||
        + o16(bits)  # depth
 | 
			
		||||
        + o32(0)  # compression (0=uncompressed)
 | 
			
		||||
        + o32(image)  # size of bitmap
 | 
			
		||||
        + o32(ppm[0])  # resolution
 | 
			
		||||
        + o32(ppm[1])  # resolution
 | 
			
		||||
        + o32(colors)  # colors used
 | 
			
		||||
        + o32(colors)  # colors important
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    fp.write(b"\0" * (header - 40))  # padding (for OS/2 format)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -346,8 +357,8 @@ def _save(im, fp, filename, bitmap_header=True):
 | 
			
		|||
    elif im.mode == "P":
 | 
			
		||||
        fp.write(im.im.getpalette("RGB", "BGRX"))
 | 
			
		||||
 | 
			
		||||
    ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0,
 | 
			
		||||
                    (rawmode, stride, -1))])
 | 
			
		||||
    ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, stride, -1))])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# --------------------------------------------------------------------
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,6 +27,7 @@ def register_handler(handler):
 | 
			
		|||
# --------------------------------------------------------------------
 | 
			
		||||
# Image adapter
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _accept(prefix):
 | 
			
		||||
    return prefix[:4] == b"BUFR" or prefix[:4] == b"ZCZC"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,7 +22,6 @@ import io
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
class ContainerIO(object):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, file, offset, length):
 | 
			
		||||
        """
 | 
			
		||||
        Create file object.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,6 +36,7 @@ def _accept(prefix):
 | 
			
		|||
##
 | 
			
		||||
# Image plugin for Windows Cursor files.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CurImageFile(BmpImagePlugin.BmpImageFile):
 | 
			
		||||
 | 
			
		||||
    format = "CUR"
 | 
			
		||||
| 
						 | 
				
			
			@ -65,9 +66,9 @@ class CurImageFile(BmpImagePlugin.BmpImageFile):
 | 
			
		|||
        self._bitmap(i32(m[12:]) + offset)
 | 
			
		||||
 | 
			
		||||
        # patch up the bitmap height
 | 
			
		||||
        self._size = self.size[0], self.size[1]//2
 | 
			
		||||
        self._size = self.size[0], self.size[1] // 2
 | 
			
		||||
        d, e, o, a = self.tile[0]
 | 
			
		||||
        self.tile[0] = d, (0, 0)+self.size, o, a
 | 
			
		||||
        self.tile[0] = d, (0, 0) + self.size, o, a
 | 
			
		||||
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -39,6 +39,7 @@ def _accept(prefix):
 | 
			
		|||
##
 | 
			
		||||
# Image plugin for the Intel DCX format.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DcxImageFile(PcxImageFile):
 | 
			
		||||
 | 
			
		||||
    format = "DCX"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -61,8 +61,7 @@ DDS_LUMINANCEA = DDPF_LUMINANCE | DDPF_ALPHAPIXELS
 | 
			
		|||
DDS_ALPHA = DDPF_ALPHA
 | 
			
		||||
DDS_PAL8 = DDPF_PALETTEINDEXED8
 | 
			
		||||
 | 
			
		||||
DDS_HEADER_FLAGS_TEXTURE = (DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH |
 | 
			
		||||
                            DDSD_PIXELFORMAT)
 | 
			
		||||
DDS_HEADER_FLAGS_TEXTURE = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT
 | 
			
		||||
DDS_HEADER_FLAGS_MIPMAP = DDSD_MIPMAPCOUNT
 | 
			
		||||
DDS_HEADER_FLAGS_VOLUME = DDSD_DEPTH
 | 
			
		||||
DDS_HEADER_FLAGS_PITCH = DDSD_PITCH
 | 
			
		||||
| 
						 | 
				
			
			@ -130,8 +129,8 @@ class DdsImageFile(ImageFile.ImageFile):
 | 
			
		|||
            masks = {mask: ["R", "G", "B", "A"][i] for i, mask in enumerate(masks)}
 | 
			
		||||
            rawmode = ""
 | 
			
		||||
            if bitcount == 32:
 | 
			
		||||
                rawmode += masks[0xff000000]
 | 
			
		||||
            rawmode += masks[0xff0000] + masks[0xff00] + masks[0xff]
 | 
			
		||||
                rawmode += masks[0xFF000000]
 | 
			
		||||
            rawmode += masks[0xFF0000] + masks[0xFF00] + masks[0xFF]
 | 
			
		||||
 | 
			
		||||
            self.tile = [("raw", (0, 0) + self.size, 0, (rawmode, 0, 1))]
 | 
			
		||||
        else:
 | 
			
		||||
| 
						 | 
				
			
			@ -151,24 +150,21 @@ class DdsImageFile(ImageFile.ImageFile):
 | 
			
		|||
                # ignoring flags which pertain to volume textures and cubemaps
 | 
			
		||||
                dxt10 = BytesIO(self.fp.read(20))
 | 
			
		||||
                dxgi_format, dimension = struct.unpack("<II", dxt10.read(8))
 | 
			
		||||
                if dxgi_format in (DXGI_FORMAT_BC7_TYPELESS,
 | 
			
		||||
                                   DXGI_FORMAT_BC7_UNORM):
 | 
			
		||||
                if dxgi_format in (DXGI_FORMAT_BC7_TYPELESS, DXGI_FORMAT_BC7_UNORM):
 | 
			
		||||
                    self.pixel_format = "BC7"
 | 
			
		||||
                    n = 7
 | 
			
		||||
                elif dxgi_format == DXGI_FORMAT_BC7_UNORM_SRGB:
 | 
			
		||||
                    self.pixel_format = "BC7"
 | 
			
		||||
                    self.im_info["gamma"] = 1/2.2
 | 
			
		||||
                    self.im_info["gamma"] = 1 / 2.2
 | 
			
		||||
                    n = 7
 | 
			
		||||
                else:
 | 
			
		||||
                    raise NotImplementedError("Unimplemented DXGI format %d" %
 | 
			
		||||
                                              (dxgi_format))
 | 
			
		||||
                    raise NotImplementedError(
 | 
			
		||||
                        "Unimplemented DXGI format %d" % (dxgi_format)
 | 
			
		||||
                    )
 | 
			
		||||
            else:
 | 
			
		||||
                raise NotImplementedError("Unimplemented pixel format %r" %
 | 
			
		||||
                                          (fourcc))
 | 
			
		||||
                raise NotImplementedError("Unimplemented pixel format %r" % (fourcc))
 | 
			
		||||
 | 
			
		||||
            self.tile = [
 | 
			
		||||
                ("bcn", (0, 0) + self.size, data_start, (n))
 | 
			
		||||
            ]
 | 
			
		||||
            self.tile = [("bcn", (0, 0) + self.size, data_start, (n))]
 | 
			
		||||
 | 
			
		||||
    def load_seek(self, pos):
 | 
			
		||||
        pass
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,15 +38,17 @@ split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$")
 | 
			
		|||
field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$")
 | 
			
		||||
 | 
			
		||||
gs_windows_binary = None
 | 
			
		||||
if sys.platform.startswith('win'):
 | 
			
		||||
if sys.platform.startswith("win"):
 | 
			
		||||
    import shutil
 | 
			
		||||
    if hasattr(shutil, 'which'):
 | 
			
		||||
 | 
			
		||||
    if hasattr(shutil, "which"):
 | 
			
		||||
        which = shutil.which
 | 
			
		||||
    else:
 | 
			
		||||
        # Python 2
 | 
			
		||||
        import distutils.spawn
 | 
			
		||||
 | 
			
		||||
        which = distutils.spawn.find_executable
 | 
			
		||||
    for binary in ('gswin32c', 'gswin64c', 'gs'):
 | 
			
		||||
    for binary in ("gswin32c", "gswin64c", "gs"):
 | 
			
		||||
        if which(binary) is not None:
 | 
			
		||||
            gs_windows_binary = binary
 | 
			
		||||
            break
 | 
			
		||||
| 
						 | 
				
			
			@ -57,11 +59,12 @@ if sys.platform.startswith('win'):
 | 
			
		|||
def has_ghostscript():
 | 
			
		||||
    if gs_windows_binary:
 | 
			
		||||
        return True
 | 
			
		||||
    if not sys.platform.startswith('win'):
 | 
			
		||||
    if not sys.platform.startswith("win"):
 | 
			
		||||
        import subprocess
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            with open(os.devnull, 'wb') as devnull:
 | 
			
		||||
                subprocess.check_call(['gs', '--version'], stdout=devnull)
 | 
			
		||||
            with open(os.devnull, "wb") as devnull:
 | 
			
		||||
                subprocess.check_call(["gs", "--version"], stdout=devnull)
 | 
			
		||||
            return True
 | 
			
		||||
        except OSError:
 | 
			
		||||
            # No Ghostscript
 | 
			
		||||
| 
						 | 
				
			
			@ -82,8 +85,10 @@ def Ghostscript(tile, size, fp, scale=1):
 | 
			
		|||
    # orig_bbox = bbox
 | 
			
		||||
    size = (size[0] * scale, size[1] * scale)
 | 
			
		||||
    # resolution is dependent on bbox and size
 | 
			
		||||
    res = (float((72.0 * size[0]) / (bbox[2]-bbox[0])),
 | 
			
		||||
           float((72.0 * size[1]) / (bbox[3]-bbox[1])))
 | 
			
		||||
    res = (
 | 
			
		||||
        float((72.0 * size[0]) / (bbox[2] - bbox[0])),
 | 
			
		||||
        float((72.0 * size[1]) / (bbox[3] - bbox[1])),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    import subprocess
 | 
			
		||||
    import tempfile
 | 
			
		||||
| 
						 | 
				
			
			@ -92,7 +97,7 @@ def Ghostscript(tile, size, fp, scale=1):
 | 
			
		|||
    os.close(out_fd)
 | 
			
		||||
 | 
			
		||||
    infile_temp = None
 | 
			
		||||
    if hasattr(fp, 'name') and os.path.exists(fp.name):
 | 
			
		||||
    if hasattr(fp, "name") and os.path.exists(fp.name):
 | 
			
		||||
        infile = fp.name
 | 
			
		||||
    else:
 | 
			
		||||
        in_fd, infile_temp = tempfile.mkstemp()
 | 
			
		||||
| 
						 | 
				
			
			@ -102,7 +107,7 @@ def Ghostscript(tile, size, fp, scale=1):
 | 
			
		|||
        # Ignore length and offset!
 | 
			
		||||
        # Ghostscript can read it
 | 
			
		||||
        # Copy whole file to read in Ghostscript
 | 
			
		||||
        with open(infile_temp, 'wb') as f:
 | 
			
		||||
        with open(infile_temp, "wb") as f:
 | 
			
		||||
            # fetch length of fp
 | 
			
		||||
            fp.seek(0, io.SEEK_END)
 | 
			
		||||
            fsize = fp.tell()
 | 
			
		||||
| 
						 | 
				
			
			@ -111,14 +116,15 @@ def Ghostscript(tile, size, fp, scale=1):
 | 
			
		|||
            fp.seek(0)
 | 
			
		||||
            lengthfile = fsize
 | 
			
		||||
            while lengthfile > 0:
 | 
			
		||||
                s = fp.read(min(lengthfile, 100*1024))
 | 
			
		||||
                s = fp.read(min(lengthfile, 100 * 1024))
 | 
			
		||||
                if not s:
 | 
			
		||||
                    break
 | 
			
		||||
                lengthfile -= len(s)
 | 
			
		||||
                f.write(s)
 | 
			
		||||
 | 
			
		||||
    # Build Ghostscript command
 | 
			
		||||
    command = ["gs",
 | 
			
		||||
    command = [
 | 
			
		||||
        "gs",
 | 
			
		||||
        "-q",  # quiet mode
 | 
			
		||||
        "-g%dx%d" % size,  # set output geometry (pixels)
 | 
			
		||||
        "-r%fx%f" % res,  # set input DPI (dots per inch)
 | 
			
		||||
| 
						 | 
				
			
			@ -128,21 +134,24 @@ def Ghostscript(tile, size, fp, scale=1):
 | 
			
		|||
        "-sDEVICE=ppmraw",  # ppm driver
 | 
			
		||||
        "-sOutputFile=%s" % outfile,  # output file
 | 
			
		||||
        # adjust for image origin
 | 
			
		||||
               "-c", "%d %d translate" % (-bbox[0], -bbox[1]),
 | 
			
		||||
               "-f", infile,                 # input file
 | 
			
		||||
        "-c",
 | 
			
		||||
        "%d %d translate" % (-bbox[0], -bbox[1]),
 | 
			
		||||
        "-f",
 | 
			
		||||
        infile,  # input file
 | 
			
		||||
        # showpage (see https://bugs.ghostscript.com/show_bug.cgi?id=698272)
 | 
			
		||||
               "-c", "showpage",
 | 
			
		||||
        "-c",
 | 
			
		||||
        "showpage",
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    if gs_windows_binary is not None:
 | 
			
		||||
        if not gs_windows_binary:
 | 
			
		||||
            raise WindowsError('Unable to locate Ghostscript on paths')
 | 
			
		||||
            raise WindowsError("Unable to locate Ghostscript on paths")
 | 
			
		||||
        command[0] = gs_windows_binary
 | 
			
		||||
 | 
			
		||||
    # push data through Ghostscript
 | 
			
		||||
    try:
 | 
			
		||||
        startupinfo = None
 | 
			
		||||
        if sys.platform.startswith('win'):
 | 
			
		||||
        if sys.platform.startswith("win"):
 | 
			
		||||
            startupinfo = subprocess.STARTUPINFO()
 | 
			
		||||
            startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
 | 
			
		||||
        subprocess.check_call(command, startupinfo=startupinfo)
 | 
			
		||||
| 
						 | 
				
			
			@ -163,6 +172,7 @@ class PSFile(object):
 | 
			
		|||
    """
 | 
			
		||||
    Wrapper for bytesio object that treats either CR or LF as end of line.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, fp):
 | 
			
		||||
        self.fp = fp
 | 
			
		||||
        self.char = None
 | 
			
		||||
| 
						 | 
				
			
			@ -185,12 +195,12 @@ class PSFile(object):
 | 
			
		|||
        if self.char in b"\r\n":
 | 
			
		||||
            self.char = None
 | 
			
		||||
 | 
			
		||||
        return s.decode('latin-1')
 | 
			
		||||
        return s.decode("latin-1")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _accept(prefix):
 | 
			
		||||
    return prefix[:4] == b"%!PS" or \
 | 
			
		||||
           (len(prefix) >= 4 and i32(prefix) == 0xC6D3D0C5)
 | 
			
		||||
    return prefix[:4] == b"%!PS" or (len(prefix) >= 4 and i32(prefix) == 0xC6D3D0C5)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
# Image plugin for Encapsulated Postscript.  This plugin supports only
 | 
			
		||||
| 
						 | 
				
			
			@ -224,7 +234,7 @@ class EpsImageFile(ImageFile.ImageFile):
 | 
			
		|||
        # Load EPS header
 | 
			
		||||
 | 
			
		||||
        s_raw = fp.readline()
 | 
			
		||||
        s = s_raw.strip('\r\n')
 | 
			
		||||
        s = s_raw.strip("\r\n")
 | 
			
		||||
 | 
			
		||||
        while s_raw:
 | 
			
		||||
            if s:
 | 
			
		||||
| 
						 | 
				
			
			@ -246,8 +256,9 @@ class EpsImageFile(ImageFile.ImageFile):
 | 
			
		|||
                            # put floating point values there anyway.
 | 
			
		||||
                            box = [int(float(i)) for i in v.split()]
 | 
			
		||||
                            self._size = box[2] - box[0], box[3] - box[1]
 | 
			
		||||
                            self.tile = [("eps", (0, 0) + self.size, offset,
 | 
			
		||||
                                          (length, box))]
 | 
			
		||||
                            self.tile = [
 | 
			
		||||
                                ("eps", (0, 0) + self.size, offset, (length, box))
 | 
			
		||||
                            ]
 | 
			
		||||
                        except Exception:
 | 
			
		||||
                            pass
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -262,7 +273,7 @@ class EpsImageFile(ImageFile.ImageFile):
 | 
			
		|||
                            self.info[k[:8]] = k[9:]
 | 
			
		||||
                        else:
 | 
			
		||||
                            self.info[k] = ""
 | 
			
		||||
                    elif s[0] == '%':
 | 
			
		||||
                    elif s[0] == "%":
 | 
			
		||||
                        # handle non-DSC Postscript comments that some
 | 
			
		||||
                        # tools mistakenly put in the Comments section
 | 
			
		||||
                        pass
 | 
			
		||||
| 
						 | 
				
			
			@ -270,7 +281,7 @@ class EpsImageFile(ImageFile.ImageFile):
 | 
			
		|||
                        raise IOError("bad EPS header")
 | 
			
		||||
 | 
			
		||||
            s_raw = fp.readline()
 | 
			
		||||
            s = s_raw.strip('\r\n')
 | 
			
		||||
            s = s_raw.strip("\r\n")
 | 
			
		||||
 | 
			
		||||
            if s and s[:1] != "%":
 | 
			
		||||
                break
 | 
			
		||||
| 
						 | 
				
			
			@ -297,7 +308,7 @@ class EpsImageFile(ImageFile.ImageFile):
 | 
			
		|||
                self._size = int(x), int(y)
 | 
			
		||||
                return
 | 
			
		||||
 | 
			
		||||
            s = fp.readline().strip('\r\n')
 | 
			
		||||
            s = fp.readline().strip("\r\n")
 | 
			
		||||
            if not s:
 | 
			
		||||
                break
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -344,6 +355,7 @@ class EpsImageFile(ImageFile.ImageFile):
 | 
			
		|||
#
 | 
			
		||||
# --------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _save(im, fp, filename, eps=1):
 | 
			
		||||
    """EPS Writer for the Python Imaging Library."""
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -366,7 +378,7 @@ def _save(im, fp, filename, eps=1):
 | 
			
		|||
    wrapped_fp = False
 | 
			
		||||
    if fp != sys.stdout:
 | 
			
		||||
        if sys.version_info.major > 2:
 | 
			
		||||
            fp = io.TextIOWrapper(fp, encoding='latin-1')
 | 
			
		||||
            fp = io.TextIOWrapper(fp, encoding="latin-1")
 | 
			
		||||
            wrapped_fp = True
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
| 
						 | 
				
			
			@ -381,7 +393,7 @@ def _save(im, fp, filename, eps=1):
 | 
			
		|||
            fp.write("%%EndComments\n")
 | 
			
		||||
            fp.write("%%Page: 1 1\n")
 | 
			
		||||
            fp.write("%%ImageData: %d %d " % im.size)
 | 
			
		||||
            fp.write("%d %d 0 1 1 \"%s\"\n" % operator)
 | 
			
		||||
            fp.write('%d %d 0 1 1 "%s"\n' % operator)
 | 
			
		||||
 | 
			
		||||
        #
 | 
			
		||||
        # image header
 | 
			
		||||
| 
						 | 
				
			
			@ -396,7 +408,7 @@ def _save(im, fp, filename, eps=1):
 | 
			
		|||
        if hasattr(fp, "flush"):
 | 
			
		||||
            fp.flush()
 | 
			
		||||
 | 
			
		||||
        ImageFile._save(im, base_fp, [("eps", (0, 0)+im.size, 0, None)])
 | 
			
		||||
        ImageFile._save(im, base_fp, [("eps", (0, 0) + im.size, 0, None)])
 | 
			
		||||
 | 
			
		||||
        fp.write("\n%%%%EndBinary\n")
 | 
			
		||||
        fp.write("grestore end\n")
 | 
			
		||||
| 
						 | 
				
			
			@ -406,6 +418,7 @@ def _save(im, fp, filename, eps=1):
 | 
			
		|||
        if wrapped_fp:
 | 
			
		||||
            fp.detach()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# --------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,11 +18,10 @@
 | 
			
		|||
# Maps EXIF tags to tag names.
 | 
			
		||||
 | 
			
		||||
TAGS = {
 | 
			
		||||
 | 
			
		||||
    # possibly incomplete
 | 
			
		||||
    0x000b: "ProcessingSoftware",
 | 
			
		||||
    0x00fe: "NewSubfileType",
 | 
			
		||||
    0x00ff: "SubfileType",
 | 
			
		||||
    0x000B: "ProcessingSoftware",
 | 
			
		||||
    0x00FE: "NewSubfileType",
 | 
			
		||||
    0x00FF: "SubfileType",
 | 
			
		||||
    0x0100: "ImageWidth",
 | 
			
		||||
    0x0101: "ImageLength",
 | 
			
		||||
    0x0102: "BitsPerSample",
 | 
			
		||||
| 
						 | 
				
			
			@ -31,10 +30,10 @@ TAGS = {
 | 
			
		|||
    0x0107: "Thresholding",
 | 
			
		||||
    0x0108: "CellWidth",
 | 
			
		||||
    0x0109: "CellLength",
 | 
			
		||||
    0x010a: "FillOrder",
 | 
			
		||||
    0x010d: "DocumentName",
 | 
			
		||||
    0x010e: "ImageDescription",
 | 
			
		||||
    0x010f: "Make",
 | 
			
		||||
    0x010A: "FillOrder",
 | 
			
		||||
    0x010D: "DocumentName",
 | 
			
		||||
    0x010E: "ImageDescription",
 | 
			
		||||
    0x010F: "Make",
 | 
			
		||||
    0x0110: "Model",
 | 
			
		||||
    0x0111: "StripOffsets",
 | 
			
		||||
    0x0112: "Orientation",
 | 
			
		||||
| 
						 | 
				
			
			@ -43,10 +42,10 @@ TAGS = {
 | 
			
		|||
    0x0117: "StripByteCounts",
 | 
			
		||||
    0x0118: "MinSampleValue",
 | 
			
		||||
    0x0119: "MaxSampleValue",
 | 
			
		||||
    0x011a: "XResolution",
 | 
			
		||||
    0x011b: "YResolution",
 | 
			
		||||
    0x011c: "PlanarConfiguration",
 | 
			
		||||
    0x011d: "PageName",
 | 
			
		||||
    0x011A: "XResolution",
 | 
			
		||||
    0x011B: "YResolution",
 | 
			
		||||
    0x011C: "PlanarConfiguration",
 | 
			
		||||
    0x011D: "PageName",
 | 
			
		||||
    0x0120: "FreeOffsets",
 | 
			
		||||
    0x0121: "FreeByteCounts",
 | 
			
		||||
    0x0122: "GrayResponseUnit",
 | 
			
		||||
| 
						 | 
				
			
			@ -55,24 +54,24 @@ TAGS = {
 | 
			
		|||
    0x0125: "T6Options",
 | 
			
		||||
    0x0128: "ResolutionUnit",
 | 
			
		||||
    0x0129: "PageNumber",
 | 
			
		||||
    0x012d: "TransferFunction",
 | 
			
		||||
    0x012D: "TransferFunction",
 | 
			
		||||
    0x0131: "Software",
 | 
			
		||||
    0x0132: "DateTime",
 | 
			
		||||
    0x013b: "Artist",
 | 
			
		||||
    0x013c: "HostComputer",
 | 
			
		||||
    0x013d: "Predictor",
 | 
			
		||||
    0x013e: "WhitePoint",
 | 
			
		||||
    0x013f: "PrimaryChromaticities",
 | 
			
		||||
    0x013B: "Artist",
 | 
			
		||||
    0x013C: "HostComputer",
 | 
			
		||||
    0x013D: "Predictor",
 | 
			
		||||
    0x013E: "WhitePoint",
 | 
			
		||||
    0x013F: "PrimaryChromaticities",
 | 
			
		||||
    0x0140: "ColorMap",
 | 
			
		||||
    0x0141: "HalftoneHints",
 | 
			
		||||
    0x0142: "TileWidth",
 | 
			
		||||
    0x0143: "TileLength",
 | 
			
		||||
    0x0144: "TileOffsets",
 | 
			
		||||
    0x0145: "TileByteCounts",
 | 
			
		||||
    0x014a: "SubIFDs",
 | 
			
		||||
    0x014c: "InkSet",
 | 
			
		||||
    0x014d: "InkNames",
 | 
			
		||||
    0x014e: "NumberOfInks",
 | 
			
		||||
    0x014A: "SubIFDs",
 | 
			
		||||
    0x014C: "InkSet",
 | 
			
		||||
    0x014D: "InkNames",
 | 
			
		||||
    0x014E: "NumberOfInks",
 | 
			
		||||
    0x0150: "DotRange",
 | 
			
		||||
    0x0151: "TargetPrinter",
 | 
			
		||||
    0x0152: "ExtraSamples",
 | 
			
		||||
| 
						 | 
				
			
			@ -83,9 +82,9 @@ TAGS = {
 | 
			
		|||
    0x0157: "ClipPath",
 | 
			
		||||
    0x0158: "XClipPathUnits",
 | 
			
		||||
    0x0159: "YClipPathUnits",
 | 
			
		||||
    0x015a: "Indexed",
 | 
			
		||||
    0x015b: "JPEGTables",
 | 
			
		||||
    0x015f: "OPIProxy",
 | 
			
		||||
    0x015A: "Indexed",
 | 
			
		||||
    0x015B: "JPEGTables",
 | 
			
		||||
    0x015F: "OPIProxy",
 | 
			
		||||
    0x0200: "JPEGProc",
 | 
			
		||||
    0x0201: "JpegIFOffset",
 | 
			
		||||
    0x0202: "JpegIFByteCount",
 | 
			
		||||
| 
						 | 
				
			
			@ -99,20 +98,20 @@ TAGS = {
 | 
			
		|||
    0x0212: "YCbCrSubSampling",
 | 
			
		||||
    0x0213: "YCbCrPositioning",
 | 
			
		||||
    0x0214: "ReferenceBlackWhite",
 | 
			
		||||
    0x02bc: "XMLPacket",
 | 
			
		||||
    0x02BC: "XMLPacket",
 | 
			
		||||
    0x1000: "RelatedImageFileFormat",
 | 
			
		||||
    0x1001: "RelatedImageWidth",
 | 
			
		||||
    0x1002: "RelatedImageLength",
 | 
			
		||||
    0x4746: "Rating",
 | 
			
		||||
    0x4749: "RatingPercent",
 | 
			
		||||
    0x800d: "ImageID",
 | 
			
		||||
    0x828d: "CFARepeatPatternDim",
 | 
			
		||||
    0x828e: "CFAPattern",
 | 
			
		||||
    0x828f: "BatteryLevel",
 | 
			
		||||
    0x800D: "ImageID",
 | 
			
		||||
    0x828D: "CFARepeatPatternDim",
 | 
			
		||||
    0x828E: "CFAPattern",
 | 
			
		||||
    0x828F: "BatteryLevel",
 | 
			
		||||
    0x8298: "Copyright",
 | 
			
		||||
    0x829a: "ExposureTime",
 | 
			
		||||
    0x829d: "FNumber",
 | 
			
		||||
    0x83bb: "IPTCNAA",
 | 
			
		||||
    0x829A: "ExposureTime",
 | 
			
		||||
    0x829D: "FNumber",
 | 
			
		||||
    0x83BB: "IPTCNAA",
 | 
			
		||||
    0x8649: "ImageResources",
 | 
			
		||||
    0x8769: "ExifOffset",
 | 
			
		||||
    0x8773: "InterColorProfile",
 | 
			
		||||
| 
						 | 
				
			
			@ -122,8 +121,8 @@ TAGS = {
 | 
			
		|||
    0x8827: "ISOSpeedRatings",
 | 
			
		||||
    0x8828: "OECF",
 | 
			
		||||
    0x8829: "Interlace",
 | 
			
		||||
    0x882a: "TimeZoneOffset",
 | 
			
		||||
    0x882b: "SelfTimerMode",
 | 
			
		||||
    0x882A: "TimeZoneOffset",
 | 
			
		||||
    0x882B: "SelfTimerMode",
 | 
			
		||||
    0x9000: "ExifVersion",
 | 
			
		||||
    0x9003: "DateTimeOriginal",
 | 
			
		||||
    0x9004: "DateTimeDigitized",
 | 
			
		||||
| 
						 | 
				
			
			@ -138,142 +137,142 @@ TAGS = {
 | 
			
		|||
    0x9207: "MeteringMode",
 | 
			
		||||
    0x9208: "LightSource",
 | 
			
		||||
    0x9209: "Flash",
 | 
			
		||||
    0x920a: "FocalLength",
 | 
			
		||||
    0x920b: "FlashEnergy",
 | 
			
		||||
    0x920c: "SpatialFrequencyResponse",
 | 
			
		||||
    0x920d: "Noise",
 | 
			
		||||
    0x920A: "FocalLength",
 | 
			
		||||
    0x920B: "FlashEnergy",
 | 
			
		||||
    0x920C: "SpatialFrequencyResponse",
 | 
			
		||||
    0x920D: "Noise",
 | 
			
		||||
    0x9211: "ImageNumber",
 | 
			
		||||
    0x9212: "SecurityClassification",
 | 
			
		||||
    0x9213: "ImageHistory",
 | 
			
		||||
    0x9214: "SubjectLocation",
 | 
			
		||||
    0x9215: "ExposureIndex",
 | 
			
		||||
    0x9216: "TIFF/EPStandardID",
 | 
			
		||||
    0x927c: "MakerNote",
 | 
			
		||||
    0x927C: "MakerNote",
 | 
			
		||||
    0x9286: "UserComment",
 | 
			
		||||
    0x9290: "SubsecTime",
 | 
			
		||||
    0x9291: "SubsecTimeOriginal",
 | 
			
		||||
    0x9292: "SubsecTimeDigitized",
 | 
			
		||||
    0x9c9b: "XPTitle",
 | 
			
		||||
    0x9c9c: "XPComment",
 | 
			
		||||
    0x9c9d: "XPAuthor",
 | 
			
		||||
    0x9c9e: "XPKeywords",
 | 
			
		||||
    0x9c9f: "XPSubject",
 | 
			
		||||
    0xa000: "FlashPixVersion",
 | 
			
		||||
    0xa001: "ColorSpace",
 | 
			
		||||
    0xa002: "ExifImageWidth",
 | 
			
		||||
    0xa003: "ExifImageHeight",
 | 
			
		||||
    0xa004: "RelatedSoundFile",
 | 
			
		||||
    0xa005: "ExifInteroperabilityOffset",
 | 
			
		||||
    0xa20b: "FlashEnergy",
 | 
			
		||||
    0xa20c: "SpatialFrequencyResponse",
 | 
			
		||||
    0xa20e: "FocalPlaneXResolution",
 | 
			
		||||
    0xa20f: "FocalPlaneYResolution",
 | 
			
		||||
    0xa210: "FocalPlaneResolutionUnit",
 | 
			
		||||
    0xa214: "SubjectLocation",
 | 
			
		||||
    0xa215: "ExposureIndex",
 | 
			
		||||
    0xa217: "SensingMethod",
 | 
			
		||||
    0xa300: "FileSource",
 | 
			
		||||
    0xa301: "SceneType",
 | 
			
		||||
    0xa302: "CFAPattern",
 | 
			
		||||
    0xa401: "CustomRendered",
 | 
			
		||||
    0xa402: "ExposureMode",
 | 
			
		||||
    0xa403: "WhiteBalance",
 | 
			
		||||
    0xa404: "DigitalZoomRatio",
 | 
			
		||||
    0xa405: "FocalLengthIn35mmFilm",
 | 
			
		||||
    0xa406: "SceneCaptureType",
 | 
			
		||||
    0xa407: "GainControl",
 | 
			
		||||
    0xa408: "Contrast",
 | 
			
		||||
    0xa409: "Saturation",
 | 
			
		||||
    0xa40a: "Sharpness",
 | 
			
		||||
    0xa40b: "DeviceSettingDescription",
 | 
			
		||||
    0xa40c: "SubjectDistanceRange",
 | 
			
		||||
    0xa420: "ImageUniqueID",
 | 
			
		||||
    0xa430: "CameraOwnerName",
 | 
			
		||||
    0xa431: "BodySerialNumber",
 | 
			
		||||
    0xa432: "LensSpecification",
 | 
			
		||||
    0xa433: "LensMake",
 | 
			
		||||
    0xa434: "LensModel",
 | 
			
		||||
    0xa435: "LensSerialNumber",
 | 
			
		||||
    0xa500: "Gamma",
 | 
			
		||||
    0xc4a5: "PrintImageMatching",
 | 
			
		||||
    0xc612: "DNGVersion",
 | 
			
		||||
    0xc613: "DNGBackwardVersion",
 | 
			
		||||
    0xc614: "UniqueCameraModel",
 | 
			
		||||
    0xc615: "LocalizedCameraModel",
 | 
			
		||||
    0xc616: "CFAPlaneColor",
 | 
			
		||||
    0xc617: "CFALayout",
 | 
			
		||||
    0xc618: "LinearizationTable",
 | 
			
		||||
    0xc619: "BlackLevelRepeatDim",
 | 
			
		||||
    0xc61a: "BlackLevel",
 | 
			
		||||
    0xc61b: "BlackLevelDeltaH",
 | 
			
		||||
    0xc61c: "BlackLevelDeltaV",
 | 
			
		||||
    0xc61d: "WhiteLevel",
 | 
			
		||||
    0xc61e: "DefaultScale",
 | 
			
		||||
    0xc61f: "DefaultCropOrigin",
 | 
			
		||||
    0xc620: "DefaultCropSize",
 | 
			
		||||
    0xc621: "ColorMatrix1",
 | 
			
		||||
    0xc622: "ColorMatrix2",
 | 
			
		||||
    0xc623: "CameraCalibration1",
 | 
			
		||||
    0xc624: "CameraCalibration2",
 | 
			
		||||
    0xc625: "ReductionMatrix1",
 | 
			
		||||
    0xc626: "ReductionMatrix2",
 | 
			
		||||
    0xc627: "AnalogBalance",
 | 
			
		||||
    0xc628: "AsShotNeutral",
 | 
			
		||||
    0xc629: "AsShotWhiteXY",
 | 
			
		||||
    0xc62a: "BaselineExposure",
 | 
			
		||||
    0xc62b: "BaselineNoise",
 | 
			
		||||
    0xc62c: "BaselineSharpness",
 | 
			
		||||
    0xc62d: "BayerGreenSplit",
 | 
			
		||||
    0xc62e: "LinearResponseLimit",
 | 
			
		||||
    0xc62f: "CameraSerialNumber",
 | 
			
		||||
    0xc630: "LensInfo",
 | 
			
		||||
    0xc631: "ChromaBlurRadius",
 | 
			
		||||
    0xc632: "AntiAliasStrength",
 | 
			
		||||
    0xc633: "ShadowScale",
 | 
			
		||||
    0xc634: "DNGPrivateData",
 | 
			
		||||
    0xc635: "MakerNoteSafety",
 | 
			
		||||
    0xc65a: "CalibrationIlluminant1",
 | 
			
		||||
    0xc65b: "CalibrationIlluminant2",
 | 
			
		||||
    0xc65c: "BestQualityScale",
 | 
			
		||||
    0xc65d: "RawDataUniqueID",
 | 
			
		||||
    0xc68b: "OriginalRawFileName",
 | 
			
		||||
    0xc68c: "OriginalRawFileData",
 | 
			
		||||
    0xc68d: "ActiveArea",
 | 
			
		||||
    0xc68e: "MaskedAreas",
 | 
			
		||||
    0xc68f: "AsShotICCProfile",
 | 
			
		||||
    0xc690: "AsShotPreProfileMatrix",
 | 
			
		||||
    0xc691: "CurrentICCProfile",
 | 
			
		||||
    0xc692: "CurrentPreProfileMatrix",
 | 
			
		||||
    0xc6bf: "ColorimetricReference",
 | 
			
		||||
    0xc6f3: "CameraCalibrationSignature",
 | 
			
		||||
    0xc6f4: "ProfileCalibrationSignature",
 | 
			
		||||
    0xc6f6: "AsShotProfileName",
 | 
			
		||||
    0xc6f7: "NoiseReductionApplied",
 | 
			
		||||
    0xc6f8: "ProfileName",
 | 
			
		||||
    0xc6f9: "ProfileHueSatMapDims",
 | 
			
		||||
    0xc6fa: "ProfileHueSatMapData1",
 | 
			
		||||
    0xc6fb: "ProfileHueSatMapData2",
 | 
			
		||||
    0xc6fc: "ProfileToneCurve",
 | 
			
		||||
    0xc6fd: "ProfileEmbedPolicy",
 | 
			
		||||
    0xc6fe: "ProfileCopyright",
 | 
			
		||||
    0xc714: "ForwardMatrix1",
 | 
			
		||||
    0xc715: "ForwardMatrix2",
 | 
			
		||||
    0xc716: "PreviewApplicationName",
 | 
			
		||||
    0xc717: "PreviewApplicationVersion",
 | 
			
		||||
    0xc718: "PreviewSettingsName",
 | 
			
		||||
    0xc719: "PreviewSettingsDigest",
 | 
			
		||||
    0xc71a: "PreviewColorSpace",
 | 
			
		||||
    0xc71b: "PreviewDateTime",
 | 
			
		||||
    0xc71c: "RawImageDigest",
 | 
			
		||||
    0xc71d: "OriginalRawFileDigest",
 | 
			
		||||
    0xc71e: "SubTileBlockSize",
 | 
			
		||||
    0xc71f: "RowInterleaveFactor",
 | 
			
		||||
    0xc725: "ProfileLookTableDims",
 | 
			
		||||
    0xc726: "ProfileLookTableData",
 | 
			
		||||
    0xc740: "OpcodeList1",
 | 
			
		||||
    0xc741: "OpcodeList2",
 | 
			
		||||
    0xc74e: "OpcodeList3",
 | 
			
		||||
    0xc761: "NoiseProfile"
 | 
			
		||||
    0x9C9B: "XPTitle",
 | 
			
		||||
    0x9C9C: "XPComment",
 | 
			
		||||
    0x9C9D: "XPAuthor",
 | 
			
		||||
    0x9C9E: "XPKeywords",
 | 
			
		||||
    0x9C9F: "XPSubject",
 | 
			
		||||
    0xA000: "FlashPixVersion",
 | 
			
		||||
    0xA001: "ColorSpace",
 | 
			
		||||
    0xA002: "ExifImageWidth",
 | 
			
		||||
    0xA003: "ExifImageHeight",
 | 
			
		||||
    0xA004: "RelatedSoundFile",
 | 
			
		||||
    0xA005: "ExifInteroperabilityOffset",
 | 
			
		||||
    0xA20B: "FlashEnergy",
 | 
			
		||||
    0xA20C: "SpatialFrequencyResponse",
 | 
			
		||||
    0xA20E: "FocalPlaneXResolution",
 | 
			
		||||
    0xA20F: "FocalPlaneYResolution",
 | 
			
		||||
    0xA210: "FocalPlaneResolutionUnit",
 | 
			
		||||
    0xA214: "SubjectLocation",
 | 
			
		||||
    0xA215: "ExposureIndex",
 | 
			
		||||
    0xA217: "SensingMethod",
 | 
			
		||||
    0xA300: "FileSource",
 | 
			
		||||
    0xA301: "SceneType",
 | 
			
		||||
    0xA302: "CFAPattern",
 | 
			
		||||
    0xA401: "CustomRendered",
 | 
			
		||||
    0xA402: "ExposureMode",
 | 
			
		||||
    0xA403: "WhiteBalance",
 | 
			
		||||
    0xA404: "DigitalZoomRatio",
 | 
			
		||||
    0xA405: "FocalLengthIn35mmFilm",
 | 
			
		||||
    0xA406: "SceneCaptureType",
 | 
			
		||||
    0xA407: "GainControl",
 | 
			
		||||
    0xA408: "Contrast",
 | 
			
		||||
    0xA409: "Saturation",
 | 
			
		||||
    0xA40A: "Sharpness",
 | 
			
		||||
    0xA40B: "DeviceSettingDescription",
 | 
			
		||||
    0xA40C: "SubjectDistanceRange",
 | 
			
		||||
    0xA420: "ImageUniqueID",
 | 
			
		||||
    0xA430: "CameraOwnerName",
 | 
			
		||||
    0xA431: "BodySerialNumber",
 | 
			
		||||
    0xA432: "LensSpecification",
 | 
			
		||||
    0xA433: "LensMake",
 | 
			
		||||
    0xA434: "LensModel",
 | 
			
		||||
    0xA435: "LensSerialNumber",
 | 
			
		||||
    0xA500: "Gamma",
 | 
			
		||||
    0xC4A5: "PrintImageMatching",
 | 
			
		||||
    0xC612: "DNGVersion",
 | 
			
		||||
    0xC613: "DNGBackwardVersion",
 | 
			
		||||
    0xC614: "UniqueCameraModel",
 | 
			
		||||
    0xC615: "LocalizedCameraModel",
 | 
			
		||||
    0xC616: "CFAPlaneColor",
 | 
			
		||||
    0xC617: "CFALayout",
 | 
			
		||||
    0xC618: "LinearizationTable",
 | 
			
		||||
    0xC619: "BlackLevelRepeatDim",
 | 
			
		||||
    0xC61A: "BlackLevel",
 | 
			
		||||
    0xC61B: "BlackLevelDeltaH",
 | 
			
		||||
    0xC61C: "BlackLevelDeltaV",
 | 
			
		||||
    0xC61D: "WhiteLevel",
 | 
			
		||||
    0xC61E: "DefaultScale",
 | 
			
		||||
    0xC61F: "DefaultCropOrigin",
 | 
			
		||||
    0xC620: "DefaultCropSize",
 | 
			
		||||
    0xC621: "ColorMatrix1",
 | 
			
		||||
    0xC622: "ColorMatrix2",
 | 
			
		||||
    0xC623: "CameraCalibration1",
 | 
			
		||||
    0xC624: "CameraCalibration2",
 | 
			
		||||
    0xC625: "ReductionMatrix1",
 | 
			
		||||
    0xC626: "ReductionMatrix2",
 | 
			
		||||
    0xC627: "AnalogBalance",
 | 
			
		||||
    0xC628: "AsShotNeutral",
 | 
			
		||||
    0xC629: "AsShotWhiteXY",
 | 
			
		||||
    0xC62A: "BaselineExposure",
 | 
			
		||||
    0xC62B: "BaselineNoise",
 | 
			
		||||
    0xC62C: "BaselineSharpness",
 | 
			
		||||
    0xC62D: "BayerGreenSplit",
 | 
			
		||||
    0xC62E: "LinearResponseLimit",
 | 
			
		||||
    0xC62F: "CameraSerialNumber",
 | 
			
		||||
    0xC630: "LensInfo",
 | 
			
		||||
    0xC631: "ChromaBlurRadius",
 | 
			
		||||
    0xC632: "AntiAliasStrength",
 | 
			
		||||
    0xC633: "ShadowScale",
 | 
			
		||||
    0xC634: "DNGPrivateData",
 | 
			
		||||
    0xC635: "MakerNoteSafety",
 | 
			
		||||
    0xC65A: "CalibrationIlluminant1",
 | 
			
		||||
    0xC65B: "CalibrationIlluminant2",
 | 
			
		||||
    0xC65C: "BestQualityScale",
 | 
			
		||||
    0xC65D: "RawDataUniqueID",
 | 
			
		||||
    0xC68B: "OriginalRawFileName",
 | 
			
		||||
    0xC68C: "OriginalRawFileData",
 | 
			
		||||
    0xC68D: "ActiveArea",
 | 
			
		||||
    0xC68E: "MaskedAreas",
 | 
			
		||||
    0xC68F: "AsShotICCProfile",
 | 
			
		||||
    0xC690: "AsShotPreProfileMatrix",
 | 
			
		||||
    0xC691: "CurrentICCProfile",
 | 
			
		||||
    0xC692: "CurrentPreProfileMatrix",
 | 
			
		||||
    0xC6BF: "ColorimetricReference",
 | 
			
		||||
    0xC6F3: "CameraCalibrationSignature",
 | 
			
		||||
    0xC6F4: "ProfileCalibrationSignature",
 | 
			
		||||
    0xC6F6: "AsShotProfileName",
 | 
			
		||||
    0xC6F7: "NoiseReductionApplied",
 | 
			
		||||
    0xC6F8: "ProfileName",
 | 
			
		||||
    0xC6F9: "ProfileHueSatMapDims",
 | 
			
		||||
    0xC6FA: "ProfileHueSatMapData1",
 | 
			
		||||
    0xC6FB: "ProfileHueSatMapData2",
 | 
			
		||||
    0xC6FC: "ProfileToneCurve",
 | 
			
		||||
    0xC6FD: "ProfileEmbedPolicy",
 | 
			
		||||
    0xC6FE: "ProfileCopyright",
 | 
			
		||||
    0xC714: "ForwardMatrix1",
 | 
			
		||||
    0xC715: "ForwardMatrix2",
 | 
			
		||||
    0xC716: "PreviewApplicationName",
 | 
			
		||||
    0xC717: "PreviewApplicationVersion",
 | 
			
		||||
    0xC718: "PreviewSettingsName",
 | 
			
		||||
    0xC719: "PreviewSettingsDigest",
 | 
			
		||||
    0xC71A: "PreviewColorSpace",
 | 
			
		||||
    0xC71B: "PreviewDateTime",
 | 
			
		||||
    0xC71C: "RawImageDigest",
 | 
			
		||||
    0xC71D: "OriginalRawFileDigest",
 | 
			
		||||
    0xC71E: "SubTileBlockSize",
 | 
			
		||||
    0xC71F: "RowInterleaveFactor",
 | 
			
		||||
    0xC725: "ProfileLookTableDims",
 | 
			
		||||
    0xC726: "ProfileLookTableData",
 | 
			
		||||
    0xC740: "OpcodeList1",
 | 
			
		||||
    0xC741: "OpcodeList2",
 | 
			
		||||
    0xC74E: "OpcodeList3",
 | 
			
		||||
    0xC761: "NoiseProfile",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,6 +23,7 @@ def register_handler(handler):
 | 
			
		|||
    global _handler
 | 
			
		||||
    _handler = handler
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# --------------------------------------------------------------------
 | 
			
		||||
# Image adapter
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,6 +27,7 @@ __version__ = "0.2"
 | 
			
		|||
#
 | 
			
		||||
# decoder
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _accept(prefix):
 | 
			
		||||
    return len(prefix) >= 6 and i16(prefix[4:6]) in [0xAF11, 0xAF12]
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -35,6 +36,7 @@ def _accept(prefix):
 | 
			
		|||
# Image plugin for the FLI/FLC animation format.  Use the <b>seek</b>
 | 
			
		||||
# method to load individual frames.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FliImageFile(ImageFile.ImageFile):
 | 
			
		||||
 | 
			
		||||
    format = "FLI"
 | 
			
		||||
| 
						 | 
				
			
			@ -46,9 +48,11 @@ class FliImageFile(ImageFile.ImageFile):
 | 
			
		|||
        # HEAD
 | 
			
		||||
        s = self.fp.read(128)
 | 
			
		||||
        magic = i16(s[4:6])
 | 
			
		||||
        if not (magic in [0xAF11, 0xAF12] and
 | 
			
		||||
                i16(s[14:16]) in [0, 3] and  # flags
 | 
			
		||||
                s[20:22] == b"\x00\x00"):  # reserved
 | 
			
		||||
        if not (
 | 
			
		||||
            magic in [0xAF11, 0xAF12]
 | 
			
		||||
            and i16(s[14:16]) in [0, 3]  # flags
 | 
			
		||||
            and s[20:22] == b"\x00\x00"  # reserved
 | 
			
		||||
        ):
 | 
			
		||||
            raise SyntaxError("not an FLI/FLC file")
 | 
			
		||||
 | 
			
		||||
        # frames
 | 
			
		||||
| 
						 | 
				
			
			@ -84,7 +88,7 @@ class FliImageFile(ImageFile.ImageFile):
 | 
			
		|||
            elif i16(s[4:6]) == 4:
 | 
			
		||||
                self._palette(palette, 0)
 | 
			
		||||
 | 
			
		||||
        palette = [o8(r)+o8(g)+o8(b) for (r, g, b) in palette]
 | 
			
		||||
        palette = [o8(r) + o8(g) + o8(b) for (r, g, b) in palette]
 | 
			
		||||
        self.palette = ImagePalette.raw("RGB", b"".join(palette))
 | 
			
		||||
 | 
			
		||||
        # set things up to decode first frame
 | 
			
		||||
| 
						 | 
				
			
			@ -106,8 +110,8 @@ class FliImageFile(ImageFile.ImageFile):
 | 
			
		|||
            s = self.fp.read(n * 3)
 | 
			
		||||
            for n in range(0, len(s), 3):
 | 
			
		||||
                r = i8(s[n]) << shift
 | 
			
		||||
                g = i8(s[n+1]) << shift
 | 
			
		||||
                b = i8(s[n+2]) << shift
 | 
			
		||||
                g = i8(s[n + 1]) << shift
 | 
			
		||||
                b = i8(s[n + 2]) << shift
 | 
			
		||||
                palette[i] = (r, g, b)
 | 
			
		||||
                i += 1
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -152,7 +156,7 @@ class FliImageFile(ImageFile.ImageFile):
 | 
			
		|||
        framesize = i32(s)
 | 
			
		||||
 | 
			
		||||
        self.decodermaxblock = framesize
 | 
			
		||||
        self.tile = [("fli", (0, 0)+self.size, self.__offset, None)]
 | 
			
		||||
        self.tile = [("fli", (0, 0) + self.size, self.__offset, None)]
 | 
			
		||||
 | 
			
		||||
        self.__offset += framesize
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,6 +33,7 @@ def puti16(fp, values):
 | 
			
		|||
##
 | 
			
		||||
# Base class for raster font file handlers.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FontFile(object):
 | 
			
		||||
 | 
			
		||||
    bitmap = None
 | 
			
		||||
| 
						 | 
				
			
			@ -61,7 +62,7 @@ class FontFile(object):
 | 
			
		|||
                w = w + (src[2] - src[0])
 | 
			
		||||
                if w > WIDTH:
 | 
			
		||||
                    lines += 1
 | 
			
		||||
                    w = (src[2] - src[0])
 | 
			
		||||
                    w = src[2] - src[0]
 | 
			
		||||
                maxwidth = max(maxwidth, w)
 | 
			
		||||
 | 
			
		||||
        xsize = maxwidth
 | 
			
		||||
| 
						 | 
				
			
			@ -103,7 +104,7 @@ class FontFile(object):
 | 
			
		|||
        # font metrics
 | 
			
		||||
        with open(os.path.splitext(filename)[0] + ".pil", "wb") as fp:
 | 
			
		||||
            fp.write(b"PILfont\n")
 | 
			
		||||
            fp.write((";;;;;;%d;\n" % self.ysize).encode('ascii'))  # HACK!!!
 | 
			
		||||
            fp.write((";;;;;;%d;\n" % self.ysize).encode("ascii"))  # HACK!!!
 | 
			
		||||
            fp.write(b"DATA\n")
 | 
			
		||||
            for id in range(256):
 | 
			
		||||
                m = self.metrics[id]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,22 +29,23 @@ __version__ = "0.1"
 | 
			
		|||
# we map from colour field tuples to (mode, rawmode) descriptors
 | 
			
		||||
MODES = {
 | 
			
		||||
    # opacity
 | 
			
		||||
    (0x00007ffe): ("A", "L"),
 | 
			
		||||
    (0x00007FFE): ("A", "L"),
 | 
			
		||||
    # monochrome
 | 
			
		||||
    (0x00010000,): ("L", "L"),
 | 
			
		||||
    (0x00018000, 0x00017ffe): ("RGBA", "LA"),
 | 
			
		||||
    (0x00018000, 0x00017FFE): ("RGBA", "LA"),
 | 
			
		||||
    # photo YCC
 | 
			
		||||
    (0x00020000, 0x00020001, 0x00020002): ("RGB", "YCC;P"),
 | 
			
		||||
    (0x00028000, 0x00028001, 0x00028002, 0x00027ffe): ("RGBA", "YCCA;P"),
 | 
			
		||||
    (0x00028000, 0x00028001, 0x00028002, 0x00027FFE): ("RGBA", "YCCA;P"),
 | 
			
		||||
    # standard RGB (NIFRGB)
 | 
			
		||||
    (0x00030000, 0x00030001, 0x00030002): ("RGB", "RGB"),
 | 
			
		||||
    (0x00038000, 0x00038001, 0x00038002, 0x00037ffe): ("RGBA", "RGBA"),
 | 
			
		||||
    (0x00038000, 0x00038001, 0x00038002, 0x00037FFE): ("RGBA", "RGBA"),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# --------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _accept(prefix):
 | 
			
		||||
    return prefix[:8] == olefile.MAGIC
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -52,6 +53,7 @@ def _accept(prefix):
 | 
			
		|||
##
 | 
			
		||||
# Image plugin for the FlashPix images.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FpxImageFile(ImageFile.ImageFile):
 | 
			
		||||
 | 
			
		||||
    format = "FPX"
 | 
			
		||||
| 
						 | 
				
			
			@ -76,10 +78,9 @@ class FpxImageFile(ImageFile.ImageFile):
 | 
			
		|||
        #
 | 
			
		||||
        # get the Image Contents Property Set
 | 
			
		||||
 | 
			
		||||
        prop = self.ole.getproperties([
 | 
			
		||||
            "Data Object Store %06d" % index,
 | 
			
		||||
            "\005Image Contents"
 | 
			
		||||
        ])
 | 
			
		||||
        prop = self.ole.getproperties(
 | 
			
		||||
            ["Data Object Store %06d" % index, "\005Image Contents"]
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # size (highest resolution)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -105,7 +106,7 @@ class FpxImageFile(ImageFile.ImageFile):
 | 
			
		|||
        colors = []
 | 
			
		||||
        for i in range(i32(s, 4)):
 | 
			
		||||
            # note: for now, we ignore the "uncalibrated" flag
 | 
			
		||||
            colors.append(i32(s, 8+i*4) & 0x7fffffff)
 | 
			
		||||
            colors.append(i32(s, 8 + i * 4) & 0x7FFFFFFF)
 | 
			
		||||
 | 
			
		||||
        self.mode, self.rawmode = MODES[tuple(colors)]
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -125,7 +126,7 @@ class FpxImageFile(ImageFile.ImageFile):
 | 
			
		|||
        stream = [
 | 
			
		||||
            "Data Object Store %06d" % index,
 | 
			
		||||
            "Resolution %04d" % subimage,
 | 
			
		||||
            "Subimage 0000 Header"
 | 
			
		||||
            "Subimage 0000 Header",
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
        fp = self.ole.openstream(stream)
 | 
			
		||||
| 
						 | 
				
			
			@ -157,17 +158,29 @@ class FpxImageFile(ImageFile.ImageFile):
 | 
			
		|||
 | 
			
		||||
        for i in range(0, len(s), length):
 | 
			
		||||
 | 
			
		||||
            compression = i32(s, i+8)
 | 
			
		||||
            compression = i32(s, i + 8)
 | 
			
		||||
 | 
			
		||||
            if compression == 0:
 | 
			
		||||
                self.tile.append(("raw", (x, y, x+xtile, y+ytile),
 | 
			
		||||
                                 i32(s, i) + 28, (self.rawmode)))
 | 
			
		||||
                self.tile.append(
 | 
			
		||||
                    (
 | 
			
		||||
                        "raw",
 | 
			
		||||
                        (x, y, x + xtile, y + ytile),
 | 
			
		||||
                        i32(s, i) + 28,
 | 
			
		||||
                        (self.rawmode),
 | 
			
		||||
                    )
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
            elif compression == 1:
 | 
			
		||||
 | 
			
		||||
                # FIXME: the fill decoder is not implemented
 | 
			
		||||
                self.tile.append(("fill", (x, y, x+xtile, y+ytile),
 | 
			
		||||
                                 i32(s, i) + 28, (self.rawmode, s[12:16])))
 | 
			
		||||
                self.tile.append(
 | 
			
		||||
                    (
 | 
			
		||||
                        "fill",
 | 
			
		||||
                        (x, y, x + xtile, y + ytile),
 | 
			
		||||
                        i32(s, i) + 28,
 | 
			
		||||
                        (self.rawmode, s[12:16]),
 | 
			
		||||
                    )
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
            elif compression == 2:
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -189,8 +202,14 @@ class FpxImageFile(ImageFile.ImageFile):
 | 
			
		|||
                    # The image is stored as defined by rawmode
 | 
			
		||||
                    jpegmode = rawmode
 | 
			
		||||
 | 
			
		||||
                self.tile.append(("jpeg", (x, y, x+xtile, y+ytile),
 | 
			
		||||
                                 i32(s, i) + 28, (rawmode, jpegmode)))
 | 
			
		||||
                self.tile.append(
 | 
			
		||||
                    (
 | 
			
		||||
                        "jpeg",
 | 
			
		||||
                        (x, y, x + xtile, y + ytile),
 | 
			
		||||
                        i32(s, i) + 28,
 | 
			
		||||
                        (rawmode, jpegmode),
 | 
			
		||||
                    )
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
                # FIXME: jpeg tables are tile dependent; the prefix
 | 
			
		||||
                # data must be placed in the tile descriptor itself!
 | 
			
		||||
| 
						 | 
				
			
			@ -213,11 +232,11 @@ class FpxImageFile(ImageFile.ImageFile):
 | 
			
		|||
    def load(self):
 | 
			
		||||
 | 
			
		||||
        if not self.fp:
 | 
			
		||||
            self.fp = self.ole.openstream(self.stream[:2] +
 | 
			
		||||
                                          ["Subimage 0000 Data"])
 | 
			
		||||
            self.fp = self.ole.openstream(self.stream[:2] + ["Subimage 0000 Data"])
 | 
			
		||||
 | 
			
		||||
        return ImageFile.ImageFile.load(self)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# --------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -87,10 +87,9 @@ class FtexImageFile(ImageFile.ImageFile):
 | 
			
		|||
            self.mode = "RGBA"
 | 
			
		||||
            self.tile = [("bcn", (0, 0) + self.size, 0, (1))]
 | 
			
		||||
        elif format == FORMAT_UNCOMPRESSED:
 | 
			
		||||
            self.tile = [("raw", (0, 0) + self.size, 0, ('RGB', 0, 1))]
 | 
			
		||||
            self.tile = [("raw", (0, 0) + self.size, 0, ("RGB", 0, 1))]
 | 
			
		||||
        else:
 | 
			
		||||
            raise ValueError(
 | 
			
		||||
                "Invalid texture compression format: %r" % (format))
 | 
			
		||||
            raise ValueError("Invalid texture compression format: %r" % (format))
 | 
			
		||||
 | 
			
		||||
        self.fp.close()
 | 
			
		||||
        self.fp = BytesIO(data)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,13 +29,13 @@ from ._binary import i32be as i32
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
def _accept(prefix):
 | 
			
		||||
    return len(prefix) >= 8 and \
 | 
			
		||||
           i32(prefix[:4]) >= 20 and i32(prefix[4:8]) in (1, 2)
 | 
			
		||||
    return len(prefix) >= 8 and i32(prefix[:4]) >= 20 and i32(prefix[4:8]) in (1, 2)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
# Image plugin for the GIMP brush format.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GbrImageFile(ImageFile.ImageFile):
 | 
			
		||||
 | 
			
		||||
    format = "GBR"
 | 
			
		||||
| 
						 | 
				
			
			@ -55,24 +55,23 @@ class GbrImageFile(ImageFile.ImageFile):
 | 
			
		|||
        if width <= 0 or height <= 0:
 | 
			
		||||
            raise SyntaxError("not a GIMP brush")
 | 
			
		||||
        if color_depth not in (1, 4):
 | 
			
		||||
            raise SyntaxError(
 | 
			
		||||
                "Unsupported GIMP brush color depth: %s" % color_depth)
 | 
			
		||||
            raise SyntaxError("Unsupported GIMP brush color depth: %s" % color_depth)
 | 
			
		||||
 | 
			
		||||
        if version == 1:
 | 
			
		||||
            comment_length = header_size-20
 | 
			
		||||
            comment_length = header_size - 20
 | 
			
		||||
        else:
 | 
			
		||||
            comment_length = header_size-28
 | 
			
		||||
            comment_length = header_size - 28
 | 
			
		||||
            magic_number = self.fp.read(4)
 | 
			
		||||
            if magic_number != b'GIMP':
 | 
			
		||||
            if magic_number != b"GIMP":
 | 
			
		||||
                raise SyntaxError("not a GIMP brush, bad magic number")
 | 
			
		||||
            self.info['spacing'] = i32(self.fp.read(4))
 | 
			
		||||
            self.info["spacing"] = i32(self.fp.read(4))
 | 
			
		||||
 | 
			
		||||
        comment = self.fp.read(comment_length)[:-1]
 | 
			
		||||
 | 
			
		||||
        if color_depth == 1:
 | 
			
		||||
            self.mode = "L"
 | 
			
		||||
        else:
 | 
			
		||||
            self.mode = 'RGBA'
 | 
			
		||||
            self.mode = "RGBA"
 | 
			
		||||
 | 
			
		||||
        self._size = width, height
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -88,6 +87,7 @@ class GbrImageFile(ImageFile.ImageFile):
 | 
			
		|||
        self.im = Image.core.new(self.mode, self.size)
 | 
			
		||||
        self.frombytes(self.fp.read(self._data_size))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# registry
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,6 +37,7 @@ __version__ = "0.1"
 | 
			
		|||
# this plugin, you have to import the <b>GdImageFile</b> module and
 | 
			
		||||
# use the <b>GdImageFile.open</b> function.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GdImageFile(ImageFile.ImageFile):
 | 
			
		||||
 | 
			
		||||
    format = "GD"
 | 
			
		||||
| 
						 | 
				
			
			@ -57,15 +58,17 @@ class GdImageFile(ImageFile.ImageFile):
 | 
			
		|||
        trueColorOffset = 2 if trueColor else 0
 | 
			
		||||
 | 
			
		||||
        # transparency index
 | 
			
		||||
        tindex = i32(s[7+trueColorOffset:7+trueColorOffset+4])
 | 
			
		||||
        tindex = i32(s[7 + trueColorOffset : 7 + trueColorOffset + 4])
 | 
			
		||||
        if tindex < 256:
 | 
			
		||||
            self.info["transparency"] = tindex
 | 
			
		||||
 | 
			
		||||
        self.palette = ImagePalette.raw(
 | 
			
		||||
            "XBGR", s[7+trueColorOffset+4:7+trueColorOffset+4+256*4])
 | 
			
		||||
            "XBGR", s[7 + trueColorOffset + 4 : 7 + trueColorOffset + 4 + 256 * 4]
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.tile = [("raw", (0, 0)+self.size, 7+trueColorOffset+4+256*4,
 | 
			
		||||
                      ("L", 0, 1))]
 | 
			
		||||
        self.tile = [
 | 
			
		||||
            ("raw", (0, 0) + self.size, 7 + trueColorOffset + 4 + 256 * 4, ("L", 0, 1))
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def open(fp, mode="r"):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,6 +37,7 @@ __version__ = "0.9"
 | 
			
		|||
# --------------------------------------------------------------------
 | 
			
		||||
# Identify/read GIF files
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _accept(prefix):
 | 
			
		||||
    return prefix[:6] in [b"GIF87a", b"GIF89a"]
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -45,6 +46,7 @@ def _accept(prefix):
 | 
			
		|||
# Image plugin for GIF images.  This plugin supports both GIF87 and
 | 
			
		||||
# GIF89 images.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GifImageFile(ImageFile.ImageFile):
 | 
			
		||||
 | 
			
		||||
    format = "GIF"
 | 
			
		||||
| 
						 | 
				
			
			@ -78,7 +80,7 @@ class GifImageFile(ImageFile.ImageFile):
 | 
			
		|||
            # check if palette contains colour indices
 | 
			
		||||
            p = self.fp.read(3 << bits)
 | 
			
		||||
            for i in range(0, len(p), 3):
 | 
			
		||||
                if not (i//3 == i8(p[i]) == i8(p[i+1]) == i8(p[i+2])):
 | 
			
		||||
                if not (i // 3 == i8(p[i]) == i8(p[i + 1]) == i8(p[i + 2])):
 | 
			
		||||
                    p = ImagePalette.raw("RGB", p)
 | 
			
		||||
                    self.global_palette = self.palette = p
 | 
			
		||||
                    break
 | 
			
		||||
| 
						 | 
				
			
			@ -168,6 +170,7 @@ class GifImageFile(ImageFile.ImageFile):
 | 
			
		|||
            self.im.paste(self.dispose, self.dispose_extent)
 | 
			
		||||
 | 
			
		||||
        from copy import copy
 | 
			
		||||
 | 
			
		||||
        self.palette = copy(self.global_palette)
 | 
			
		||||
 | 
			
		||||
        info = {}
 | 
			
		||||
| 
						 | 
				
			
			@ -242,16 +245,14 @@ class GifImageFile(ImageFile.ImageFile):
 | 
			
		|||
 | 
			
		||||
                if flags & 128:
 | 
			
		||||
                    bits = (flags & 7) + 1
 | 
			
		||||
                    self.palette =\
 | 
			
		||||
                        ImagePalette.raw("RGB", self.fp.read(3 << bits))
 | 
			
		||||
                    self.palette = ImagePalette.raw("RGB", self.fp.read(3 << bits))
 | 
			
		||||
 | 
			
		||||
                # image data
 | 
			
		||||
                bits = i8(self.fp.read(1))
 | 
			
		||||
                self.__offset = self.fp.tell()
 | 
			
		||||
                self.tile = [("gif",
 | 
			
		||||
                             (x0, y0, x1, y1),
 | 
			
		||||
                             self.__offset,
 | 
			
		||||
                             (bits, interlace))]
 | 
			
		||||
                self.tile = [
 | 
			
		||||
                    ("gif", (x0, y0, x1, y1), self.__offset, (bits, interlace))
 | 
			
		||||
                ]
 | 
			
		||||
                break
 | 
			
		||||
 | 
			
		||||
            else:
 | 
			
		||||
| 
						 | 
				
			
			@ -264,8 +265,7 @@ class GifImageFile(ImageFile.ImageFile):
 | 
			
		|||
                self.dispose = None
 | 
			
		||||
            elif self.disposal_method == 2:
 | 
			
		||||
                # replace with background colour
 | 
			
		||||
                self.dispose = Image.core.fill("P", self.size,
 | 
			
		||||
                                               self.info["background"])
 | 
			
		||||
                self.dispose = Image.core.fill("P", self.size, self.info["background"])
 | 
			
		||||
            else:
 | 
			
		||||
                # replace with previous contents
 | 
			
		||||
                if self.im:
 | 
			
		||||
| 
						 | 
				
			
			@ -303,8 +303,7 @@ class GifImageFile(ImageFile.ImageFile):
 | 
			
		|||
            # we do this by pasting the updated area onto the previous
 | 
			
		||||
            # frame which we then use as the current image content
 | 
			
		||||
            updated = self._crop(self.im, self.dispose_extent)
 | 
			
		||||
            self._prev_im.paste(updated, self.dispose_extent,
 | 
			
		||||
                                updated.convert('RGBA'))
 | 
			
		||||
            self._prev_im.paste(updated, self.dispose_extent, updated.convert("RGBA"))
 | 
			
		||||
            self.im = self._prev_im
 | 
			
		||||
        self._prev_im = self.im.copy()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -317,15 +316,12 @@ class GifImageFile(ImageFile.ImageFile):
 | 
			
		|||
        finally:
 | 
			
		||||
            self.__fp = None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# --------------------------------------------------------------------
 | 
			
		||||
# Write GIF files
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
RAWMODE = {
 | 
			
		||||
    "1": "L",
 | 
			
		||||
    "L": "L",
 | 
			
		||||
    "P": "P"
 | 
			
		||||
}
 | 
			
		||||
RAWMODE = {"1": "L", "L": "L", "P": "P"}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _normalize_mode(im, initial_call=False):
 | 
			
		||||
| 
						 | 
				
			
			@ -376,19 +372,23 @@ def _normalize_palette(im, palette, info):
 | 
			
		|||
        if isinstance(palette, (bytes, bytearray, list)):
 | 
			
		||||
            source_palette = bytearray(palette[:768])
 | 
			
		||||
        if isinstance(palette, ImagePalette.ImagePalette):
 | 
			
		||||
            source_palette = bytearray(itertools.chain.from_iterable(
 | 
			
		||||
                                zip(palette.palette[:256],
 | 
			
		||||
            source_palette = bytearray(
 | 
			
		||||
                itertools.chain.from_iterable(
 | 
			
		||||
                    zip(
 | 
			
		||||
                        palette.palette[:256],
 | 
			
		||||
                        palette.palette[256:512],
 | 
			
		||||
                                    palette.palette[512:768])))
 | 
			
		||||
                        palette.palette[512:768],
 | 
			
		||||
                    )
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    if im.mode == "P":
 | 
			
		||||
        if not source_palette:
 | 
			
		||||
            source_palette = im.im.getpalette("RGB")[:768]
 | 
			
		||||
    else:  # L-mode
 | 
			
		||||
        if not source_palette:
 | 
			
		||||
            source_palette = bytearray(i//3 for i in range(768))
 | 
			
		||||
        im.palette = ImagePalette.ImagePalette("RGB",
 | 
			
		||||
                                               palette=source_palette)
 | 
			
		||||
            source_palette = bytearray(i // 3 for i in range(768))
 | 
			
		||||
        im.palette = ImagePalette.ImagePalette("RGB", palette=source_palette)
 | 
			
		||||
 | 
			
		||||
    used_palette_colors = _get_optimize(im, info)
 | 
			
		||||
    if used_palette_colors is not None:
 | 
			
		||||
| 
						 | 
				
			
			@ -414,8 +414,7 @@ def _write_single_frame(im, fp, palette):
 | 
			
		|||
    _write_local_header(fp, im, (0, 0), flags)
 | 
			
		||||
 | 
			
		||||
    im_out.encoderconfig = (8, get_interlace(im))
 | 
			
		||||
    ImageFile._save(im_out, fp, [("gif", (0, 0)+im.size, 0,
 | 
			
		||||
                                  RAWMODE[im_out.mode])])
 | 
			
		||||
    ImageFile._save(im_out, fp, [("gif", (0, 0) + im.size, 0, RAWMODE[im_out.mode])])
 | 
			
		||||
 | 
			
		||||
    fp.write(b"\0")  # end of image data
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -427,8 +426,7 @@ def _write_multiple_frames(im, fp, palette):
 | 
			
		|||
 | 
			
		||||
    im_frames = []
 | 
			
		||||
    frame_count = 0
 | 
			
		||||
    for imSequence in itertools.chain([im],
 | 
			
		||||
                                      im.encoderinfo.get("append_images", [])):
 | 
			
		||||
    for imSequence in itertools.chain([im], im.encoderinfo.get("append_images", [])):
 | 
			
		||||
        for im_frame in ImageSequence.Iterator(imSequence):
 | 
			
		||||
            # a copy is required here since seek can still mutate the image
 | 
			
		||||
            im_frame = _normalize_mode(im_frame.copy())
 | 
			
		||||
| 
						 | 
				
			
			@ -439,7 +437,7 @@ def _write_multiple_frames(im, fp, palette):
 | 
			
		|||
 | 
			
		||||
            encoderinfo = im.encoderinfo.copy()
 | 
			
		||||
            if isinstance(duration, (list, tuple)):
 | 
			
		||||
                encoderinfo['duration'] = duration[frame_count]
 | 
			
		||||
                encoderinfo["duration"] = duration[frame_count]
 | 
			
		||||
            if isinstance(disposal, (list, tuple)):
 | 
			
		||||
                encoderinfo["disposal"] = disposal[frame_count]
 | 
			
		||||
            frame_count += 1
 | 
			
		||||
| 
						 | 
				
			
			@ -447,44 +445,37 @@ def _write_multiple_frames(im, fp, palette):
 | 
			
		|||
            if im_frames:
 | 
			
		||||
                # delta frame
 | 
			
		||||
                previous = im_frames[-1]
 | 
			
		||||
                if _get_palette_bytes(im_frame) == \
 | 
			
		||||
                   _get_palette_bytes(previous['im']):
 | 
			
		||||
                    delta = ImageChops.subtract_modulo(im_frame,
 | 
			
		||||
                                                       previous['im'])
 | 
			
		||||
                if _get_palette_bytes(im_frame) == _get_palette_bytes(previous["im"]):
 | 
			
		||||
                    delta = ImageChops.subtract_modulo(im_frame, previous["im"])
 | 
			
		||||
                else:
 | 
			
		||||
                    delta = ImageChops.subtract_modulo(
 | 
			
		||||
                        im_frame.convert('RGB'), previous['im'].convert('RGB'))
 | 
			
		||||
                        im_frame.convert("RGB"), previous["im"].convert("RGB")
 | 
			
		||||
                    )
 | 
			
		||||
                bbox = delta.getbbox()
 | 
			
		||||
                if not bbox:
 | 
			
		||||
                    # This frame is identical to the previous frame
 | 
			
		||||
                    if duration:
 | 
			
		||||
                        previous['encoderinfo']['duration'] += \
 | 
			
		||||
                            encoderinfo['duration']
 | 
			
		||||
                        previous["encoderinfo"]["duration"] += encoderinfo["duration"]
 | 
			
		||||
                    continue
 | 
			
		||||
            else:
 | 
			
		||||
                bbox = None
 | 
			
		||||
            im_frames.append({
 | 
			
		||||
                'im': im_frame,
 | 
			
		||||
                'bbox': bbox,
 | 
			
		||||
                'encoderinfo': encoderinfo
 | 
			
		||||
            })
 | 
			
		||||
            im_frames.append({"im": im_frame, "bbox": bbox, "encoderinfo": encoderinfo})
 | 
			
		||||
 | 
			
		||||
    if len(im_frames) > 1:
 | 
			
		||||
        for frame_data in im_frames:
 | 
			
		||||
            im_frame = frame_data['im']
 | 
			
		||||
            if not frame_data['bbox']:
 | 
			
		||||
            im_frame = frame_data["im"]
 | 
			
		||||
            if not frame_data["bbox"]:
 | 
			
		||||
                # global header
 | 
			
		||||
                for s in _get_global_header(im_frame,
 | 
			
		||||
                                            frame_data['encoderinfo']):
 | 
			
		||||
                for s in _get_global_header(im_frame, frame_data["encoderinfo"]):
 | 
			
		||||
                    fp.write(s)
 | 
			
		||||
                offset = (0, 0)
 | 
			
		||||
            else:
 | 
			
		||||
                # compress difference
 | 
			
		||||
                frame_data['encoderinfo']['include_color_table'] = True
 | 
			
		||||
                frame_data["encoderinfo"]["include_color_table"] = True
 | 
			
		||||
 | 
			
		||||
                im_frame = im_frame.crop(frame_data['bbox'])
 | 
			
		||||
                offset = frame_data['bbox'][:2]
 | 
			
		||||
            _write_frame_data(fp, im_frame, offset, frame_data['encoderinfo'])
 | 
			
		||||
                im_frame = im_frame.crop(frame_data["bbox"])
 | 
			
		||||
                offset = frame_data["bbox"][:2]
 | 
			
		||||
            _write_frame_data(fp, im_frame, offset, frame_data["encoderinfo"])
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -543,7 +534,7 @@ def _write_local_header(fp, im, offset, flags):
 | 
			
		|||
    else:
 | 
			
		||||
        duration = 0
 | 
			
		||||
 | 
			
		||||
    disposal = int(im.encoderinfo.get('disposal', 0))
 | 
			
		||||
    disposal = int(im.encoderinfo.get("disposal", 0))
 | 
			
		||||
 | 
			
		||||
    if transparent_color_exists or duration != 0 or disposal:
 | 
			
		||||
        packed_flag = 1 if transparent_color_exists else 0
 | 
			
		||||
| 
						 | 
				
			
			@ -551,34 +542,35 @@ def _write_local_header(fp, im, offset, flags):
 | 
			
		|||
        if not transparent_color_exists:
 | 
			
		||||
            transparency = 0
 | 
			
		||||
 | 
			
		||||
        fp.write(b"!" +
 | 
			
		||||
                 o8(249) +                # extension intro
 | 
			
		||||
                 o8(4) +                  # length
 | 
			
		||||
                 o8(packed_flag) +        # packed fields
 | 
			
		||||
                 o16(duration) +          # duration
 | 
			
		||||
                 o8(transparency) +       # transparency index
 | 
			
		||||
                 o8(0))
 | 
			
		||||
        fp.write(
 | 
			
		||||
            b"!"
 | 
			
		||||
            + o8(249)  # extension intro
 | 
			
		||||
            + o8(4)  # length
 | 
			
		||||
            + o8(packed_flag)  # packed fields
 | 
			
		||||
            + o16(duration)  # duration
 | 
			
		||||
            + o8(transparency)  # transparency index
 | 
			
		||||
            + o8(0)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    if "comment" in im.encoderinfo and \
 | 
			
		||||
       1 <= len(im.encoderinfo["comment"]):
 | 
			
		||||
        fp.write(b"!" +
 | 
			
		||||
                 o8(254))                 # extension intro
 | 
			
		||||
    if "comment" in im.encoderinfo and 1 <= len(im.encoderinfo["comment"]):
 | 
			
		||||
        fp.write(b"!" + o8(254))  # extension intro
 | 
			
		||||
        for i in range(0, len(im.encoderinfo["comment"]), 255):
 | 
			
		||||
            subblock = im.encoderinfo["comment"][i:i+255]
 | 
			
		||||
            fp.write(o8(len(subblock)) +
 | 
			
		||||
                     subblock)
 | 
			
		||||
            subblock = im.encoderinfo["comment"][i : i + 255]
 | 
			
		||||
            fp.write(o8(len(subblock)) + subblock)
 | 
			
		||||
        fp.write(o8(0))
 | 
			
		||||
    if "loop" in im.encoderinfo:
 | 
			
		||||
        number_of_loops = im.encoderinfo["loop"]
 | 
			
		||||
        fp.write(b"!" +
 | 
			
		||||
                 o8(255) +                # extension intro
 | 
			
		||||
                 o8(11) +
 | 
			
		||||
                 b"NETSCAPE2.0" +
 | 
			
		||||
                 o8(3) +
 | 
			
		||||
                 o8(1) +
 | 
			
		||||
                 o16(number_of_loops) +   # number of loops
 | 
			
		||||
                 o8(0))
 | 
			
		||||
    include_color_table = im.encoderinfo.get('include_color_table')
 | 
			
		||||
        fp.write(
 | 
			
		||||
            b"!"
 | 
			
		||||
            + o8(255)  # extension intro
 | 
			
		||||
            + o8(11)
 | 
			
		||||
            + b"NETSCAPE2.0"
 | 
			
		||||
            + o8(3)
 | 
			
		||||
            + o8(1)
 | 
			
		||||
            + o16(number_of_loops)  # number of loops
 | 
			
		||||
            + o8(0)
 | 
			
		||||
        )
 | 
			
		||||
    include_color_table = im.encoderinfo.get("include_color_table")
 | 
			
		||||
    if include_color_table:
 | 
			
		||||
        palette_bytes = _get_palette_bytes(im)
 | 
			
		||||
        color_table_size = _get_color_table_size(palette_bytes)
 | 
			
		||||
| 
						 | 
				
			
			@ -586,12 +578,14 @@ def _write_local_header(fp, im, offset, flags):
 | 
			
		|||
            flags = flags | 128  # local color table flag
 | 
			
		||||
            flags = flags | color_table_size
 | 
			
		||||
 | 
			
		||||
    fp.write(b"," +
 | 
			
		||||
             o16(offset[0]) +             # offset
 | 
			
		||||
             o16(offset[1]) +
 | 
			
		||||
             o16(im.size[0]) +            # size
 | 
			
		||||
             o16(im.size[1]) +
 | 
			
		||||
             o8(flags))                   # flags
 | 
			
		||||
    fp.write(
 | 
			
		||||
        b","
 | 
			
		||||
        + o16(offset[0])  # offset
 | 
			
		||||
        + o16(offset[1])
 | 
			
		||||
        + o16(im.size[0])  # size
 | 
			
		||||
        + o16(im.size[1])
 | 
			
		||||
        + o8(flags)  # flags
 | 
			
		||||
    )
 | 
			
		||||
    if include_color_table and color_table_size:
 | 
			
		||||
        fp.write(_get_header_palette(palette_bytes))
 | 
			
		||||
    fp.write(o8(8))  # bits
 | 
			
		||||
| 
						 | 
				
			
			@ -608,21 +602,23 @@ def _save_netpbm(im, fp, filename):
 | 
			
		|||
 | 
			
		||||
    import os
 | 
			
		||||
    from subprocess import Popen, check_call, PIPE, CalledProcessError
 | 
			
		||||
 | 
			
		||||
    tempfile = im._dump()
 | 
			
		||||
 | 
			
		||||
    with open(filename, 'wb') as f:
 | 
			
		||||
    with open(filename, "wb") as f:
 | 
			
		||||
        if im.mode != "RGB":
 | 
			
		||||
            with open(os.devnull, 'wb') as devnull:
 | 
			
		||||
            with open(os.devnull, "wb") as devnull:
 | 
			
		||||
                check_call(["ppmtogif", tempfile], stdout=f, stderr=devnull)
 | 
			
		||||
        else:
 | 
			
		||||
            # Pipe ppmquant output into ppmtogif
 | 
			
		||||
            # "ppmquant 256 %s | ppmtogif > %s" % (tempfile, filename)
 | 
			
		||||
            quant_cmd = ["ppmquant", "256", tempfile]
 | 
			
		||||
            togif_cmd = ["ppmtogif"]
 | 
			
		||||
            with open(os.devnull, 'wb') as devnull:
 | 
			
		||||
            with open(os.devnull, "wb") as devnull:
 | 
			
		||||
                quant_proc = Popen(quant_cmd, stdout=PIPE, stderr=devnull)
 | 
			
		||||
                togif_proc = Popen(togif_cmd, stdin=quant_proc.stdout,
 | 
			
		||||
                                   stdout=f, stderr=devnull)
 | 
			
		||||
                togif_proc = Popen(
 | 
			
		||||
                    togif_cmd, stdin=quant_proc.stdout, stdout=f, stderr=devnull
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
            # Allow ppmquant to receive SIGPIPE if ppmtogif exits
 | 
			
		||||
            quant_proc.stdout.close()
 | 
			
		||||
| 
						 | 
				
			
			@ -668,7 +664,7 @@ def _get_optimize(im, info):
 | 
			
		|||
        # * If we have a 'large' image, the palette is in the noise.
 | 
			
		||||
 | 
			
		||||
        # create the new palette if not every color is used
 | 
			
		||||
        optimise = _FORCE_OPTIMIZE or im.mode == 'L'
 | 
			
		||||
        optimise = _FORCE_OPTIMIZE or im.mode == "L"
 | 
			
		||||
        if optimise or im.width * im.height < 512 * 512:
 | 
			
		||||
            # check which colors are used
 | 
			
		||||
            used_palette_colors = []
 | 
			
		||||
| 
						 | 
				
			
			@ -676,15 +672,18 @@ def _get_optimize(im, info):
 | 
			
		|||
                if count:
 | 
			
		||||
                    used_palette_colors.append(i)
 | 
			
		||||
 | 
			
		||||
            if optimise or (len(used_palette_colors) <= 128 and
 | 
			
		||||
               max(used_palette_colors) > len(used_palette_colors)):
 | 
			
		||||
            if optimise or (
 | 
			
		||||
                len(used_palette_colors) <= 128
 | 
			
		||||
                and max(used_palette_colors) > len(used_palette_colors)
 | 
			
		||||
            ):
 | 
			
		||||
                return used_palette_colors
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _get_color_table_size(palette_bytes):
 | 
			
		||||
    # calculate the palette size for the header
 | 
			
		||||
    import math
 | 
			
		||||
    color_table_size = int(math.ceil(math.log(len(palette_bytes)//3, 2)))-1
 | 
			
		||||
 | 
			
		||||
    color_table_size = int(math.ceil(math.log(len(palette_bytes) // 3, 2))) - 1
 | 
			
		||||
    if color_table_size < 0:
 | 
			
		||||
        color_table_size = 0
 | 
			
		||||
    return color_table_size
 | 
			
		||||
| 
						 | 
				
			
			@ -702,7 +701,7 @@ def _get_header_palette(palette_bytes):
 | 
			
		|||
 | 
			
		||||
    # add the missing amount of bytes
 | 
			
		||||
    # the palette has to be 2<<n in size
 | 
			
		||||
    actual_target_size_diff = (2 << color_table_size) - len(palette_bytes)//3
 | 
			
		||||
    actual_target_size_diff = (2 << color_table_size) - len(palette_bytes) // 3
 | 
			
		||||
    if actual_target_size_diff > 0:
 | 
			
		||||
        palette_bytes += o8(0) * 3 * actual_target_size_diff
 | 
			
		||||
    return palette_bytes
 | 
			
		||||
| 
						 | 
				
			
			@ -727,9 +726,9 @@ def _get_global_header(im, info):
 | 
			
		|||
    version = b"87a"
 | 
			
		||||
    for extensionKey in ["transparency", "duration", "loop", "comment"]:
 | 
			
		||||
        if info and extensionKey in info:
 | 
			
		||||
            if ((extensionKey == "duration" and info[extensionKey] == 0) or
 | 
			
		||||
                (extensionKey == "comment" and
 | 
			
		||||
                 not (1 <= len(info[extensionKey]) <= 255))):
 | 
			
		||||
            if (extensionKey == "duration" and info[extensionKey] == 0) or (
 | 
			
		||||
                extensionKey == "comment" and not (1 <= len(info[extensionKey]) <= 255)
 | 
			
		||||
            ):
 | 
			
		||||
                continue
 | 
			
		||||
            version = b"89a"
 | 
			
		||||
            break
 | 
			
		||||
| 
						 | 
				
			
			@ -750,18 +749,17 @@ def _get_global_header(im, info):
 | 
			
		|||
    color_table_size = _get_color_table_size(palette_bytes)
 | 
			
		||||
 | 
			
		||||
    return [
 | 
			
		||||
        b"GIF"+version +               # signature + version
 | 
			
		||||
        o16(im.size[0]) +              # canvas width
 | 
			
		||||
        o16(im.size[1]),               # canvas height
 | 
			
		||||
 | 
			
		||||
        b"GIF"  # signature
 | 
			
		||||
        + version  # version
 | 
			
		||||
        + o16(im.size[0])  # canvas width
 | 
			
		||||
        + o16(im.size[1]),  # canvas height
 | 
			
		||||
        # Logical Screen Descriptor
 | 
			
		||||
        # size of global color table + global color table flag
 | 
			
		||||
        o8(color_table_size + 128),  # packed fields
 | 
			
		||||
        # background + reserved/aspect
 | 
			
		||||
        o8(background) + o8(0),
 | 
			
		||||
 | 
			
		||||
        # Global Color Table
 | 
			
		||||
        _get_header_palette(palette_bytes)
 | 
			
		||||
        _get_header_palette(palette_bytes),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -772,13 +770,15 @@ def _write_frame_data(fp, im_frame, offset, params):
 | 
			
		|||
        # local image header
 | 
			
		||||
        _write_local_header(fp, im_frame, offset, 0)
 | 
			
		||||
 | 
			
		||||
        ImageFile._save(im_frame, fp, [("gif", (0, 0)+im_frame.size, 0,
 | 
			
		||||
                                        RAWMODE[im_frame.mode])])
 | 
			
		||||
        ImageFile._save(
 | 
			
		||||
            im_frame, fp, [("gif", (0, 0) + im_frame.size, 0, RAWMODE[im_frame.mode])]
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        fp.write(b"\0")  # end of image data
 | 
			
		||||
    finally:
 | 
			
		||||
        del im_frame.encoderinfo
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# --------------------------------------------------------------------
 | 
			
		||||
# Legacy GIF utilities
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -827,6 +827,7 @@ def getdata(im, offset=(0, 0), **params):
 | 
			
		|||
    :returns: List of Bytes containing gif encoded frame data
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    class Collector(object):
 | 
			
		||||
        data = []
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -72,7 +72,7 @@ class GradientFile(object):
 | 
			
		|||
 | 
			
		||||
        for i in range(entries):
 | 
			
		||||
 | 
			
		||||
            x = i / float(entries-1)
 | 
			
		||||
            x = i / float(entries - 1)
 | 
			
		||||
 | 
			
		||||
            while x1 < x:
 | 
			
		||||
                ix += 1
 | 
			
		||||
| 
						 | 
				
			
			@ -100,8 +100,8 @@ class GradientFile(object):
 | 
			
		|||
##
 | 
			
		||||
# File handler for GIMP's gradient format.
 | 
			
		||||
 | 
			
		||||
class GimpGradientFile(GradientFile):
 | 
			
		||||
 | 
			
		||||
class GimpGradientFile(GradientFile):
 | 
			
		||||
    def __init__(self, fp):
 | 
			
		||||
 | 
			
		||||
        if fp.readline()[:13] != b"GIMP Gradient":
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,13 +21,14 @@ from ._binary import o8
 | 
			
		|||
##
 | 
			
		||||
# File handler for GIMP's palette format.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GimpPaletteFile(object):
 | 
			
		||||
 | 
			
		||||
    rawmode = "RGB"
 | 
			
		||||
 | 
			
		||||
    def __init__(self, fp):
 | 
			
		||||
 | 
			
		||||
        self.palette = [o8(i)*3 for i in range(256)]
 | 
			
		||||
        self.palette = [o8(i) * 3 for i in range(256)]
 | 
			
		||||
 | 
			
		||||
        if fp.readline()[:12] != b"GIMP Palette":
 | 
			
		||||
            raise SyntaxError("not a GIMP palette file")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,6 +28,7 @@ def register_handler(handler):
 | 
			
		|||
# --------------------------------------------------------------------
 | 
			
		||||
# Image adapter
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _accept(prefix):
 | 
			
		||||
    return prefix[0:4] == b"GRIB" and i8(prefix[7]) == 1
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,6 +27,7 @@ def register_handler(handler):
 | 
			
		|||
# --------------------------------------------------------------------
 | 
			
		||||
# Image adapter
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _accept(prefix):
 | 
			
		||||
    return prefix[:8] == b"\x89HDF\r\n\x1a\n"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,7 +24,7 @@ import struct
 | 
			
		|||
import sys
 | 
			
		||||
import tempfile
 | 
			
		||||
 | 
			
		||||
enable_jpeg2k = hasattr(Image.core, 'jp2klib_version')
 | 
			
		||||
enable_jpeg2k = hasattr(Image.core, "jp2klib_version")
 | 
			
		||||
if enable_jpeg2k:
 | 
			
		||||
    from PIL import Jpeg2KImagePlugin
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -32,7 +32,7 @@ HEADERSIZE = 8
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
def nextheader(fobj):
 | 
			
		||||
    return struct.unpack('>4sI', fobj.read(HEADERSIZE))
 | 
			
		||||
    return struct.unpack(">4sI", fobj.read(HEADERSIZE))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def read_32t(fobj, start_length, size):
 | 
			
		||||
| 
						 | 
				
			
			@ -40,8 +40,8 @@ def read_32t(fobj, start_length, size):
 | 
			
		|||
    (start, length) = start_length
 | 
			
		||||
    fobj.seek(start)
 | 
			
		||||
    sig = fobj.read(4)
 | 
			
		||||
    if sig != b'\x00\x00\x00\x00':
 | 
			
		||||
        raise SyntaxError('Unknown signature, expecting 0x00000000')
 | 
			
		||||
    if sig != b"\x00\x00\x00\x00":
 | 
			
		||||
        raise SyntaxError("Unknown signature, expecting 0x00000000")
 | 
			
		||||
    return read_32(fobj, (start + 4, length - 4), size)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -81,12 +81,8 @@ def read_32(fobj, start_length, size):
 | 
			
		|||
                if bytesleft <= 0:
 | 
			
		||||
                    break
 | 
			
		||||
            if bytesleft != 0:
 | 
			
		||||
                raise SyntaxError(
 | 
			
		||||
                    "Error reading channel [%r left]" % bytesleft
 | 
			
		||||
                    )
 | 
			
		||||
            band = Image.frombuffer(
 | 
			
		||||
                "L", pixel_size, b"".join(data), "raw", "L", 0, 1
 | 
			
		||||
                )
 | 
			
		||||
                raise SyntaxError("Error reading channel [%r left]" % bytesleft)
 | 
			
		||||
            band = Image.frombuffer("L", pixel_size, b"".join(data), "raw", "L", 0, 1)
 | 
			
		||||
            im.im.putband(band.im, band_ix)
 | 
			
		||||
    return {"RGB": im}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -97,9 +93,7 @@ def read_mk(fobj, start_length, size):
 | 
			
		|||
    fobj.seek(start)
 | 
			
		||||
    pixel_size = (size[0] * size[2], size[1] * size[2])
 | 
			
		||||
    sizesq = pixel_size[0] * pixel_size[1]
 | 
			
		||||
    band = Image.frombuffer(
 | 
			
		||||
        "L", pixel_size, fobj.read(sizesq), "raw", "L", 0, 1
 | 
			
		||||
        )
 | 
			
		||||
    band = Image.frombuffer("L", pixel_size, fobj.read(sizesq), "raw", "L", 0, 1)
 | 
			
		||||
    return {"A": band}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -107,73 +101,58 @@ def read_png_or_jpeg2000(fobj, start_length, size):
 | 
			
		|||
    (start, length) = start_length
 | 
			
		||||
    fobj.seek(start)
 | 
			
		||||
    sig = fobj.read(12)
 | 
			
		||||
    if sig[:8] == b'\x89PNG\x0d\x0a\x1a\x0a':
 | 
			
		||||
    if sig[:8] == b"\x89PNG\x0d\x0a\x1a\x0a":
 | 
			
		||||
        fobj.seek(start)
 | 
			
		||||
        im = PngImagePlugin.PngImageFile(fobj)
 | 
			
		||||
        return {"RGBA": im}
 | 
			
		||||
    elif sig[:4] == b'\xff\x4f\xff\x51' \
 | 
			
		||||
            or sig[:4] == b'\x0d\x0a\x87\x0a' \
 | 
			
		||||
            or sig == b'\x00\x00\x00\x0cjP  \x0d\x0a\x87\x0a':
 | 
			
		||||
    elif (
 | 
			
		||||
        sig[:4] == b"\xff\x4f\xff\x51"
 | 
			
		||||
        or sig[:4] == b"\x0d\x0a\x87\x0a"
 | 
			
		||||
        or sig == b"\x00\x00\x00\x0cjP  \x0d\x0a\x87\x0a"
 | 
			
		||||
    ):
 | 
			
		||||
        if not enable_jpeg2k:
 | 
			
		||||
            raise ValueError('Unsupported icon subimage format (rebuild PIL '
 | 
			
		||||
                             'with JPEG 2000 support to fix this)')
 | 
			
		||||
            raise ValueError(
 | 
			
		||||
                "Unsupported icon subimage format (rebuild PIL "
 | 
			
		||||
                "with JPEG 2000 support to fix this)"
 | 
			
		||||
            )
 | 
			
		||||
        # j2k, jpc or j2c
 | 
			
		||||
        fobj.seek(start)
 | 
			
		||||
        jp2kstream = fobj.read(length)
 | 
			
		||||
        f = io.BytesIO(jp2kstream)
 | 
			
		||||
        im = Jpeg2KImagePlugin.Jpeg2KImageFile(f)
 | 
			
		||||
        if im.mode != 'RGBA':
 | 
			
		||||
            im = im.convert('RGBA')
 | 
			
		||||
        if im.mode != "RGBA":
 | 
			
		||||
            im = im.convert("RGBA")
 | 
			
		||||
        return {"RGBA": im}
 | 
			
		||||
    else:
 | 
			
		||||
        raise ValueError('Unsupported icon subimage format')
 | 
			
		||||
        raise ValueError("Unsupported icon subimage format")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class IcnsFile(object):
 | 
			
		||||
 | 
			
		||||
    SIZES = {
 | 
			
		||||
        (512, 512, 2): [
 | 
			
		||||
            (b'ic10', read_png_or_jpeg2000),
 | 
			
		||||
        ],
 | 
			
		||||
        (512, 512, 1): [
 | 
			
		||||
            (b'ic09', read_png_or_jpeg2000),
 | 
			
		||||
        ],
 | 
			
		||||
        (256, 256, 2): [
 | 
			
		||||
            (b'ic14', read_png_or_jpeg2000),
 | 
			
		||||
        ],
 | 
			
		||||
        (256, 256, 1): [
 | 
			
		||||
            (b'ic08', read_png_or_jpeg2000),
 | 
			
		||||
        ],
 | 
			
		||||
        (128, 128, 2): [
 | 
			
		||||
            (b'ic13', read_png_or_jpeg2000),
 | 
			
		||||
        ],
 | 
			
		||||
        (512, 512, 2): [(b"ic10", read_png_or_jpeg2000)],
 | 
			
		||||
        (512, 512, 1): [(b"ic09", read_png_or_jpeg2000)],
 | 
			
		||||
        (256, 256, 2): [(b"ic14", read_png_or_jpeg2000)],
 | 
			
		||||
        (256, 256, 1): [(b"ic08", read_png_or_jpeg2000)],
 | 
			
		||||
        (128, 128, 2): [(b"ic13", read_png_or_jpeg2000)],
 | 
			
		||||
        (128, 128, 1): [
 | 
			
		||||
            (b'ic07', read_png_or_jpeg2000),
 | 
			
		||||
            (b'it32', read_32t),
 | 
			
		||||
            (b't8mk', read_mk),
 | 
			
		||||
        ],
 | 
			
		||||
        (64, 64, 1): [
 | 
			
		||||
            (b'icp6', read_png_or_jpeg2000),
 | 
			
		||||
        ],
 | 
			
		||||
        (32, 32, 2): [
 | 
			
		||||
            (b'ic12', read_png_or_jpeg2000),
 | 
			
		||||
        ],
 | 
			
		||||
        (48, 48, 1): [
 | 
			
		||||
            (b'ih32', read_32),
 | 
			
		||||
            (b'h8mk', read_mk),
 | 
			
		||||
            (b"ic07", read_png_or_jpeg2000),
 | 
			
		||||
            (b"it32", read_32t),
 | 
			
		||||
            (b"t8mk", read_mk),
 | 
			
		||||
        ],
 | 
			
		||||
        (64, 64, 1): [(b"icp6", read_png_or_jpeg2000)],
 | 
			
		||||
        (32, 32, 2): [(b"ic12", read_png_or_jpeg2000)],
 | 
			
		||||
        (48, 48, 1): [(b"ih32", read_32), (b"h8mk", read_mk)],
 | 
			
		||||
        (32, 32, 1): [
 | 
			
		||||
            (b'icp5', read_png_or_jpeg2000),
 | 
			
		||||
            (b'il32', read_32),
 | 
			
		||||
            (b'l8mk', read_mk),
 | 
			
		||||
        ],
 | 
			
		||||
        (16, 16, 2): [
 | 
			
		||||
            (b'ic11', read_png_or_jpeg2000),
 | 
			
		||||
            (b"icp5", read_png_or_jpeg2000),
 | 
			
		||||
            (b"il32", read_32),
 | 
			
		||||
            (b"l8mk", read_mk),
 | 
			
		||||
        ],
 | 
			
		||||
        (16, 16, 2): [(b"ic11", read_png_or_jpeg2000)],
 | 
			
		||||
        (16, 16, 1): [
 | 
			
		||||
            (b'icp4', read_png_or_jpeg2000),
 | 
			
		||||
            (b'is32', read_32),
 | 
			
		||||
            (b's8mk', read_mk),
 | 
			
		||||
            (b"icp4", read_png_or_jpeg2000),
 | 
			
		||||
            (b"is32", read_32),
 | 
			
		||||
            (b"s8mk", read_mk),
 | 
			
		||||
        ],
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -185,13 +164,13 @@ class IcnsFile(object):
 | 
			
		|||
        self.dct = dct = {}
 | 
			
		||||
        self.fobj = fobj
 | 
			
		||||
        sig, filesize = nextheader(fobj)
 | 
			
		||||
        if sig != b'icns':
 | 
			
		||||
            raise SyntaxError('not an icns file')
 | 
			
		||||
        if sig != b"icns":
 | 
			
		||||
            raise SyntaxError("not an icns file")
 | 
			
		||||
        i = HEADERSIZE
 | 
			
		||||
        while i < filesize:
 | 
			
		||||
            sig, blocksize = nextheader(fobj)
 | 
			
		||||
            if blocksize <= 0:
 | 
			
		||||
                raise SyntaxError('invalid block header')
 | 
			
		||||
                raise SyntaxError("invalid block header")
 | 
			
		||||
            i += HEADERSIZE
 | 
			
		||||
            blocksize -= HEADERSIZE
 | 
			
		||||
            dct[sig] = (i, blocksize)
 | 
			
		||||
| 
						 | 
				
			
			@ -233,7 +212,7 @@ class IcnsFile(object):
 | 
			
		|||
            size = (size[0], size[1], 1)
 | 
			
		||||
        channels = self.dataforsize(size)
 | 
			
		||||
 | 
			
		||||
        im = channels.get('RGBA', None)
 | 
			
		||||
        im = channels.get("RGBA", None)
 | 
			
		||||
        if im:
 | 
			
		||||
            return im
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -248,6 +227,7 @@ class IcnsFile(object):
 | 
			
		|||
##
 | 
			
		||||
# Image plugin for Mac OS icons.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class IcnsImageFile(ImageFile.ImageFile):
 | 
			
		||||
    """
 | 
			
		||||
    PIL image support for Mac OS .icns files.
 | 
			
		||||
| 
						 | 
				
			
			@ -264,13 +244,15 @@ class IcnsImageFile(ImageFile.ImageFile):
 | 
			
		|||
 | 
			
		||||
    def _open(self):
 | 
			
		||||
        self.icns = IcnsFile(self.fp)
 | 
			
		||||
        self.mode = 'RGBA'
 | 
			
		||||
        self.info['sizes'] = self.icns.itersizes()
 | 
			
		||||
        self.mode = "RGBA"
 | 
			
		||||
        self.info["sizes"] = self.icns.itersizes()
 | 
			
		||||
        self.best_size = self.icns.bestsize()
 | 
			
		||||
        self.size = (self.best_size[0] * self.best_size[2],
 | 
			
		||||
                     self.best_size[1] * self.best_size[2])
 | 
			
		||||
        self.size = (
 | 
			
		||||
            self.best_size[0] * self.best_size[2],
 | 
			
		||||
            self.best_size[1] * self.best_size[2],
 | 
			
		||||
        )
 | 
			
		||||
        # Just use this to see if it's loaded or not yet.
 | 
			
		||||
        self.tile = ('',)
 | 
			
		||||
        self.tile = ("",)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def size(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -279,24 +261,29 @@ class IcnsImageFile(ImageFile.ImageFile):
 | 
			
		|||
    @size.setter
 | 
			
		||||
    def size(self, value):
 | 
			
		||||
        info_size = value
 | 
			
		||||
        if info_size not in self.info['sizes'] and len(info_size) == 2:
 | 
			
		||||
        if info_size not in self.info["sizes"] and len(info_size) == 2:
 | 
			
		||||
            info_size = (info_size[0], info_size[1], 1)
 | 
			
		||||
        if info_size not in self.info['sizes'] and len(info_size) == 3 and \
 | 
			
		||||
           info_size[2] == 1:
 | 
			
		||||
            simple_sizes = [(size[0] * size[2], size[1] * size[2])
 | 
			
		||||
                            for size in self.info['sizes']]
 | 
			
		||||
        if (
 | 
			
		||||
            info_size not in self.info["sizes"]
 | 
			
		||||
            and len(info_size) == 3
 | 
			
		||||
            and info_size[2] == 1
 | 
			
		||||
        ):
 | 
			
		||||
            simple_sizes = [
 | 
			
		||||
                (size[0] * size[2], size[1] * size[2]) for size in self.info["sizes"]
 | 
			
		||||
            ]
 | 
			
		||||
            if value in simple_sizes:
 | 
			
		||||
                info_size = self.info['sizes'][simple_sizes.index(value)]
 | 
			
		||||
        if info_size not in self.info['sizes']:
 | 
			
		||||
            raise ValueError(
 | 
			
		||||
                "This is not one of the allowed sizes of this image")
 | 
			
		||||
                info_size = self.info["sizes"][simple_sizes.index(value)]
 | 
			
		||||
        if info_size not in self.info["sizes"]:
 | 
			
		||||
            raise ValueError("This is not one of the allowed sizes of this image")
 | 
			
		||||
        self._size = value
 | 
			
		||||
 | 
			
		||||
    def load(self):
 | 
			
		||||
        if len(self.size) == 3:
 | 
			
		||||
            self.best_size = self.size
 | 
			
		||||
            self.size = (self.best_size[0] * self.best_size[2],
 | 
			
		||||
                         self.best_size[1] * self.best_size[2])
 | 
			
		||||
            self.size = (
 | 
			
		||||
                self.best_size[0] * self.best_size[2],
 | 
			
		||||
                self.best_size[1] * self.best_size[2],
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        Image.Image.load(self)
 | 
			
		||||
        if not self.tile:
 | 
			
		||||
| 
						 | 
				
			
			@ -331,31 +318,30 @@ def _save(im, fp, filename):
 | 
			
		|||
        fp.flush()
 | 
			
		||||
 | 
			
		||||
    # create the temporary set of pngs
 | 
			
		||||
    iconset = tempfile.mkdtemp('.iconset')
 | 
			
		||||
    provided_images = {im.width: im
 | 
			
		||||
                       for im in im.encoderinfo.get("append_images", [])}
 | 
			
		||||
    iconset = tempfile.mkdtemp(".iconset")
 | 
			
		||||
    provided_images = {im.width: im for im in im.encoderinfo.get("append_images", [])}
 | 
			
		||||
    last_w = None
 | 
			
		||||
    second_path = None
 | 
			
		||||
    for w in [16, 32, 128, 256, 512]:
 | 
			
		||||
        prefix = 'icon_{}x{}'.format(w, w)
 | 
			
		||||
        prefix = "icon_{}x{}".format(w, w)
 | 
			
		||||
 | 
			
		||||
        first_path = os.path.join(iconset, prefix+'.png')
 | 
			
		||||
        first_path = os.path.join(iconset, prefix + ".png")
 | 
			
		||||
        if last_w == w:
 | 
			
		||||
            shutil.copyfile(second_path, first_path)
 | 
			
		||||
        else:
 | 
			
		||||
            im_w = provided_images.get(w, im.resize((w, w), Image.LANCZOS))
 | 
			
		||||
            im_w.save(first_path)
 | 
			
		||||
 | 
			
		||||
        second_path = os.path.join(iconset, prefix+'@2x.png')
 | 
			
		||||
        im_w2 = provided_images.get(w*2, im.resize((w*2, w*2), Image.LANCZOS))
 | 
			
		||||
        second_path = os.path.join(iconset, prefix + "@2x.png")
 | 
			
		||||
        im_w2 = provided_images.get(w * 2, im.resize((w * 2, w * 2), Image.LANCZOS))
 | 
			
		||||
        im_w2.save(second_path)
 | 
			
		||||
        last_w = w*2
 | 
			
		||||
        last_w = w * 2
 | 
			
		||||
 | 
			
		||||
    # iconutil -c icns -o {} {}
 | 
			
		||||
    from subprocess import Popen, PIPE, CalledProcessError
 | 
			
		||||
 | 
			
		||||
    convert_cmd = ["iconutil", "-c", "icns", "-o", filename, iconset]
 | 
			
		||||
    with open(os.devnull, 'wb') as devnull:
 | 
			
		||||
    with open(os.devnull, "wb") as devnull:
 | 
			
		||||
        convert_proc = Popen(convert_cmd, stdout=PIPE, stderr=devnull)
 | 
			
		||||
 | 
			
		||||
    convert_proc.stdout.close()
 | 
			
		||||
| 
						 | 
				
			
			@ -369,29 +355,28 @@ def _save(im, fp, filename):
 | 
			
		|||
        raise CalledProcessError(retcode, convert_cmd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Image.register_open(IcnsImageFile.format, IcnsImageFile,
 | 
			
		||||
                    lambda x: x[:4] == b'icns')
 | 
			
		||||
Image.register_extension(IcnsImageFile.format, '.icns')
 | 
			
		||||
Image.register_open(IcnsImageFile.format, IcnsImageFile, lambda x: x[:4] == b"icns")
 | 
			
		||||
Image.register_extension(IcnsImageFile.format, ".icns")
 | 
			
		||||
 | 
			
		||||
if sys.platform == 'darwin':
 | 
			
		||||
if sys.platform == "darwin":
 | 
			
		||||
    Image.register_save(IcnsImageFile.format, _save)
 | 
			
		||||
 | 
			
		||||
    Image.register_mime(IcnsImageFile.format, "image/icns")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
 | 
			
		||||
    if len(sys.argv) < 2:
 | 
			
		||||
        print("Syntax: python IcnsImagePlugin.py [file]")
 | 
			
		||||
        sys.exit()
 | 
			
		||||
 | 
			
		||||
    imf = IcnsImageFile(open(sys.argv[1], 'rb'))
 | 
			
		||||
    for size in imf.info['sizes']:
 | 
			
		||||
    imf = IcnsImageFile(open(sys.argv[1], "rb"))
 | 
			
		||||
    for size in imf.info["sizes"]:
 | 
			
		||||
        imf.size = size
 | 
			
		||||
        imf.load()
 | 
			
		||||
        im = imf.im
 | 
			
		||||
        im.save('out-%s-%s-%s.png' % size)
 | 
			
		||||
        im.save("out-%s-%s-%s.png" % size)
 | 
			
		||||
    im = Image.open(sys.argv[1])
 | 
			
		||||
    im.save("out.png")
 | 
			
		||||
    if sys.platform == 'windows':
 | 
			
		||||
    if sys.platform == "windows":
 | 
			
		||||
        os.startfile("out.png")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,16 +42,20 @@ _MAGIC = b"\0\0\1\0"
 | 
			
		|||
 | 
			
		||||
def _save(im, fp, filename):
 | 
			
		||||
    fp.write(_MAGIC)  # (2+2)
 | 
			
		||||
    sizes = im.encoderinfo.get("sizes",
 | 
			
		||||
                               [(16, 16), (24, 24), (32, 32), (48, 48),
 | 
			
		||||
                                (64, 64), (128, 128), (256, 256)])
 | 
			
		||||
    sizes = im.encoderinfo.get(
 | 
			
		||||
        "sizes",
 | 
			
		||||
        [(16, 16), (24, 24), (32, 32), (48, 48), (64, 64), (128, 128), (256, 256)],
 | 
			
		||||
    )
 | 
			
		||||
    width, height = im.size
 | 
			
		||||
    sizes = filter(lambda x: False if (x[0] > width or x[1] > height or
 | 
			
		||||
                                       x[0] > 256 or x[1] > 256) else True,
 | 
			
		||||
                   sizes)
 | 
			
		||||
    sizes = filter(
 | 
			
		||||
        lambda x: False
 | 
			
		||||
        if (x[0] > width or x[1] > height or x[0] > 256 or x[1] > 256)
 | 
			
		||||
        else True,
 | 
			
		||||
        sizes,
 | 
			
		||||
    )
 | 
			
		||||
    sizes = list(sizes)
 | 
			
		||||
    fp.write(struct.pack("<H", len(sizes)))  # idCount(2)
 | 
			
		||||
    offset = fp.tell() + len(sizes)*16
 | 
			
		||||
    offset = fp.tell() + len(sizes) * 16
 | 
			
		||||
    for size in sizes:
 | 
			
		||||
        width, height = size
 | 
			
		||||
        # 0 means 256
 | 
			
		||||
| 
						 | 
				
			
			@ -104,49 +108,52 @@ class IcoFile(object):
 | 
			
		|||
            s = buf.read(16)
 | 
			
		||||
 | 
			
		||||
            icon_header = {
 | 
			
		||||
                'width': i8(s[0]),
 | 
			
		||||
                'height': i8(s[1]),
 | 
			
		||||
                'nb_color': i8(s[2]),  # No. of colors in image (0 if >=8bpp)
 | 
			
		||||
                'reserved': i8(s[3]),
 | 
			
		||||
                'planes': i16(s[4:]),
 | 
			
		||||
                'bpp': i16(s[6:]),
 | 
			
		||||
                'size': i32(s[8:]),
 | 
			
		||||
                'offset': i32(s[12:])
 | 
			
		||||
                "width": i8(s[0]),
 | 
			
		||||
                "height": i8(s[1]),
 | 
			
		||||
                "nb_color": i8(s[2]),  # No. of colors in image (0 if >=8bpp)
 | 
			
		||||
                "reserved": i8(s[3]),
 | 
			
		||||
                "planes": i16(s[4:]),
 | 
			
		||||
                "bpp": i16(s[6:]),
 | 
			
		||||
                "size": i32(s[8:]),
 | 
			
		||||
                "offset": i32(s[12:]),
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            # See Wikipedia
 | 
			
		||||
            for j in ('width', 'height'):
 | 
			
		||||
            for j in ("width", "height"):
 | 
			
		||||
                if not icon_header[j]:
 | 
			
		||||
                    icon_header[j] = 256
 | 
			
		||||
 | 
			
		||||
            # See Wikipedia notes about color depth.
 | 
			
		||||
            # We need this just to differ images with equal sizes
 | 
			
		||||
            icon_header['color_depth'] = (icon_header['bpp'] or
 | 
			
		||||
                                          (icon_header['nb_color'] != 0 and
 | 
			
		||||
                                           ceil(log(icon_header['nb_color'],
 | 
			
		||||
                                                    2))) or 256)
 | 
			
		||||
            icon_header["color_depth"] = (
 | 
			
		||||
                icon_header["bpp"]
 | 
			
		||||
                or (
 | 
			
		||||
                    icon_header["nb_color"] != 0
 | 
			
		||||
                    and ceil(log(icon_header["nb_color"], 2))
 | 
			
		||||
                )
 | 
			
		||||
                or 256
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            icon_header['dim'] = (icon_header['width'], icon_header['height'])
 | 
			
		||||
            icon_header['square'] = (icon_header['width'] *
 | 
			
		||||
                                     icon_header['height'])
 | 
			
		||||
            icon_header["dim"] = (icon_header["width"], icon_header["height"])
 | 
			
		||||
            icon_header["square"] = icon_header["width"] * icon_header["height"]
 | 
			
		||||
 | 
			
		||||
            self.entry.append(icon_header)
 | 
			
		||||
 | 
			
		||||
        self.entry = sorted(self.entry, key=lambda x: x['color_depth'])
 | 
			
		||||
        self.entry = sorted(self.entry, key=lambda x: x["color_depth"])
 | 
			
		||||
        # ICO images are usually squares
 | 
			
		||||
        # self.entry = sorted(self.entry, key=lambda x: x['width'])
 | 
			
		||||
        self.entry = sorted(self.entry, key=lambda x: x['square'])
 | 
			
		||||
        self.entry = sorted(self.entry, key=lambda x: x["square"])
 | 
			
		||||
        self.entry.reverse()
 | 
			
		||||
 | 
			
		||||
    def sizes(self):
 | 
			
		||||
        """
 | 
			
		||||
        Get a list of all available icon sizes and color depths.
 | 
			
		||||
        """
 | 
			
		||||
        return {(h['width'], h['height']) for h in self.entry}
 | 
			
		||||
        return {(h["width"], h["height"]) for h in self.entry}
 | 
			
		||||
 | 
			
		||||
    def getentryindex(self, size, bpp=False):
 | 
			
		||||
        for (i, h) in enumerate(self.entry):
 | 
			
		||||
            if size == h['dim'] and (bpp is False or bpp == h['color_depth']):
 | 
			
		||||
            if size == h["dim"] and (bpp is False or bpp == h["color_depth"]):
 | 
			
		||||
                return i
 | 
			
		||||
        return 0
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -163,9 +170,9 @@ class IcoFile(object):
 | 
			
		|||
 | 
			
		||||
        header = self.entry[idx]
 | 
			
		||||
 | 
			
		||||
        self.buf.seek(header['offset'])
 | 
			
		||||
        self.buf.seek(header["offset"])
 | 
			
		||||
        data = self.buf.read(8)
 | 
			
		||||
        self.buf.seek(header['offset'])
 | 
			
		||||
        self.buf.seek(header["offset"])
 | 
			
		||||
 | 
			
		||||
        if data[:8] == PngImagePlugin._MAGIC:
 | 
			
		||||
            # png frame
 | 
			
		||||
| 
						 | 
				
			
			@ -200,11 +207,11 @@ class IcoFile(object):
 | 
			
		|||
 | 
			
		||||
                # convert to an 8bpp grayscale image
 | 
			
		||||
                mask = Image.frombuffer(
 | 
			
		||||
                    'L',            # 8bpp
 | 
			
		||||
                    "L",  # 8bpp
 | 
			
		||||
                    im.size,  # (w, h)
 | 
			
		||||
                    alpha_bytes,  # source chars
 | 
			
		||||
                    'raw',          # raw decoder
 | 
			
		||||
                    ('L', 0, -1)    # 8bpp inverted, unpadded, reversed
 | 
			
		||||
                    "raw",  # raw decoder
 | 
			
		||||
                    ("L", 0, -1),  # 8bpp inverted, unpadded, reversed
 | 
			
		||||
                )
 | 
			
		||||
            else:
 | 
			
		||||
                # get AND image from end of bitmap
 | 
			
		||||
| 
						 | 
				
			
			@ -216,8 +223,7 @@ class IcoFile(object):
 | 
			
		|||
                # the total mask data is
 | 
			
		||||
                # padded row size * height / bits per char
 | 
			
		||||
 | 
			
		||||
                and_mask_offset = o + int(im.size[0] * im.size[1] *
 | 
			
		||||
                                          (bpp / 8.0))
 | 
			
		||||
                and_mask_offset = o + int(im.size[0] * im.size[1] * (bpp / 8.0))
 | 
			
		||||
                total_bytes = int((w * im.size[1]) / 8)
 | 
			
		||||
 | 
			
		||||
                self.buf.seek(and_mask_offset)
 | 
			
		||||
| 
						 | 
				
			
			@ -225,17 +231,17 @@ class IcoFile(object):
 | 
			
		|||
 | 
			
		||||
                # convert raw data to image
 | 
			
		||||
                mask = Image.frombuffer(
 | 
			
		||||
                    '1',            # 1 bpp
 | 
			
		||||
                    "1",  # 1 bpp
 | 
			
		||||
                    im.size,  # (w, h)
 | 
			
		||||
                    mask_data,  # source chars
 | 
			
		||||
                    'raw',          # raw decoder
 | 
			
		||||
                    ('1;I', int(w/8), -1)  # 1bpp inverted, padded, reversed
 | 
			
		||||
                    "raw",  # raw decoder
 | 
			
		||||
                    ("1;I", int(w / 8), -1),  # 1bpp inverted, padded, reversed
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
                # now we have two images, im is XOR image and mask is AND image
 | 
			
		||||
 | 
			
		||||
            # apply mask image as alpha channel
 | 
			
		||||
            im = im.convert('RGBA')
 | 
			
		||||
            im = im.convert("RGBA")
 | 
			
		||||
            im.putalpha(mask)
 | 
			
		||||
 | 
			
		||||
        return im
 | 
			
		||||
| 
						 | 
				
			
			@ -244,6 +250,7 @@ class IcoFile(object):
 | 
			
		|||
##
 | 
			
		||||
# Image plugin for Windows Icon files.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class IcoImageFile(ImageFile.ImageFile):
 | 
			
		||||
    """
 | 
			
		||||
    PIL read-only image support for Microsoft Windows .ico files.
 | 
			
		||||
| 
						 | 
				
			
			@ -260,13 +267,14 @@ class IcoImageFile(ImageFile.ImageFile):
 | 
			
		|||
    <casadebender@gmail.com>.
 | 
			
		||||
    https://code.google.com/archive/p/casadebender/wikis/Win32IconImagePlugin.wiki
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    format = "ICO"
 | 
			
		||||
    format_description = "Windows Icon"
 | 
			
		||||
 | 
			
		||||
    def _open(self):
 | 
			
		||||
        self.ico = IcoFile(self.fp)
 | 
			
		||||
        self.info['sizes'] = self.ico.sizes()
 | 
			
		||||
        self.size = self.ico.entry[0]['dim']
 | 
			
		||||
        self.info["sizes"] = self.ico.sizes()
 | 
			
		||||
        self.size = self.ico.entry[0]["dim"]
 | 
			
		||||
        self.load()
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
| 
						 | 
				
			
			@ -275,9 +283,8 @@ class IcoImageFile(ImageFile.ImageFile):
 | 
			
		|||
 | 
			
		||||
    @size.setter
 | 
			
		||||
    def size(self, value):
 | 
			
		||||
        if value not in self.info['sizes']:
 | 
			
		||||
            raise ValueError(
 | 
			
		||||
                "This is not one of the allowed sizes of this image")
 | 
			
		||||
        if value not in self.info["sizes"]:
 | 
			
		||||
            raise ValueError("This is not one of the allowed sizes of this image")
 | 
			
		||||
        self._size = value
 | 
			
		||||
 | 
			
		||||
    def load(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -290,9 +297,9 @@ class IcoImageFile(ImageFile.ImageFile):
 | 
			
		|||
            warnings.warn("Image was not the expected size")
 | 
			
		||||
 | 
			
		||||
            index = self.ico.getentryindex(self.size)
 | 
			
		||||
            sizes = list(self.info['sizes'])
 | 
			
		||||
            sizes = list(self.info["sizes"])
 | 
			
		||||
            sizes[index] = im.size
 | 
			
		||||
            self.info['sizes'] = set(sizes)
 | 
			
		||||
            self.info["sizes"] = set(sizes)
 | 
			
		||||
 | 
			
		||||
            self.size = im.size
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -300,6 +307,8 @@ class IcoImageFile(ImageFile.ImageFile):
 | 
			
		|||
        # Flag the ImageFile.Parser so that it
 | 
			
		||||
        # just does all the decode at the end.
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# --------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -48,8 +48,17 @@ SCALE = "Scale (x,y)"
 | 
			
		|||
SIZE = "Image size (x*y)"
 | 
			
		||||
MODE = "Image type"
 | 
			
		||||
 | 
			
		||||
TAGS = {COMMENT: 0, DATE: 0, EQUIPMENT: 0, FRAMES: 0, LUT: 0, NAME: 0,
 | 
			
		||||
        SCALE: 0, SIZE: 0, MODE: 0}
 | 
			
		||||
TAGS = {
 | 
			
		||||
    COMMENT: 0,
 | 
			
		||||
    DATE: 0,
 | 
			
		||||
    EQUIPMENT: 0,
 | 
			
		||||
    FRAMES: 0,
 | 
			
		||||
    LUT: 0,
 | 
			
		||||
    NAME: 0,
 | 
			
		||||
    SCALE: 0,
 | 
			
		||||
    SIZE: 0,
 | 
			
		||||
    MODE: 0,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
OPEN = {
 | 
			
		||||
    # ifunc93/p3cfunc formats
 | 
			
		||||
| 
						 | 
				
			
			@ -108,6 +117,7 @@ def number(s):
 | 
			
		|||
##
 | 
			
		||||
# Image plugin for the IFUNC IM file format.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ImImageFile(ImageFile.ImageFile):
 | 
			
		||||
 | 
			
		||||
    format = "IM"
 | 
			
		||||
| 
						 | 
				
			
			@ -140,7 +150,7 @@ class ImImageFile(ImageFile.ImageFile):
 | 
			
		|||
            if s == b"\r":
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            if not s or s == b'\0' or s == b'\x1A':
 | 
			
		||||
            if not s or s == b"\0" or s == b"\x1A":
 | 
			
		||||
                break
 | 
			
		||||
 | 
			
		||||
            # FIXME: this may read whole file if not a text file
 | 
			
		||||
| 
						 | 
				
			
			@ -149,9 +159,9 @@ class ImImageFile(ImageFile.ImageFile):
 | 
			
		|||
            if len(s) > 100:
 | 
			
		||||
                raise SyntaxError("not an IM file")
 | 
			
		||||
 | 
			
		||||
            if s[-2:] == b'\r\n':
 | 
			
		||||
            if s[-2:] == b"\r\n":
 | 
			
		||||
                s = s[:-2]
 | 
			
		||||
            elif s[-1:] == b'\n':
 | 
			
		||||
            elif s[-1:] == b"\n":
 | 
			
		||||
                s = s[:-1]
 | 
			
		||||
 | 
			
		||||
            try:
 | 
			
		||||
| 
						 | 
				
			
			@ -165,8 +175,8 @@ class ImImageFile(ImageFile.ImageFile):
 | 
			
		|||
 | 
			
		||||
                # Don't know if this is the correct encoding,
 | 
			
		||||
                # but a decent guess (I guess)
 | 
			
		||||
                k = k.decode('latin-1', 'replace')
 | 
			
		||||
                v = v.decode('latin-1', 'replace')
 | 
			
		||||
                k = k.decode("latin-1", "replace")
 | 
			
		||||
                v = v.decode("latin-1", "replace")
 | 
			
		||||
 | 
			
		||||
                # Convert value as appropriate
 | 
			
		||||
                if k in [FRAMES, SCALE, SIZE]:
 | 
			
		||||
| 
						 | 
				
			
			@ -192,8 +202,9 @@ class ImImageFile(ImageFile.ImageFile):
 | 
			
		|||
 | 
			
		||||
            else:
 | 
			
		||||
 | 
			
		||||
                raise SyntaxError("Syntax error in IM header: " +
 | 
			
		||||
                                  s.decode('ascii', 'replace'))
 | 
			
		||||
                raise SyntaxError(
 | 
			
		||||
                    "Syntax error in IM header: " + s.decode("ascii", "replace")
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
        if not n:
 | 
			
		||||
            raise SyntaxError("Not an IM file")
 | 
			
		||||
| 
						 | 
				
			
			@ -203,7 +214,7 @@ class ImImageFile(ImageFile.ImageFile):
 | 
			
		|||
        self.mode = self.info[MODE]
 | 
			
		||||
 | 
			
		||||
        # Skip forward to start of image data
 | 
			
		||||
        while s and s[0:1] != b'\x1A':
 | 
			
		||||
        while s and s[0:1] != b"\x1A":
 | 
			
		||||
            s = self.fp.read(1)
 | 
			
		||||
        if not s:
 | 
			
		||||
            raise SyntaxError("File truncated")
 | 
			
		||||
| 
						 | 
				
			
			@ -214,7 +225,7 @@ class ImImageFile(ImageFile.ImageFile):
 | 
			
		|||
            greyscale = 1  # greyscale palette
 | 
			
		||||
            linear = 1  # linear greyscale palette
 | 
			
		||||
            for i in range(256):
 | 
			
		||||
                if palette[i] == palette[i+256] == palette[i+512]:
 | 
			
		||||
                if palette[i] == palette[i + 256] == palette[i + 512]:
 | 
			
		||||
                    if i8(palette[i]) != i:
 | 
			
		||||
                        linear = 0
 | 
			
		||||
                else:
 | 
			
		||||
| 
						 | 
				
			
			@ -247,8 +258,7 @@ class ImImageFile(ImageFile.ImageFile):
 | 
			
		|||
                # use bit decoder (if necessary)
 | 
			
		||||
                bits = int(self.rawmode[2:])
 | 
			
		||||
                if bits not in [8, 16, 32]:
 | 
			
		||||
                    self.tile = [("bit", (0, 0)+self.size, offs,
 | 
			
		||||
                                 (bits, 8, 3, 0, -1))]
 | 
			
		||||
                    self.tile = [("bit", (0, 0) + self.size, offs, (bits, 8, 3, 0, -1))]
 | 
			
		||||
                    return
 | 
			
		||||
            except ValueError:
 | 
			
		||||
                pass
 | 
			
		||||
| 
						 | 
				
			
			@ -257,13 +267,14 @@ class ImImageFile(ImageFile.ImageFile):
 | 
			
		|||
            # Old LabEye/3PC files.  Would be very surprised if anyone
 | 
			
		||||
            # ever stumbled upon such a file ;-)
 | 
			
		||||
            size = self.size[0] * self.size[1]
 | 
			
		||||
            self.tile = [("raw", (0, 0)+self.size, offs, ("G", 0, -1)),
 | 
			
		||||
                         ("raw", (0, 0)+self.size, offs+size, ("R", 0, -1)),
 | 
			
		||||
                         ("raw", (0, 0)+self.size, offs+2*size, ("B", 0, -1))]
 | 
			
		||||
            self.tile = [
 | 
			
		||||
                ("raw", (0, 0) + self.size, offs, ("G", 0, -1)),
 | 
			
		||||
                ("raw", (0, 0) + self.size, offs + size, ("R", 0, -1)),
 | 
			
		||||
                ("raw", (0, 0) + self.size, offs + 2 * size, ("B", 0, -1)),
 | 
			
		||||
            ]
 | 
			
		||||
        else:
 | 
			
		||||
            # LabEye/IFUNC files
 | 
			
		||||
            self.tile = [("raw", (0, 0)+self.size, offs,
 | 
			
		||||
                         (self.rawmode, 0, -1))]
 | 
			
		||||
            self.tile = [("raw", (0, 0) + self.size, offs, (self.rawmode, 0, -1))]
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def n_frames(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -289,7 +300,7 @@ class ImImageFile(ImageFile.ImageFile):
 | 
			
		|||
 | 
			
		||||
        self.fp = self.__fp
 | 
			
		||||
 | 
			
		||||
        self.tile = [("raw", (0, 0)+self.size, offs, (self.rawmode, 0, -1))]
 | 
			
		||||
        self.tile = [("raw", (0, 0) + self.size, offs, (self.rawmode, 0, -1))]
 | 
			
		||||
 | 
			
		||||
    def tell(self):
 | 
			
		||||
        return self.frame
 | 
			
		||||
| 
						 | 
				
			
			@ -303,6 +314,7 @@ class ImImageFile(ImageFile.ImageFile):
 | 
			
		|||
        finally:
 | 
			
		||||
            self.__fp = None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# --------------------------------------------------------------------
 | 
			
		||||
# Save IM files
 | 
			
		||||
| 
						 | 
				
			
			@ -324,7 +336,7 @@ SAVE = {
 | 
			
		|||
    "RGBA": ("RGBA", "RGBA;L"),
 | 
			
		||||
    "RGBX": ("RGBX", "RGBX;L"),
 | 
			
		||||
    "CMYK": ("CMYK", "CMYK;L"),
 | 
			
		||||
    "YCbCr": ("YCC", "YCbCr;L")
 | 
			
		||||
    "YCbCr": ("YCC", "YCbCr;L"),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -337,17 +349,18 @@ def _save(im, fp, filename):
 | 
			
		|||
 | 
			
		||||
    frames = im.encoderinfo.get("frames", 1)
 | 
			
		||||
 | 
			
		||||
    fp.write(("Image type: %s image\r\n" % image_type).encode('ascii'))
 | 
			
		||||
    fp.write(("Image type: %s image\r\n" % image_type).encode("ascii"))
 | 
			
		||||
    if filename:
 | 
			
		||||
        fp.write(("Name: %s\r\n" % filename).encode('ascii'))
 | 
			
		||||
    fp.write(("Image size (x*y): %d*%d\r\n" % im.size).encode('ascii'))
 | 
			
		||||
    fp.write(("File size (no of images): %d\r\n" % frames).encode('ascii'))
 | 
			
		||||
        fp.write(("Name: %s\r\n" % filename).encode("ascii"))
 | 
			
		||||
    fp.write(("Image size (x*y): %d*%d\r\n" % im.size).encode("ascii"))
 | 
			
		||||
    fp.write(("File size (no of images): %d\r\n" % frames).encode("ascii"))
 | 
			
		||||
    if im.mode in ["P", "PA"]:
 | 
			
		||||
        fp.write(b"Lut: 1\r\n")
 | 
			
		||||
    fp.write(b"\000" * (511-fp.tell()) + b"\032")
 | 
			
		||||
    fp.write(b"\000" * (511 - fp.tell()) + b"\032")
 | 
			
		||||
    if im.mode in ["P", "PA"]:
 | 
			
		||||
        fp.write(im.im.getpalette("RGB", "RGB;L"))  # 768 bytes
 | 
			
		||||
    ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0, (rawmode, 0, -1))])
 | 
			
		||||
    ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, 0, -1))])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# --------------------------------------------------------------------
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										601
									
								
								src/PIL/Image.py
									
									
									
									
									
								
							
							
						
						
									
										601
									
								
								src/PIL/Image.py
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
				
			
			@ -19,12 +19,14 @@ from __future__ import print_function
 | 
			
		|||
import sys
 | 
			
		||||
 | 
			
		||||
from PIL import Image
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    from PIL import _imagingcms
 | 
			
		||||
except ImportError as ex:
 | 
			
		||||
    # Allow error import for doc purposes, but error out when accessing
 | 
			
		||||
    # anything in core.
 | 
			
		||||
    from ._util import deferred_error
 | 
			
		||||
 | 
			
		||||
    _imagingcms = deferred_error(ex)
 | 
			
		||||
from PIL._util import isStringType
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -132,7 +134,7 @@ FLAGS = {
 | 
			
		|||
    "SOFTPROOFING": 16384,  # Do softproofing
 | 
			
		||||
    "PRESERVEBLACK": 32768,  # Black preservation
 | 
			
		||||
    "NODEFAULTRESOURCEDEF": 16777216,  # CRD special
 | 
			
		||||
    "GRIDPOINTS": lambda n: ((n) & 0xFF) << 16  # Gridpoints
 | 
			
		||||
    "GRIDPOINTS": lambda n: ((n) & 0xFF) << 16,  # Gridpoints
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_MAX_FLAG = 0
 | 
			
		||||
| 
						 | 
				
			
			@ -148,8 +150,8 @@ for flag in FLAGS.values():
 | 
			
		|||
##
 | 
			
		||||
# Profile.
 | 
			
		||||
 | 
			
		||||
class ImageCmsProfile(object):
 | 
			
		||||
 | 
			
		||||
class ImageCmsProfile(object):
 | 
			
		||||
    def __init__(self, profile):
 | 
			
		||||
        """
 | 
			
		||||
        :param profile: Either a string representing a filename,
 | 
			
		||||
| 
						 | 
				
			
			@ -197,22 +199,31 @@ class ImageCmsTransform(Image.ImagePointHandler):
 | 
			
		|||
    Will return the output profile in the output.info['icc_profile'].
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, input, output, input_mode, output_mode,
 | 
			
		||||
                 intent=INTENT_PERCEPTUAL, proof=None,
 | 
			
		||||
                 proof_intent=INTENT_ABSOLUTE_COLORIMETRIC, flags=0):
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self,
 | 
			
		||||
        input,
 | 
			
		||||
        output,
 | 
			
		||||
        input_mode,
 | 
			
		||||
        output_mode,
 | 
			
		||||
        intent=INTENT_PERCEPTUAL,
 | 
			
		||||
        proof=None,
 | 
			
		||||
        proof_intent=INTENT_ABSOLUTE_COLORIMETRIC,
 | 
			
		||||
        flags=0,
 | 
			
		||||
    ):
 | 
			
		||||
        if proof is None:
 | 
			
		||||
            self.transform = core.buildTransform(
 | 
			
		||||
                input.profile, output.profile,
 | 
			
		||||
                input_mode, output_mode,
 | 
			
		||||
                intent,
 | 
			
		||||
                flags
 | 
			
		||||
                input.profile, output.profile, input_mode, output_mode, intent, flags
 | 
			
		||||
            )
 | 
			
		||||
        else:
 | 
			
		||||
            self.transform = core.buildProofTransform(
 | 
			
		||||
                input.profile, output.profile, proof.profile,
 | 
			
		||||
                input_mode, output_mode,
 | 
			
		||||
                intent, proof_intent,
 | 
			
		||||
                flags
 | 
			
		||||
                input.profile,
 | 
			
		||||
                output.profile,
 | 
			
		||||
                proof.profile,
 | 
			
		||||
                input_mode,
 | 
			
		||||
                output_mode,
 | 
			
		||||
                intent,
 | 
			
		||||
                proof_intent,
 | 
			
		||||
                flags,
 | 
			
		||||
            )
 | 
			
		||||
        # Note: inputMode and outputMode are for pyCMS compatibility only
 | 
			
		||||
        self.input_mode = self.inputMode = input_mode
 | 
			
		||||
| 
						 | 
				
			
			@ -228,7 +239,7 @@ class ImageCmsTransform(Image.ImagePointHandler):
 | 
			
		|||
        if imOut is None:
 | 
			
		||||
            imOut = Image.new(self.output_mode, im.size, None)
 | 
			
		||||
        self.transform.apply(im.im.id, imOut.im.id)
 | 
			
		||||
        imOut.info['icc_profile'] = self.output_profile.tobytes()
 | 
			
		||||
        imOut.info["icc_profile"] = self.output_profile.tobytes()
 | 
			
		||||
        return imOut
 | 
			
		||||
 | 
			
		||||
    def apply_in_place(self, im):
 | 
			
		||||
| 
						 | 
				
			
			@ -236,7 +247,7 @@ class ImageCmsTransform(Image.ImagePointHandler):
 | 
			
		|||
        if im.mode != self.output_mode:
 | 
			
		||||
            raise ValueError("mode mismatch")  # wrong output mode
 | 
			
		||||
        self.transform.apply(im.im.id, im.im.id)
 | 
			
		||||
        im.info['icc_profile'] = self.output_profile.tobytes()
 | 
			
		||||
        im.info["icc_profile"] = self.output_profile.tobytes()
 | 
			
		||||
        return im
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -247,6 +258,7 @@ def get_display_profile(handle=None):
 | 
			
		|||
 | 
			
		||||
    if sys.platform == "win32":
 | 
			
		||||
        from PIL import ImageWin
 | 
			
		||||
 | 
			
		||||
        if isinstance(handle, ImageWin.HDC):
 | 
			
		||||
            profile = core.get_display_profile_win32(handle, 1)
 | 
			
		||||
        else:
 | 
			
		||||
| 
						 | 
				
			
			@ -265,16 +277,24 @@ def get_display_profile(handle=None):
 | 
			
		|||
# pyCMS compatible layer
 | 
			
		||||
# --------------------------------------------------------------------.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PyCMSError(Exception):
 | 
			
		||||
 | 
			
		||||
    """ (pyCMS) Exception class.
 | 
			
		||||
    This is used for all errors in the pyCMS API. """
 | 
			
		||||
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def profileToProfile(
 | 
			
		||||
        im, inputProfile, outputProfile, renderingIntent=INTENT_PERCEPTUAL,
 | 
			
		||||
        outputMode=None, inPlace=False, flags=0):
 | 
			
		||||
    im,
 | 
			
		||||
    inputProfile,
 | 
			
		||||
    outputProfile,
 | 
			
		||||
    renderingIntent=INTENT_PERCEPTUAL,
 | 
			
		||||
    outputMode=None,
 | 
			
		||||
    inPlace=False,
 | 
			
		||||
    flags=0,
 | 
			
		||||
):
 | 
			
		||||
    """
 | 
			
		||||
    (pyCMS) Applies an ICC transformation to a given image, mapping from
 | 
			
		||||
    inputProfile to outputProfile.
 | 
			
		||||
| 
						 | 
				
			
			@ -333,8 +353,7 @@ def profileToProfile(
 | 
			
		|||
        raise PyCMSError("renderingIntent must be an integer between 0 and 3")
 | 
			
		||||
 | 
			
		||||
    if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
 | 
			
		||||
        raise PyCMSError(
 | 
			
		||||
            "flags must be an integer between 0 and %s" + _MAX_FLAG)
 | 
			
		||||
        raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG)
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        if not isinstance(inputProfile, ImageCmsProfile):
 | 
			
		||||
| 
						 | 
				
			
			@ -342,8 +361,12 @@ def profileToProfile(
 | 
			
		|||
        if not isinstance(outputProfile, ImageCmsProfile):
 | 
			
		||||
            outputProfile = ImageCmsProfile(outputProfile)
 | 
			
		||||
        transform = ImageCmsTransform(
 | 
			
		||||
            inputProfile, outputProfile, im.mode, outputMode,
 | 
			
		||||
            renderingIntent, flags=flags
 | 
			
		||||
            inputProfile,
 | 
			
		||||
            outputProfile,
 | 
			
		||||
            im.mode,
 | 
			
		||||
            outputMode,
 | 
			
		||||
            renderingIntent,
 | 
			
		||||
            flags=flags,
 | 
			
		||||
        )
 | 
			
		||||
        if inPlace:
 | 
			
		||||
            transform.apply_in_place(im)
 | 
			
		||||
| 
						 | 
				
			
			@ -379,8 +402,13 @@ def getOpenProfile(profileFilename):
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
def buildTransform(
 | 
			
		||||
        inputProfile, outputProfile, inMode, outMode,
 | 
			
		||||
        renderingIntent=INTENT_PERCEPTUAL, flags=0):
 | 
			
		||||
    inputProfile,
 | 
			
		||||
    outputProfile,
 | 
			
		||||
    inMode,
 | 
			
		||||
    outMode,
 | 
			
		||||
    renderingIntent=INTENT_PERCEPTUAL,
 | 
			
		||||
    flags=0,
 | 
			
		||||
):
 | 
			
		||||
    """
 | 
			
		||||
    (pyCMS) Builds an ICC transform mapping from the inputProfile to the
 | 
			
		||||
    outputProfile.  Use applyTransform to apply the transform to a given
 | 
			
		||||
| 
						 | 
				
			
			@ -440,8 +468,7 @@ def buildTransform(
 | 
			
		|||
        raise PyCMSError("renderingIntent must be an integer between 0 and 3")
 | 
			
		||||
 | 
			
		||||
    if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
 | 
			
		||||
        raise PyCMSError(
 | 
			
		||||
            "flags must be an integer between 0 and %s" + _MAX_FLAG)
 | 
			
		||||
        raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG)
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        if not isinstance(inputProfile, ImageCmsProfile):
 | 
			
		||||
| 
						 | 
				
			
			@ -449,17 +476,22 @@ def buildTransform(
 | 
			
		|||
        if not isinstance(outputProfile, ImageCmsProfile):
 | 
			
		||||
            outputProfile = ImageCmsProfile(outputProfile)
 | 
			
		||||
        return ImageCmsTransform(
 | 
			
		||||
            inputProfile, outputProfile, inMode, outMode,
 | 
			
		||||
            renderingIntent, flags=flags)
 | 
			
		||||
            inputProfile, outputProfile, inMode, outMode, renderingIntent, flags=flags
 | 
			
		||||
        )
 | 
			
		||||
    except (IOError, TypeError, ValueError) as v:
 | 
			
		||||
        raise PyCMSError(v)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def buildProofTransform(
 | 
			
		||||
        inputProfile, outputProfile, proofProfile, inMode, outMode,
 | 
			
		||||
    inputProfile,
 | 
			
		||||
    outputProfile,
 | 
			
		||||
    proofProfile,
 | 
			
		||||
    inMode,
 | 
			
		||||
    outMode,
 | 
			
		||||
    renderingIntent=INTENT_PERCEPTUAL,
 | 
			
		||||
    proofRenderingIntent=INTENT_ABSOLUTE_COLORIMETRIC,
 | 
			
		||||
        flags=FLAGS["SOFTPROOFING"]):
 | 
			
		||||
    flags=FLAGS["SOFTPROOFING"],
 | 
			
		||||
):
 | 
			
		||||
    """
 | 
			
		||||
    (pyCMS) Builds an ICC transform mapping from the inputProfile to the
 | 
			
		||||
    outputProfile, but tries to simulate the result that would be
 | 
			
		||||
| 
						 | 
				
			
			@ -538,8 +570,7 @@ def buildProofTransform(
 | 
			
		|||
        raise PyCMSError("renderingIntent must be an integer between 0 and 3")
 | 
			
		||||
 | 
			
		||||
    if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
 | 
			
		||||
        raise PyCMSError(
 | 
			
		||||
            "flags must be an integer between 0 and %s" + _MAX_FLAG)
 | 
			
		||||
        raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG)
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        if not isinstance(inputProfile, ImageCmsProfile):
 | 
			
		||||
| 
						 | 
				
			
			@ -549,8 +580,15 @@ def buildProofTransform(
 | 
			
		|||
        if not isinstance(proofProfile, ImageCmsProfile):
 | 
			
		||||
            proofProfile = ImageCmsProfile(proofProfile)
 | 
			
		||||
        return ImageCmsTransform(
 | 
			
		||||
            inputProfile, outputProfile, inMode, outMode, renderingIntent,
 | 
			
		||||
            proofProfile, proofRenderingIntent, flags)
 | 
			
		||||
            inputProfile,
 | 
			
		||||
            outputProfile,
 | 
			
		||||
            inMode,
 | 
			
		||||
            outMode,
 | 
			
		||||
            renderingIntent,
 | 
			
		||||
            proofProfile,
 | 
			
		||||
            proofRenderingIntent,
 | 
			
		||||
            flags,
 | 
			
		||||
        )
 | 
			
		||||
    except (IOError, TypeError, ValueError) as v:
 | 
			
		||||
        raise PyCMSError(v)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -641,15 +679,16 @@ def createProfile(colorSpace, colorTemp=-1):
 | 
			
		|||
    if colorSpace not in ["LAB", "XYZ", "sRGB"]:
 | 
			
		||||
        raise PyCMSError(
 | 
			
		||||
            "Color space not supported for on-the-fly profile creation (%s)"
 | 
			
		||||
            % colorSpace)
 | 
			
		||||
            % colorSpace
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    if colorSpace == "LAB":
 | 
			
		||||
        try:
 | 
			
		||||
            colorTemp = float(colorTemp)
 | 
			
		||||
        except (TypeError, ValueError):
 | 
			
		||||
            raise PyCMSError(
 | 
			
		||||
                "Color temperature must be numeric, \"%s\" not valid"
 | 
			
		||||
                % colorTemp)
 | 
			
		||||
                'Color temperature must be numeric, "%s" not valid' % colorTemp
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        return core.createProfile(colorSpace, colorTemp)
 | 
			
		||||
| 
						 | 
				
			
			@ -948,7 +987,4 @@ def versions():
 | 
			
		|||
    (pyCMS) Fetches versions.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        VERSION, core.littlecms_version,
 | 
			
		||||
        sys.version.split()[0], Image.__version__
 | 
			
		||||
    )
 | 
			
		||||
    return (VERSION, core.littlecms_version, sys.version.split()[0], Image.__version__)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,29 +41,21 @@ def getrgb(color):
 | 
			
		|||
        return rgb
 | 
			
		||||
 | 
			
		||||
    # check for known string formats
 | 
			
		||||
    if re.match('#[a-f0-9]{3}$', color):
 | 
			
		||||
    if re.match("#[a-f0-9]{3}$", color):
 | 
			
		||||
        return (int(color[1] * 2, 16), int(color[2] * 2, 16), int(color[3] * 2, 16))
 | 
			
		||||
 | 
			
		||||
    if re.match("#[a-f0-9]{4}$", color):
 | 
			
		||||
        return (
 | 
			
		||||
            int(color[1]*2, 16),
 | 
			
		||||
            int(color[2]*2, 16),
 | 
			
		||||
            int(color[3]*2, 16),
 | 
			
		||||
            int(color[1] * 2, 16),
 | 
			
		||||
            int(color[2] * 2, 16),
 | 
			
		||||
            int(color[3] * 2, 16),
 | 
			
		||||
            int(color[4] * 2, 16),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    if re.match('#[a-f0-9]{4}$', color):
 | 
			
		||||
        return (
 | 
			
		||||
            int(color[1]*2, 16),
 | 
			
		||||
            int(color[2]*2, 16),
 | 
			
		||||
            int(color[3]*2, 16),
 | 
			
		||||
            int(color[4]*2, 16),
 | 
			
		||||
            )
 | 
			
		||||
    if re.match("#[a-f0-9]{6}$", color):
 | 
			
		||||
        return (int(color[1:3], 16), int(color[3:5], 16), int(color[5:7], 16))
 | 
			
		||||
 | 
			
		||||
    if re.match('#[a-f0-9]{6}$', color):
 | 
			
		||||
        return (
 | 
			
		||||
            int(color[1:3], 16),
 | 
			
		||||
            int(color[3:5], 16),
 | 
			
		||||
            int(color[5:7], 16),
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    if re.match('#[a-f0-9]{8}$', color):
 | 
			
		||||
    if re.match("#[a-f0-9]{8}$", color):
 | 
			
		||||
        return (
 | 
			
		||||
            int(color[1:3], 16),
 | 
			
		||||
            int(color[3:5], 16),
 | 
			
		||||
| 
						 | 
				
			
			@ -73,26 +65,22 @@ def getrgb(color):
 | 
			
		|||
 | 
			
		||||
    m = re.match(r"rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color)
 | 
			
		||||
    if m:
 | 
			
		||||
        return (
 | 
			
		||||
            int(m.group(1)),
 | 
			
		||||
            int(m.group(2)),
 | 
			
		||||
            int(m.group(3))
 | 
			
		||||
            )
 | 
			
		||||
        return (int(m.group(1)), int(m.group(2)), int(m.group(3)))
 | 
			
		||||
 | 
			
		||||
    m = re.match(r"rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$", color)
 | 
			
		||||
    if m:
 | 
			
		||||
        return (
 | 
			
		||||
            int((int(m.group(1)) * 255) / 100.0 + 0.5),
 | 
			
		||||
            int((int(m.group(2)) * 255) / 100.0 + 0.5),
 | 
			
		||||
            int((int(m.group(3)) * 255) / 100.0 + 0.5)
 | 
			
		||||
            int((int(m.group(3)) * 255) / 100.0 + 0.5),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    m = re.match(
 | 
			
		||||
        r"hsl\(\s*(\d+\.?\d*)\s*,\s*(\d+\.?\d*)%\s*,\s*(\d+\.?\d*)%\s*\)$",
 | 
			
		||||
        color,
 | 
			
		||||
        r"hsl\(\s*(\d+\.?\d*)\s*,\s*(\d+\.?\d*)%\s*,\s*(\d+\.?\d*)%\s*\)$", color
 | 
			
		||||
    )
 | 
			
		||||
    if m:
 | 
			
		||||
        from colorsys import hls_to_rgb
 | 
			
		||||
 | 
			
		||||
        rgb = hls_to_rgb(
 | 
			
		||||
            float(m.group(1)) / 360.0,
 | 
			
		||||
            float(m.group(3)) / 100.0,
 | 
			
		||||
| 
						 | 
				
			
			@ -101,15 +89,15 @@ def getrgb(color):
 | 
			
		|||
        return (
 | 
			
		||||
            int(rgb[0] * 255 + 0.5),
 | 
			
		||||
            int(rgb[1] * 255 + 0.5),
 | 
			
		||||
            int(rgb[2] * 255 + 0.5)
 | 
			
		||||
            int(rgb[2] * 255 + 0.5),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    m = re.match(
 | 
			
		||||
        r"hs[bv]\(\s*(\d+\.?\d*)\s*,\s*(\d+\.?\d*)%\s*,\s*(\d+\.?\d*)%\s*\)$",
 | 
			
		||||
        color,
 | 
			
		||||
        r"hs[bv]\(\s*(\d+\.?\d*)\s*,\s*(\d+\.?\d*)%\s*,\s*(\d+\.?\d*)%\s*\)$", color
 | 
			
		||||
    )
 | 
			
		||||
    if m:
 | 
			
		||||
        from colorsys import hsv_to_rgb
 | 
			
		||||
 | 
			
		||||
        rgb = hsv_to_rgb(
 | 
			
		||||
            float(m.group(1)) / 360.0,
 | 
			
		||||
            float(m.group(2)) / 100.0,
 | 
			
		||||
| 
						 | 
				
			
			@ -118,18 +106,12 @@ def getrgb(color):
 | 
			
		|||
        return (
 | 
			
		||||
            int(rgb[0] * 255 + 0.5),
 | 
			
		||||
            int(rgb[1] * 255 + 0.5),
 | 
			
		||||
            int(rgb[2] * 255 + 0.5)
 | 
			
		||||
            int(rgb[2] * 255 + 0.5),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    m = re.match(r"rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$",
 | 
			
		||||
                 color)
 | 
			
		||||
    m = re.match(r"rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color)
 | 
			
		||||
    if m:
 | 
			
		||||
        return (
 | 
			
		||||
            int(m.group(1)),
 | 
			
		||||
            int(m.group(2)),
 | 
			
		||||
            int(m.group(3)),
 | 
			
		||||
            int(m.group(4))
 | 
			
		||||
            )
 | 
			
		||||
        return (int(m.group(1)), int(m.group(2)), int(m.group(3)), int(m.group(4)))
 | 
			
		||||
    raise ValueError("unknown color specifier: %r" % color)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -151,11 +133,11 @@ def getcolor(color, mode):
 | 
			
		|||
 | 
			
		||||
    if Image.getmodebase(mode) == "L":
 | 
			
		||||
        r, g, b = color
 | 
			
		||||
        color = (r*299 + g*587 + b*114)//1000
 | 
			
		||||
        if mode[-1] == 'A':
 | 
			
		||||
        color = (r * 299 + g * 587 + b * 114) // 1000
 | 
			
		||||
        if mode[-1] == "A":
 | 
			
		||||
            return (color, alpha)
 | 
			
		||||
    else:
 | 
			
		||||
        if mode[-1] == 'A':
 | 
			
		||||
        if mode[-1] == "A":
 | 
			
		||||
            return color + (alpha,)
 | 
			
		||||
    return color
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,7 +45,6 @@ directly.
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
class ImageDraw(object):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, im, mode=None):
 | 
			
		||||
        """
 | 
			
		||||
        Create a drawing instance.
 | 
			
		||||
| 
						 | 
				
			
			@ -95,6 +94,7 @@ class ImageDraw(object):
 | 
			
		|||
        if not self.font:
 | 
			
		||||
            # FIXME: should add a font repository
 | 
			
		||||
            from . import ImageFont
 | 
			
		||||
 | 
			
		||||
            self.font = ImageFont.load_default()
 | 
			
		||||
        return self.font
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -156,13 +156,12 @@ class ImageDraw(object):
 | 
			
		|||
        if ink is not None:
 | 
			
		||||
            self.draw.draw_lines(xy, ink, width)
 | 
			
		||||
            if joint == "curve" and width > 4:
 | 
			
		||||
                for i in range(1, len(xy)-1):
 | 
			
		||||
                for i in range(1, len(xy) - 1):
 | 
			
		||||
                    point = xy[i]
 | 
			
		||||
                    angles = [
 | 
			
		||||
                        math.degrees(math.atan2(
 | 
			
		||||
                            end[0] - start[0], start[1] - end[1]
 | 
			
		||||
                        )) % 360
 | 
			
		||||
                        for start, end in ((xy[i-1], point), (point, xy[i+1]))
 | 
			
		||||
                        math.degrees(math.atan2(end[0] - start[0], start[1] - end[1]))
 | 
			
		||||
                        % 360
 | 
			
		||||
                        for start, end in ((xy[i - 1], point), (point, xy[i + 1]))
 | 
			
		||||
                    ]
 | 
			
		||||
                    if angles[0] == angles[1]:
 | 
			
		||||
                        # This is a straight line, so no joint is required
 | 
			
		||||
| 
						 | 
				
			
			@ -171,21 +170,23 @@ class ImageDraw(object):
 | 
			
		|||
                    def coord_at_angle(coord, angle):
 | 
			
		||||
                        x, y = coord
 | 
			
		||||
                        angle -= 90
 | 
			
		||||
                        distance = width/2 - 1
 | 
			
		||||
                        return tuple([
 | 
			
		||||
                            p +
 | 
			
		||||
                            (math.floor(p_d) if p_d > 0 else math.ceil(p_d))
 | 
			
		||||
                            for p, p_d in
 | 
			
		||||
                            ((x, distance * math.cos(math.radians(angle))),
 | 
			
		||||
                             (y, distance * math.sin(math.radians(angle))))
 | 
			
		||||
                        ])
 | 
			
		||||
                    flipped = ((angles[1] > angles[0] and
 | 
			
		||||
                                angles[1] - 180 > angles[0]) or
 | 
			
		||||
                               (angles[1] < angles[0] and
 | 
			
		||||
                                angles[1] + 180 > angles[0]))
 | 
			
		||||
                        distance = width / 2 - 1
 | 
			
		||||
                        return tuple(
 | 
			
		||||
                            [
 | 
			
		||||
                                p + (math.floor(p_d) if p_d > 0 else math.ceil(p_d))
 | 
			
		||||
                                for p, p_d in (
 | 
			
		||||
                                    (x, distance * math.cos(math.radians(angle))),
 | 
			
		||||
                                    (y, distance * math.sin(math.radians(angle))),
 | 
			
		||||
                                )
 | 
			
		||||
                            ]
 | 
			
		||||
                        )
 | 
			
		||||
 | 
			
		||||
                    flipped = (
 | 
			
		||||
                        angles[1] > angles[0] and angles[1] - 180 > angles[0]
 | 
			
		||||
                    ) or (angles[1] < angles[0] and angles[1] + 180 > angles[0])
 | 
			
		||||
                    coords = [
 | 
			
		||||
                        (point[0] - width/2 + 1, point[1] - width/2 + 1),
 | 
			
		||||
                        (point[0] + width/2 - 1, point[1] + width/2 - 1)
 | 
			
		||||
                        (point[0] - width / 2 + 1, point[1] - width / 2 + 1),
 | 
			
		||||
                        (point[0] + width / 2 - 1, point[1] + width / 2 - 1),
 | 
			
		||||
                    ]
 | 
			
		||||
                    if flipped:
 | 
			
		||||
                        start, end = (angles[1] + 90, angles[0] + 90)
 | 
			
		||||
| 
						 | 
				
			
			@ -197,15 +198,15 @@ class ImageDraw(object):
 | 
			
		|||
                        # Cover potential gaps between the line and the joint
 | 
			
		||||
                        if flipped:
 | 
			
		||||
                            gapCoords = [
 | 
			
		||||
                                coord_at_angle(point, angles[0]+90),
 | 
			
		||||
                                coord_at_angle(point, angles[0] + 90),
 | 
			
		||||
                                point,
 | 
			
		||||
                                coord_at_angle(point, angles[1]+90)
 | 
			
		||||
                                coord_at_angle(point, angles[1] + 90),
 | 
			
		||||
                            ]
 | 
			
		||||
                        else:
 | 
			
		||||
                            gapCoords = [
 | 
			
		||||
                                coord_at_angle(point, angles[0]-90),
 | 
			
		||||
                                coord_at_angle(point, angles[0] - 90),
 | 
			
		||||
                                point,
 | 
			
		||||
                                coord_at_angle(point, angles[1]-90)
 | 
			
		||||
                                coord_at_angle(point, angles[1] - 90),
 | 
			
		||||
                            ]
 | 
			
		||||
                        self.line(gapCoords, fill, width=3)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -259,11 +260,9 @@ class ImageDraw(object):
 | 
			
		|||
 | 
			
		||||
        return text.split(split_character)
 | 
			
		||||
 | 
			
		||||
    def text(self, xy, text, fill=None, font=None, anchor=None,
 | 
			
		||||
             *args, **kwargs):
 | 
			
		||||
    def text(self, xy, text, fill=None, font=None, anchor=None, *args, **kwargs):
 | 
			
		||||
        if self._multiline_check(text):
 | 
			
		||||
            return self.multiline_text(xy, text, fill, font, anchor,
 | 
			
		||||
                                       *args, **kwargs)
 | 
			
		||||
            return self.multiline_text(xy, text, fill, font, anchor, *args, **kwargs)
 | 
			
		||||
        ink, fill = self._getink(fill)
 | 
			
		||||
        if font is None:
 | 
			
		||||
            font = self.getfont()
 | 
			
		||||
| 
						 | 
				
			
			@ -271,8 +270,7 @@ class ImageDraw(object):
 | 
			
		|||
            ink = fill
 | 
			
		||||
        if ink is not None:
 | 
			
		||||
            try:
 | 
			
		||||
                mask, offset = font.getmask2(text, self.fontmode,
 | 
			
		||||
                                             *args, **kwargs)
 | 
			
		||||
                mask, offset = font.getmask2(text, self.fontmode, *args, **kwargs)
 | 
			
		||||
                xy = xy[0] + offset[0], xy[1] + offset[1]
 | 
			
		||||
            except AttributeError:
 | 
			
		||||
                try:
 | 
			
		||||
| 
						 | 
				
			
			@ -281,18 +279,27 @@ class ImageDraw(object):
 | 
			
		|||
                    mask = font.getmask(text)
 | 
			
		||||
            self.draw.draw_bitmap(xy, mask, ink)
 | 
			
		||||
 | 
			
		||||
    def multiline_text(self, xy, text, fill=None, font=None, anchor=None,
 | 
			
		||||
                       spacing=4, align="left", direction=None, features=None,
 | 
			
		||||
                       language=None):
 | 
			
		||||
    def multiline_text(
 | 
			
		||||
        self,
 | 
			
		||||
        xy,
 | 
			
		||||
        text,
 | 
			
		||||
        fill=None,
 | 
			
		||||
        font=None,
 | 
			
		||||
        anchor=None,
 | 
			
		||||
        spacing=4,
 | 
			
		||||
        align="left",
 | 
			
		||||
        direction=None,
 | 
			
		||||
        features=None,
 | 
			
		||||
        language=None,
 | 
			
		||||
    ):
 | 
			
		||||
        widths = []
 | 
			
		||||
        max_width = 0
 | 
			
		||||
        lines = self._multiline_split(text)
 | 
			
		||||
        line_spacing = self.textsize('A', font=font)[1] + spacing
 | 
			
		||||
        line_spacing = self.textsize("A", font=font)[1] + spacing
 | 
			
		||||
        for line in lines:
 | 
			
		||||
            line_width, line_height = self.textsize(line, font,
 | 
			
		||||
                                                    direction=direction,
 | 
			
		||||
                                                    features=features,
 | 
			
		||||
                                                    language=language)
 | 
			
		||||
            line_width, line_height = self.textsize(
 | 
			
		||||
                line, font, direction=direction, features=features, language=language
 | 
			
		||||
            )
 | 
			
		||||
            widths.append(line_width)
 | 
			
		||||
            max_width = max(max_width, line_width)
 | 
			
		||||
        left, top = xy
 | 
			
		||||
| 
						 | 
				
			
			@ -302,36 +309,47 @@ class ImageDraw(object):
 | 
			
		|||
            elif align == "center":
 | 
			
		||||
                left += (max_width - widths[idx]) / 2.0
 | 
			
		||||
            elif align == "right":
 | 
			
		||||
                left += (max_width - widths[idx])
 | 
			
		||||
                left += max_width - widths[idx]
 | 
			
		||||
            else:
 | 
			
		||||
                raise ValueError('align must be "left", "center" or "right"')
 | 
			
		||||
            self.text((left, top), line, fill, font, anchor,
 | 
			
		||||
                      direction=direction, features=features, language=language)
 | 
			
		||||
            self.text(
 | 
			
		||||
                (left, top),
 | 
			
		||||
                line,
 | 
			
		||||
                fill,
 | 
			
		||||
                font,
 | 
			
		||||
                anchor,
 | 
			
		||||
                direction=direction,
 | 
			
		||||
                features=features,
 | 
			
		||||
                language=language,
 | 
			
		||||
            )
 | 
			
		||||
            top += line_spacing
 | 
			
		||||
            left = xy[0]
 | 
			
		||||
 | 
			
		||||
    def textsize(self, text, font=None, spacing=4, direction=None,
 | 
			
		||||
                 features=None, language=None):
 | 
			
		||||
    def textsize(
 | 
			
		||||
        self, text, font=None, spacing=4, direction=None, features=None, language=None
 | 
			
		||||
    ):
 | 
			
		||||
        """Get the size of a given string, in pixels."""
 | 
			
		||||
        if self._multiline_check(text):
 | 
			
		||||
            return self.multiline_textsize(text, font, spacing,
 | 
			
		||||
                                           direction, features, language)
 | 
			
		||||
            return self.multiline_textsize(
 | 
			
		||||
                text, font, spacing, direction, features, language
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        if font is None:
 | 
			
		||||
            font = self.getfont()
 | 
			
		||||
        return font.getsize(text, direction, features, language)
 | 
			
		||||
 | 
			
		||||
    def multiline_textsize(self, text, font=None, spacing=4, direction=None,
 | 
			
		||||
                           features=None, language=None):
 | 
			
		||||
    def multiline_textsize(
 | 
			
		||||
        self, text, font=None, spacing=4, direction=None, features=None, language=None
 | 
			
		||||
    ):
 | 
			
		||||
        max_width = 0
 | 
			
		||||
        lines = self._multiline_split(text)
 | 
			
		||||
        line_spacing = self.textsize('A', font=font)[1] + spacing
 | 
			
		||||
        line_spacing = self.textsize("A", font=font)[1] + spacing
 | 
			
		||||
        for line in lines:
 | 
			
		||||
            line_width, line_height = self.textsize(line, font, spacing,
 | 
			
		||||
                                                    direction, features,
 | 
			
		||||
                                                    language)
 | 
			
		||||
            line_width, line_height = self.textsize(
 | 
			
		||||
                line, font, spacing, direction, features, language
 | 
			
		||||
            )
 | 
			
		||||
            max_width = max(max_width, line_width)
 | 
			
		||||
        return max_width, len(lines)*line_spacing - spacing
 | 
			
		||||
        return max_width, len(lines) * line_spacing - spacing
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def Draw(im, mode=None):
 | 
			
		||||
| 
						 | 
				
			
			@ -417,7 +435,7 @@ def floodfill(image, xy, value, border=None, thresh=0):
 | 
			
		|||
    while edge:
 | 
			
		||||
        new_edge = set()
 | 
			
		||||
        for (x, y) in edge:  # 4 adjacent method
 | 
			
		||||
            for (s, t) in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)):
 | 
			
		||||
            for (s, t) in ((x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)):
 | 
			
		||||
                if (s, t) in full_edge:
 | 
			
		||||
                    continue  # if already processed, skip
 | 
			
		||||
                try:
 | 
			
		||||
| 
						 | 
				
			
			@ -442,6 +460,6 @@ def _color_diff(color1, color2):
 | 
			
		|||
    Uses 1-norm distance to calculate difference between two values.
 | 
			
		||||
    """
 | 
			
		||||
    if isinstance(color2, tuple):
 | 
			
		||||
        return sum([abs(color1[i]-color2[i]) for i in range(0, len(color2))])
 | 
			
		||||
        return sum([abs(color1[i] - color2[i]) for i in range(0, len(color2))])
 | 
			
		||||
    else:
 | 
			
		||||
        return abs(color1-color2)
 | 
			
		||||
        return abs(color1 - color2)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,7 +38,6 @@ class Font(object):
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
class Draw(object):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, image, size=None, color=None):
 | 
			
		||||
        if not hasattr(image, "im"):
 | 
			
		||||
            image = Image.new(image, size, color)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,7 +22,6 @@ from . import Image, ImageFilter, ImageStat
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
class _Enhance(object):
 | 
			
		||||
 | 
			
		||||
    def enhance(self, factor):
 | 
			
		||||
        """
 | 
			
		||||
        Returns an enhanced image.
 | 
			
		||||
| 
						 | 
				
			
			@ -45,14 +44,14 @@ class Color(_Enhance):
 | 
			
		|||
    factor of 0.0 gives a black and white image. A factor of 1.0 gives
 | 
			
		||||
    the original image.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, image):
 | 
			
		||||
        self.image = image
 | 
			
		||||
        self.intermediate_mode = 'L'
 | 
			
		||||
        if 'A' in image.getbands():
 | 
			
		||||
            self.intermediate_mode = 'LA'
 | 
			
		||||
        self.intermediate_mode = "L"
 | 
			
		||||
        if "A" in image.getbands():
 | 
			
		||||
            self.intermediate_mode = "LA"
 | 
			
		||||
 | 
			
		||||
        self.degenerate = image.convert(
 | 
			
		||||
            self.intermediate_mode).convert(image.mode)
 | 
			
		||||
        self.degenerate = image.convert(self.intermediate_mode).convert(image.mode)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Contrast(_Enhance):
 | 
			
		||||
| 
						 | 
				
			
			@ -62,13 +61,14 @@ class Contrast(_Enhance):
 | 
			
		|||
    to the contrast control on a TV set. An enhancement factor of 0.0
 | 
			
		||||
    gives a solid grey image. A factor of 1.0 gives the original image.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, image):
 | 
			
		||||
        self.image = image
 | 
			
		||||
        mean = int(ImageStat.Stat(image.convert("L")).mean[0] + 0.5)
 | 
			
		||||
        self.degenerate = Image.new("L", image.size, mean).convert(image.mode)
 | 
			
		||||
 | 
			
		||||
        if 'A' in image.getbands():
 | 
			
		||||
            self.degenerate.putalpha(image.getchannel('A'))
 | 
			
		||||
        if "A" in image.getbands():
 | 
			
		||||
            self.degenerate.putalpha(image.getchannel("A"))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Brightness(_Enhance):
 | 
			
		||||
| 
						 | 
				
			
			@ -78,12 +78,13 @@ class Brightness(_Enhance):
 | 
			
		|||
    enhancement factor of 0.0 gives a black image. A factor of 1.0 gives the
 | 
			
		||||
    original image.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, image):
 | 
			
		||||
        self.image = image
 | 
			
		||||
        self.degenerate = Image.new(image.mode, image.size, 0)
 | 
			
		||||
 | 
			
		||||
        if 'A' in image.getbands():
 | 
			
		||||
            self.degenerate.putalpha(image.getchannel('A'))
 | 
			
		||||
        if "A" in image.getbands():
 | 
			
		||||
            self.degenerate.putalpha(image.getchannel("A"))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Sharpness(_Enhance):
 | 
			
		||||
| 
						 | 
				
			
			@ -93,9 +94,10 @@ class Sharpness(_Enhance):
 | 
			
		|||
    enhancement factor of 0.0 gives a blurred image, a factor of 1.0 gives the
 | 
			
		||||
    original image, and a factor of 2.0 gives a sharpened image.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, image):
 | 
			
		||||
        self.image = image
 | 
			
		||||
        self.degenerate = image.filter(ImageFilter.SMOOTH)
 | 
			
		||||
 | 
			
		||||
        if 'A' in image.getbands():
 | 
			
		||||
            self.degenerate.putalpha(image.getchannel('A'))
 | 
			
		||||
        if "A" in image.getbands():
 | 
			
		||||
            self.degenerate.putalpha(image.getchannel("A"))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,7 +35,7 @@ import struct
 | 
			
		|||
 | 
			
		||||
MAXBLOCK = 65536
 | 
			
		||||
 | 
			
		||||
SAFEBLOCK = 1024*1024
 | 
			
		||||
SAFEBLOCK = 1024 * 1024
 | 
			
		||||
 | 
			
		||||
LOAD_TRUNCATED_IMAGES = False
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -44,7 +44,7 @@ ERRORS = {
 | 
			
		|||
    -2: "decoding error",
 | 
			
		||||
    -3: "unknown error",
 | 
			
		||||
    -8: "bad configuration",
 | 
			
		||||
    -9: "out of memory error"
 | 
			
		||||
    -9: "out of memory error",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -62,6 +62,7 @@ def raise_ioerror(error):
 | 
			
		|||
# --------------------------------------------------------------------
 | 
			
		||||
# Helpers
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _tilesort(t):
 | 
			
		||||
    # sort on offset
 | 
			
		||||
    return t[2]
 | 
			
		||||
| 
						 | 
				
			
			@ -71,6 +72,7 @@ def _tilesort(t):
 | 
			
		|||
# --------------------------------------------------------------------
 | 
			
		||||
# ImageFile base class
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ImageFile(Image.Image):
 | 
			
		||||
    "Base class for image file format handlers."
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -101,11 +103,13 @@ class ImageFile(Image.Image):
 | 
			
		|||
 | 
			
		||||
        try:
 | 
			
		||||
            self._open()
 | 
			
		||||
        except (IndexError,  # end of data
 | 
			
		||||
        except (
 | 
			
		||||
            IndexError,  # end of data
 | 
			
		||||
            TypeError,  # end of data (ord)
 | 
			
		||||
            KeyError,  # unsupported mode
 | 
			
		||||
            EOFError,  # got header but not the first frame
 | 
			
		||||
                struct.error) as v:
 | 
			
		||||
            struct.error,
 | 
			
		||||
        ) as v:
 | 
			
		||||
            # close the file only if we have opened it this constructor
 | 
			
		||||
            if self._exclusive_fp:
 | 
			
		||||
                self.fp.close()
 | 
			
		||||
| 
						 | 
				
			
			@ -147,7 +151,7 @@ class ImageFile(Image.Image):
 | 
			
		|||
        self.map = None
 | 
			
		||||
        use_mmap = self.filename and len(self.tile) == 1
 | 
			
		||||
        # As of pypy 2.1.0, memory mapping was failing here.
 | 
			
		||||
        use_mmap = use_mmap and not hasattr(sys, 'pypy_version_info')
 | 
			
		||||
        use_mmap = use_mmap and not hasattr(sys, "pypy_version_info")
 | 
			
		||||
 | 
			
		||||
        readonly = 0
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -168,9 +172,12 @@ class ImageFile(Image.Image):
 | 
			
		|||
        if use_mmap:
 | 
			
		||||
            # try memory mapping
 | 
			
		||||
            decoder_name, extents, offset, args = self.tile[0]
 | 
			
		||||
            if decoder_name == "raw" and len(args) >= 3 and \
 | 
			
		||||
               args[0] == self.mode and \
 | 
			
		||||
               args[0] in Image._MAPMODES:
 | 
			
		||||
            if (
 | 
			
		||||
                decoder_name == "raw"
 | 
			
		||||
                and len(args) >= 3
 | 
			
		||||
                and args[0] == self.mode
 | 
			
		||||
                and args[0] in Image._MAPMODES
 | 
			
		||||
            ):
 | 
			
		||||
                try:
 | 
			
		||||
                    if hasattr(Image.core, "map"):
 | 
			
		||||
                        # use built-in mapper  WIN32 only
 | 
			
		||||
| 
						 | 
				
			
			@ -182,12 +189,14 @@ class ImageFile(Image.Image):
 | 
			
		|||
                    else:
 | 
			
		||||
                        # use mmap, if possible
 | 
			
		||||
                        import mmap
 | 
			
		||||
 | 
			
		||||
                        with open(self.filename, "r") as fp:
 | 
			
		||||
                            self.map = mmap.mmap(fp.fileno(), 0,
 | 
			
		||||
                                                 access=mmap.ACCESS_READ)
 | 
			
		||||
                            self.map = mmap.mmap(
 | 
			
		||||
                                fp.fileno(), 0, access=mmap.ACCESS_READ
 | 
			
		||||
                            )
 | 
			
		||||
                        self.im = Image.core.map_buffer(
 | 
			
		||||
                            self.map, self.size, decoder_name, extents,
 | 
			
		||||
                            offset, args)
 | 
			
		||||
                            self.map, self.size, decoder_name, extents, offset, args
 | 
			
		||||
                        )
 | 
			
		||||
                    readonly = 1
 | 
			
		||||
                    # After trashing self.im,
 | 
			
		||||
                    # we might need to reload the palette data.
 | 
			
		||||
| 
						 | 
				
			
			@ -209,8 +218,9 @@ class ImageFile(Image.Image):
 | 
			
		|||
                prefix = b""
 | 
			
		||||
 | 
			
		||||
            for decoder_name, extents, offset, args in self.tile:
 | 
			
		||||
                decoder = Image._getdecoder(self.mode, decoder_name,
 | 
			
		||||
                                            args, self.decoderconfig)
 | 
			
		||||
                decoder = Image._getdecoder(
 | 
			
		||||
                    self.mode, decoder_name, args, self.decoderconfig
 | 
			
		||||
                )
 | 
			
		||||
                try:
 | 
			
		||||
                    seek(offset)
 | 
			
		||||
                    decoder.setimage(self.im, extents)
 | 
			
		||||
| 
						 | 
				
			
			@ -234,9 +244,10 @@ class ImageFile(Image.Image):
 | 
			
		|||
                                    break
 | 
			
		||||
                                else:
 | 
			
		||||
                                    self.tile = []
 | 
			
		||||
                                    raise IOError("image file is truncated "
 | 
			
		||||
                                                  "(%d bytes not processed)" %
 | 
			
		||||
                                                  len(b))
 | 
			
		||||
                                    raise IOError(
 | 
			
		||||
                                        "image file is truncated "
 | 
			
		||||
                                        "(%d bytes not processed)" % len(b)
 | 
			
		||||
                                    )
 | 
			
		||||
 | 
			
		||||
                            b = b + s
 | 
			
		||||
                            n, err_code = decoder.decode(b)
 | 
			
		||||
| 
						 | 
				
			
			@ -264,8 +275,7 @@ class ImageFile(Image.Image):
 | 
			
		|||
 | 
			
		||||
    def load_prepare(self):
 | 
			
		||||
        # create image memory if necessary
 | 
			
		||||
        if not self.im or\
 | 
			
		||||
           self.im.mode != self.mode or self.im.size != self.size:
 | 
			
		||||
        if not self.im or self.im.mode != self.mode or self.im.size != self.size:
 | 
			
		||||
            self.im = Image.core.new(self.mode, self.size)
 | 
			
		||||
        # create palette (optional)
 | 
			
		||||
        if self.mode == "P":
 | 
			
		||||
| 
						 | 
				
			
			@ -284,11 +294,15 @@ class ImageFile(Image.Image):
 | 
			
		|||
    #     pass
 | 
			
		||||
 | 
			
		||||
    def _seek_check(self, frame):
 | 
			
		||||
        if (frame < self._min_frame or
 | 
			
		||||
        if (
 | 
			
		||||
            frame < self._min_frame
 | 
			
		||||
            # Only check upper limit on frames if additional seek operations
 | 
			
		||||
            # are not required to do so
 | 
			
		||||
            (not (hasattr(self, "_n_frames") and self._n_frames is None) and
 | 
			
		||||
             frame >= self.n_frames+self._min_frame)):
 | 
			
		||||
            or (
 | 
			
		||||
                not (hasattr(self, "_n_frames") and self._n_frames is None)
 | 
			
		||||
                and frame >= self.n_frames + self._min_frame
 | 
			
		||||
            )
 | 
			
		||||
        ):
 | 
			
		||||
            raise EOFError("attempt to seek outside sequence")
 | 
			
		||||
 | 
			
		||||
        return self.tell() != frame
 | 
			
		||||
| 
						 | 
				
			
			@ -303,9 +317,7 @@ class StubImageFile(ImageFile):
 | 
			
		|||
    """
 | 
			
		||||
 | 
			
		||||
    def _open(self):
 | 
			
		||||
        raise NotImplementedError(
 | 
			
		||||
            "StubImageFile subclass must implement _open"
 | 
			
		||||
            )
 | 
			
		||||
        raise NotImplementedError("StubImageFile subclass must implement _open")
 | 
			
		||||
 | 
			
		||||
    def load(self):
 | 
			
		||||
        loader = self._load()
 | 
			
		||||
| 
						 | 
				
			
			@ -319,9 +331,7 @@ class StubImageFile(ImageFile):
 | 
			
		|||
 | 
			
		||||
    def _load(self):
 | 
			
		||||
        """(Hook) Find actual image loader."""
 | 
			
		||||
        raise NotImplementedError(
 | 
			
		||||
            "StubImageFile subclass must implement _load"
 | 
			
		||||
            )
 | 
			
		||||
        raise NotImplementedError("StubImageFile subclass must implement _load")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Parser(object):
 | 
			
		||||
| 
						 | 
				
			
			@ -329,6 +339,7 @@ class Parser(object):
 | 
			
		|||
    Incremental image parser.  This class implements the standard
 | 
			
		||||
    feed/close consumer interface.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    incremental = None
 | 
			
		||||
    image = None
 | 
			
		||||
    data = None
 | 
			
		||||
| 
						 | 
				
			
			@ -413,15 +424,13 @@ class Parser(object):
 | 
			
		|||
                    im.load_prepare()
 | 
			
		||||
                    d, e, o, a = im.tile[0]
 | 
			
		||||
                    im.tile = []
 | 
			
		||||
                    self.decoder = Image._getdecoder(
 | 
			
		||||
                        im.mode, d, a, im.decoderconfig
 | 
			
		||||
                        )
 | 
			
		||||
                    self.decoder = Image._getdecoder(im.mode, d, a, im.decoderconfig)
 | 
			
		||||
                    self.decoder.setimage(im.im, e)
 | 
			
		||||
 | 
			
		||||
                    # calculate decoder offset
 | 
			
		||||
                    self.offset = o
 | 
			
		||||
                    if self.offset <= len(self.data):
 | 
			
		||||
                        self.data = self.data[self.offset:]
 | 
			
		||||
                        self.data = self.data[self.offset :]
 | 
			
		||||
                        self.offset = 0
 | 
			
		||||
 | 
			
		||||
                self.image = im
 | 
			
		||||
| 
						 | 
				
			
			@ -463,6 +472,7 @@ class Parser(object):
 | 
			
		|||
 | 
			
		||||
# --------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _save(im, fp, tile, bufsize=0):
 | 
			
		||||
    """Helper to save image based on tile list
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -557,8 +567,7 @@ class PyCodecState(object):
 | 
			
		|||
        self.yoff = 0
 | 
			
		||||
 | 
			
		||||
    def extents(self):
 | 
			
		||||
        return (self.xoff, self.yoff,
 | 
			
		||||
                self.xoff+self.xsize, self.yoff+self.ysize)
 | 
			
		||||
        return (self.xoff, self.yoff, self.xoff + self.xsize, self.yoff + self.ysize)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PyDecoder(object):
 | 
			
		||||
| 
						 | 
				
			
			@ -648,8 +657,10 @@ class PyDecoder(object):
 | 
			
		|||
        if self.state.xsize <= 0 or self.state.ysize <= 0:
 | 
			
		||||
            raise ValueError("Size cannot be negative")
 | 
			
		||||
 | 
			
		||||
        if (self.state.xsize + self.state.xoff > self.im.size[0] or
 | 
			
		||||
           self.state.ysize + self.state.yoff > self.im.size[1]):
 | 
			
		||||
        if (
 | 
			
		||||
            self.state.xsize + self.state.xoff > self.im.size[0]
 | 
			
		||||
            or self.state.ysize + self.state.yoff > self.im.size[1]
 | 
			
		||||
        ):
 | 
			
		||||
            raise ValueError("Tile cannot extend outside image")
 | 
			
		||||
 | 
			
		||||
    def set_as_raw(self, data, rawmode=None):
 | 
			
		||||
| 
						 | 
				
			
			@ -664,7 +675,7 @@ class PyDecoder(object):
 | 
			
		|||
 | 
			
		||||
        if not rawmode:
 | 
			
		||||
            rawmode = self.mode
 | 
			
		||||
        d = Image._getdecoder(self.mode, 'raw', (rawmode))
 | 
			
		||||
        d = Image._getdecoder(self.mode, "raw", (rawmode))
 | 
			
		||||
        d.setimage(self.im, self.state.extents())
 | 
			
		||||
        s = d.decode(data)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -57,12 +57,13 @@ class Kernel(BuiltinFilter):
 | 
			
		|||
    :param offset: Offset. If given, this value is added to the result,
 | 
			
		||||
                    after it has been divided by the scale factor.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    name = "Kernel"
 | 
			
		||||
 | 
			
		||||
    def __init__(self, size, kernel, scale=None, offset=0):
 | 
			
		||||
        if scale is None:
 | 
			
		||||
            # default scale is sum of kernel
 | 
			
		||||
            scale = functools.reduce(lambda a, b: a+b, kernel)
 | 
			
		||||
            scale = functools.reduce(lambda a, b: a + b, kernel)
 | 
			
		||||
        if size[0] * size[1] != len(kernel):
 | 
			
		||||
            raise ValueError("not enough coefficients in kernel")
 | 
			
		||||
        self.filterargs = size, scale, offset, kernel
 | 
			
		||||
| 
						 | 
				
			
			@ -78,6 +79,7 @@ class RankFilter(Filter):
 | 
			
		|||
                 ``size * size / 2`` for a median filter, ``size * size - 1``
 | 
			
		||||
                 for a max filter, etc.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    name = "Rank"
 | 
			
		||||
 | 
			
		||||
    def __init__(self, size, rank):
 | 
			
		||||
| 
						 | 
				
			
			@ -87,7 +89,7 @@ class RankFilter(Filter):
 | 
			
		|||
    def filter(self, image):
 | 
			
		||||
        if image.mode == "P":
 | 
			
		||||
            raise ValueError("cannot filter palette images")
 | 
			
		||||
        image = image.expand(self.size//2, self.size//2)
 | 
			
		||||
        image = image.expand(self.size // 2, self.size // 2)
 | 
			
		||||
        return image.rankfilter(self.size, self.rank)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -98,11 +100,12 @@ class MedianFilter(RankFilter):
 | 
			
		|||
 | 
			
		||||
    :param size: The kernel size, in pixels.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    name = "Median"
 | 
			
		||||
 | 
			
		||||
    def __init__(self, size=3):
 | 
			
		||||
        self.size = size
 | 
			
		||||
        self.rank = size*size//2
 | 
			
		||||
        self.rank = size * size // 2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MinFilter(RankFilter):
 | 
			
		||||
| 
						 | 
				
			
			@ -112,6 +115,7 @@ class MinFilter(RankFilter):
 | 
			
		|||
 | 
			
		||||
    :param size: The kernel size, in pixels.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    name = "Min"
 | 
			
		||||
 | 
			
		||||
    def __init__(self, size=3):
 | 
			
		||||
| 
						 | 
				
			
			@ -126,11 +130,12 @@ class MaxFilter(RankFilter):
 | 
			
		|||
 | 
			
		||||
    :param size: The kernel size, in pixels.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    name = "Max"
 | 
			
		||||
 | 
			
		||||
    def __init__(self, size=3):
 | 
			
		||||
        self.size = size
 | 
			
		||||
        self.rank = size*size-1
 | 
			
		||||
        self.rank = size * size - 1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ModeFilter(Filter):
 | 
			
		||||
| 
						 | 
				
			
			@ -141,6 +146,7 @@ class ModeFilter(Filter):
 | 
			
		|||
 | 
			
		||||
    :param size: The kernel size, in pixels.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    name = "Mode"
 | 
			
		||||
 | 
			
		||||
    def __init__(self, size=3):
 | 
			
		||||
| 
						 | 
				
			
			@ -155,6 +161,7 @@ class GaussianBlur(MultibandFilter):
 | 
			
		|||
 | 
			
		||||
    :param radius: Blur radius.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    name = "GaussianBlur"
 | 
			
		||||
 | 
			
		||||
    def __init__(self, radius=2):
 | 
			
		||||
| 
						 | 
				
			
			@ -175,6 +182,7 @@ class BoxBlur(MultibandFilter):
 | 
			
		|||
                   returns an identical image. Radius 1 takes 1 pixel
 | 
			
		||||
                   in each direction, i.e. 9 pixels in total.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    name = "BoxBlur"
 | 
			
		||||
 | 
			
		||||
    def __init__(self, radius):
 | 
			
		||||
| 
						 | 
				
			
			@ -198,6 +206,7 @@ class UnsharpMask(MultibandFilter):
 | 
			
		|||
    .. _digital unsharp masking: https://en.wikipedia.org/wiki/Unsharp_masking#Digital_unsharp_masking
 | 
			
		||||
 | 
			
		||||
    """  # noqa: E501
 | 
			
		||||
 | 
			
		||||
    name = "UnsharpMask"
 | 
			
		||||
 | 
			
		||||
    def __init__(self, radius=2, percent=150, threshold=3):
 | 
			
		||||
| 
						 | 
				
			
			@ -211,96 +220,116 @@ class UnsharpMask(MultibandFilter):
 | 
			
		|||
 | 
			
		||||
class BLUR(BuiltinFilter):
 | 
			
		||||
    name = "Blur"
 | 
			
		||||
    # fmt: off
 | 
			
		||||
    filterargs = (5, 5), 16, 0, (
 | 
			
		||||
        1, 1, 1, 1, 1,
 | 
			
		||||
        1, 0, 0, 0, 1,
 | 
			
		||||
        1, 0, 0, 0, 1,
 | 
			
		||||
        1, 0, 0, 0, 1,
 | 
			
		||||
        1,  1,  1,  1,  1
 | 
			
		||||
        1, 1, 1, 1, 1,
 | 
			
		||||
    )
 | 
			
		||||
    # fmt: on
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CONTOUR(BuiltinFilter):
 | 
			
		||||
    name = "Contour"
 | 
			
		||||
    # fmt: off
 | 
			
		||||
    filterargs = (3, 3), 1, 255, (
 | 
			
		||||
        -1, -1, -1,
 | 
			
		||||
        -1,  8, -1,
 | 
			
		||||
        -1, -1, -1
 | 
			
		||||
        -1, -1, -1,
 | 
			
		||||
    )
 | 
			
		||||
    # fmt: on
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DETAIL(BuiltinFilter):
 | 
			
		||||
    name = "Detail"
 | 
			
		||||
    # fmt: off
 | 
			
		||||
    filterargs = (3, 3), 6, 0, (
 | 
			
		||||
        0,  -1,  0,
 | 
			
		||||
        -1, 10, -1,
 | 
			
		||||
        0, -1,  0
 | 
			
		||||
        0,  -1,  0,
 | 
			
		||||
    )
 | 
			
		||||
    # fmt: on
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EDGE_ENHANCE(BuiltinFilter):
 | 
			
		||||
    name = "Edge-enhance"
 | 
			
		||||
    # fmt: off
 | 
			
		||||
    filterargs = (3, 3), 2, 0, (
 | 
			
		||||
        -1, -1, -1,
 | 
			
		||||
        -1, 10, -1,
 | 
			
		||||
        -1, -1, -1
 | 
			
		||||
        -1, -1, -1,
 | 
			
		||||
    )
 | 
			
		||||
    # fmt: on
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EDGE_ENHANCE_MORE(BuiltinFilter):
 | 
			
		||||
    name = "Edge-enhance More"
 | 
			
		||||
    # fmt: off
 | 
			
		||||
    filterargs = (3, 3), 1, 0, (
 | 
			
		||||
        -1, -1, -1,
 | 
			
		||||
        -1,  9, -1,
 | 
			
		||||
        -1, -1, -1
 | 
			
		||||
        -1, -1, -1,
 | 
			
		||||
    )
 | 
			
		||||
    # fmt: on
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EMBOSS(BuiltinFilter):
 | 
			
		||||
    name = "Emboss"
 | 
			
		||||
    # fmt: off
 | 
			
		||||
    filterargs = (3, 3), 1, 128, (
 | 
			
		||||
        -1, 0, 0,
 | 
			
		||||
        0,  1, 0,
 | 
			
		||||
        0,  0,  0
 | 
			
		||||
        0,  0, 0,
 | 
			
		||||
    )
 | 
			
		||||
    # fmt: on
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FIND_EDGES(BuiltinFilter):
 | 
			
		||||
    name = "Find Edges"
 | 
			
		||||
    # fmt: off
 | 
			
		||||
    filterargs = (3, 3), 1, 0, (
 | 
			
		||||
        -1, -1, -1,
 | 
			
		||||
        -1,  8, -1,
 | 
			
		||||
        -1, -1, -1
 | 
			
		||||
        -1, -1, -1,
 | 
			
		||||
    )
 | 
			
		||||
    # fmt: on
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SHARPEN(BuiltinFilter):
 | 
			
		||||
    name = "Sharpen"
 | 
			
		||||
    # fmt: off
 | 
			
		||||
    filterargs = (3, 3), 16, 0, (
 | 
			
		||||
        -2, -2, -2,
 | 
			
		||||
        -2, 32, -2,
 | 
			
		||||
        -2, -2, -2
 | 
			
		||||
        -2, -2, -2,
 | 
			
		||||
    )
 | 
			
		||||
    # fmt: on
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SMOOTH(BuiltinFilter):
 | 
			
		||||
    name = "Smooth"
 | 
			
		||||
    # fmt: off
 | 
			
		||||
    filterargs = (3, 3), 13, 0, (
 | 
			
		||||
        1, 1, 1,
 | 
			
		||||
        1, 5, 1,
 | 
			
		||||
        1,  1,  1
 | 
			
		||||
        1, 1, 1,
 | 
			
		||||
    )
 | 
			
		||||
    # fmt: on
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SMOOTH_MORE(BuiltinFilter):
 | 
			
		||||
    name = "Smooth More"
 | 
			
		||||
    # fmt: off
 | 
			
		||||
    filterargs = (5, 5), 100, 0, (
 | 
			
		||||
        1, 1,  1, 1, 1,
 | 
			
		||||
        1, 5,  5, 5, 1,
 | 
			
		||||
        1, 5, 44, 5, 1,
 | 
			
		||||
        1, 5,  5, 5, 1,
 | 
			
		||||
        1,  1,  1,  1,  1
 | 
			
		||||
        1, 1,  1, 1, 1,
 | 
			
		||||
    )
 | 
			
		||||
    # fmt: on
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Color3DLUT(MultibandFilter):
 | 
			
		||||
| 
						 | 
				
			
			@ -327,6 +356,7 @@ class Color3DLUT(MultibandFilter):
 | 
			
		|||
                        than ``channels`` channels. Default is ``None``,
 | 
			
		||||
                        which means that mode wouldn't be changed.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    name = "Color 3D LUT"
 | 
			
		||||
 | 
			
		||||
    def __init__(self, size, table, channels=3, target_mode=None, **kwargs):
 | 
			
		||||
| 
						 | 
				
			
			@ -338,7 +368,7 @@ class Color3DLUT(MultibandFilter):
 | 
			
		|||
 | 
			
		||||
        # Hidden flag `_copy_table=False` could be used to avoid extra copying
 | 
			
		||||
        # of the table if the table is specially made for the constructor.
 | 
			
		||||
        copy_table = kwargs.get('_copy_table', True)
 | 
			
		||||
        copy_table = kwargs.get("_copy_table", True)
 | 
			
		||||
        items = size[0] * size[1] * size[2]
 | 
			
		||||
        wrong_size = False
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -346,8 +376,11 @@ class Color3DLUT(MultibandFilter):
 | 
			
		|||
            if copy_table:
 | 
			
		||||
                table = table.copy()
 | 
			
		||||
 | 
			
		||||
            if table.shape in [(items * channels,), (items, channels),
 | 
			
		||||
                               (size[2], size[1], size[0], channels)]:
 | 
			
		||||
            if table.shape in [
 | 
			
		||||
                (items * channels,),
 | 
			
		||||
                (items, channels),
 | 
			
		||||
                (size[2], size[1], size[0], channels),
 | 
			
		||||
            ]:
 | 
			
		||||
                table = table.reshape(items * channels)
 | 
			
		||||
            else:
 | 
			
		||||
                wrong_size = True
 | 
			
		||||
| 
						 | 
				
			
			@ -363,7 +396,8 @@ class Color3DLUT(MultibandFilter):
 | 
			
		|||
                    if len(pixel) != channels:
 | 
			
		||||
                        raise ValueError(
 | 
			
		||||
                            "The elements of the table should "
 | 
			
		||||
                            "have a length of {}.".format(channels))
 | 
			
		||||
                            "have a length of {}.".format(channels)
 | 
			
		||||
                        )
 | 
			
		||||
                    table.extend(pixel)
 | 
			
		||||
 | 
			
		||||
        if wrong_size or len(table) != items * channels:
 | 
			
		||||
| 
						 | 
				
			
			@ -371,7 +405,9 @@ class Color3DLUT(MultibandFilter):
 | 
			
		|||
                "The table should have either channels * size**3 float items "
 | 
			
		||||
                "or size**3 items of channels-sized tuples with floats. "
 | 
			
		||||
                "Table should be: {}x{}x{}x{}. Actual length: {}".format(
 | 
			
		||||
                    channels, size[0], size[1], size[2], len(table)))
 | 
			
		||||
                    channels, size[0], size[1], size[2], len(table)
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
        self.table = table
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
| 
						 | 
				
			
			@ -379,8 +415,9 @@ class Color3DLUT(MultibandFilter):
 | 
			
		|||
        try:
 | 
			
		||||
            _, _, _ = size
 | 
			
		||||
        except ValueError:
 | 
			
		||||
            raise ValueError("Size should be either an integer or "
 | 
			
		||||
                             "a tuple of three integers.")
 | 
			
		||||
            raise ValueError(
 | 
			
		||||
                "Size should be either an integer or a tuple of three integers."
 | 
			
		||||
            )
 | 
			
		||||
        except TypeError:
 | 
			
		||||
            size = (size, size, size)
 | 
			
		||||
        size = [int(x) for x in size]
 | 
			
		||||
| 
						 | 
				
			
			@ -411,15 +448,20 @@ class Color3DLUT(MultibandFilter):
 | 
			
		|||
        for b in range(size3D):
 | 
			
		||||
            for g in range(size2D):
 | 
			
		||||
                for r in range(size1D):
 | 
			
		||||
                    table[idx_out:idx_out + channels] = callback(
 | 
			
		||||
                        r / (size1D-1), g / (size2D-1), b / (size3D-1))
 | 
			
		||||
                    table[idx_out : idx_out + channels] = callback(
 | 
			
		||||
                        r / (size1D - 1), g / (size2D - 1), b / (size3D - 1)
 | 
			
		||||
                    )
 | 
			
		||||
                    idx_out += channels
 | 
			
		||||
 | 
			
		||||
        return cls((size1D, size2D, size3D), table, channels=channels,
 | 
			
		||||
                   target_mode=target_mode, _copy_table=False)
 | 
			
		||||
        return cls(
 | 
			
		||||
            (size1D, size2D, size3D),
 | 
			
		||||
            table,
 | 
			
		||||
            channels=channels,
 | 
			
		||||
            target_mode=target_mode,
 | 
			
		||||
            _copy_table=False,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def transform(self, callback, with_normals=False, channels=None,
 | 
			
		||||
                  target_mode=None):
 | 
			
		||||
    def transform(self, callback, with_normals=False, channels=None, target_mode=None):
 | 
			
		||||
        """Transforms the table values using provided callback and returns
 | 
			
		||||
        a new LUT with altered values.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -450,24 +492,31 @@ class Color3DLUT(MultibandFilter):
 | 
			
		|||
        for b in range(size3D):
 | 
			
		||||
            for g in range(size2D):
 | 
			
		||||
                for r in range(size1D):
 | 
			
		||||
                    values = self.table[idx_in:idx_in + ch_in]
 | 
			
		||||
                    values = self.table[idx_in : idx_in + ch_in]
 | 
			
		||||
                    if with_normals:
 | 
			
		||||
                        values = callback(r / (size1D-1), g / (size2D-1),
 | 
			
		||||
                                          b / (size3D-1), *values)
 | 
			
		||||
                        values = callback(
 | 
			
		||||
                            r / (size1D - 1),
 | 
			
		||||
                            g / (size2D - 1),
 | 
			
		||||
                            b / (size3D - 1),
 | 
			
		||||
                            *values
 | 
			
		||||
                        )
 | 
			
		||||
                    else:
 | 
			
		||||
                        values = callback(*values)
 | 
			
		||||
                    table[idx_out:idx_out + ch_out] = values
 | 
			
		||||
                    table[idx_out : idx_out + ch_out] = values
 | 
			
		||||
                    idx_in += ch_in
 | 
			
		||||
                    idx_out += ch_out
 | 
			
		||||
 | 
			
		||||
        return type(self)(self.size, table, channels=ch_out,
 | 
			
		||||
        return type(self)(
 | 
			
		||||
            self.size,
 | 
			
		||||
            table,
 | 
			
		||||
            channels=ch_out,
 | 
			
		||||
            target_mode=target_mode or self.mode,
 | 
			
		||||
                          _copy_table=False)
 | 
			
		||||
            _copy_table=False,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        r = [
 | 
			
		||||
            "{} from {}".format(self.__class__.__name__,
 | 
			
		||||
                                self.table.__class__.__name__),
 | 
			
		||||
            "{} from {}".format(self.__class__.__name__, self.table.__class__.__name__),
 | 
			
		||||
            "size={:d}x{:d}x{:d}".format(*self.size),
 | 
			
		||||
            "channels={:d}".format(self.channels),
 | 
			
		||||
        ]
 | 
			
		||||
| 
						 | 
				
			
			@ -479,5 +528,11 @@ class Color3DLUT(MultibandFilter):
 | 
			
		|||
        from . import Image
 | 
			
		||||
 | 
			
		||||
        return image.color_lut_3d(
 | 
			
		||||
            self.mode or image.mode, Image.LINEAR, self.channels,
 | 
			
		||||
            self.size[0], self.size[1], self.size[2], self.table)
 | 
			
		||||
            self.mode or image.mode,
 | 
			
		||||
            Image.LINEAR,
 | 
			
		||||
            self.channels,
 | 
			
		||||
            self.size[0],
 | 
			
		||||
            self.size[1],
 | 
			
		||||
            self.size[2],
 | 
			
		||||
            self.table,
 | 
			
		||||
        )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -98,7 +98,7 @@ class ImageFont(object):
 | 
			
		|||
            self.info.append(s)
 | 
			
		||||
 | 
			
		||||
        # read PILfont metrics
 | 
			
		||||
        data = file.read(256*20)
 | 
			
		||||
        data = file.read(256 * 20)
 | 
			
		||||
 | 
			
		||||
        # check image
 | 
			
		||||
        if image.mode not in ("1", "L"):
 | 
			
		||||
| 
						 | 
				
			
			@ -119,11 +119,11 @@ class ImageFont(object):
 | 
			
		|||
# Wrapper for FreeType fonts.  Application code should use the
 | 
			
		||||
# <b>truetype</b> factory function to create font objects.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FreeTypeFont(object):
 | 
			
		||||
    "FreeType font wrapper (requires _imagingft service)"
 | 
			
		||||
 | 
			
		||||
    def __init__(self, font=None, size=10, index=0, encoding="",
 | 
			
		||||
                 layout_engine=None):
 | 
			
		||||
    def __init__(self, font=None, size=10, index=0, encoding="", layout_engine=None):
 | 
			
		||||
        # FIXME: use service provider instead
 | 
			
		||||
 | 
			
		||||
        self.path = font
 | 
			
		||||
| 
						 | 
				
			
			@ -143,21 +143,23 @@ class FreeTypeFont(object):
 | 
			
		|||
        def load_from_bytes(f):
 | 
			
		||||
            self.font_bytes = f.read()
 | 
			
		||||
            self.font = core.getfont(
 | 
			
		||||
                "", size, index, encoding, self.font_bytes, layout_engine)
 | 
			
		||||
                "", size, index, encoding, self.font_bytes, layout_engine
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        if isPath(font):
 | 
			
		||||
            if sys.platform == "win32":
 | 
			
		||||
                font_bytes_path = font if isinstance(font, bytes) else font.encode()
 | 
			
		||||
                try:
 | 
			
		||||
                    font_bytes_path.decode('ascii')
 | 
			
		||||
                    font_bytes_path.decode("ascii")
 | 
			
		||||
                except UnicodeDecodeError:
 | 
			
		||||
                    # FreeType cannot load fonts with non-ASCII characters on Windows
 | 
			
		||||
                    # So load it into memory first
 | 
			
		||||
                    with open(font, 'rb') as f:
 | 
			
		||||
                    with open(font, "rb") as f:
 | 
			
		||||
                        load_from_bytes(f)
 | 
			
		||||
                    return
 | 
			
		||||
            self.font = core.getfont(font, size, index, encoding,
 | 
			
		||||
                                     layout_engine=layout_engine)
 | 
			
		||||
            self.font = core.getfont(
 | 
			
		||||
                font, size, index, encoding, layout_engine=layout_engine
 | 
			
		||||
            )
 | 
			
		||||
        else:
 | 
			
		||||
            load_from_bytes(font)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -221,8 +223,9 @@ class FreeTypeFont(object):
 | 
			
		|||
        size, offset = self.font.getsize(text, direction, features, language)
 | 
			
		||||
        return (size[0] + offset[0], size[1] + offset[1])
 | 
			
		||||
 | 
			
		||||
    def getsize_multiline(self, text, direction=None, spacing=4,
 | 
			
		||||
                          features=None, language=None):
 | 
			
		||||
    def getsize_multiline(
 | 
			
		||||
        self, text, direction=None, spacing=4, features=None, language=None
 | 
			
		||||
    ):
 | 
			
		||||
        """
 | 
			
		||||
        Returns width and height (in pixels) of given text if rendered in font
 | 
			
		||||
        with provided direction, features, and language, while respecting
 | 
			
		||||
| 
						 | 
				
			
			@ -261,12 +264,12 @@ class FreeTypeFont(object):
 | 
			
		|||
        """
 | 
			
		||||
        max_width = 0
 | 
			
		||||
        lines = self._multiline_split(text)
 | 
			
		||||
        line_spacing = self.getsize('A')[1] + spacing
 | 
			
		||||
        line_spacing = self.getsize("A")[1] + spacing
 | 
			
		||||
        for line in lines:
 | 
			
		||||
            line_width, line_height = self.getsize(line, direction, features, language)
 | 
			
		||||
            max_width = max(max_width, line_width)
 | 
			
		||||
 | 
			
		||||
        return max_width, len(lines)*line_spacing - spacing
 | 
			
		||||
        return max_width, len(lines) * line_spacing - spacing
 | 
			
		||||
 | 
			
		||||
    def getoffset(self, text):
 | 
			
		||||
        """
 | 
			
		||||
| 
						 | 
				
			
			@ -327,11 +330,21 @@ class FreeTypeFont(object):
 | 
			
		|||
        :return: An internal PIL storage memory instance as defined by the
 | 
			
		||||
                 :py:mod:`PIL.Image.core` interface module.
 | 
			
		||||
        """
 | 
			
		||||
        return self.getmask2(text, mode, direction=direction, features=features,
 | 
			
		||||
                             language=language)[0]
 | 
			
		||||
        return self.getmask2(
 | 
			
		||||
            text, mode, direction=direction, features=features, language=language
 | 
			
		||||
        )[0]
 | 
			
		||||
 | 
			
		||||
    def getmask2(self, text, mode="", fill=Image.core.fill, direction=None,
 | 
			
		||||
                 features=None, language=None, *args, **kwargs):
 | 
			
		||||
    def getmask2(
 | 
			
		||||
        self,
 | 
			
		||||
        text,
 | 
			
		||||
        mode="",
 | 
			
		||||
        fill=Image.core.fill,
 | 
			
		||||
        direction=None,
 | 
			
		||||
        features=None,
 | 
			
		||||
        language=None,
 | 
			
		||||
        *args,
 | 
			
		||||
        **kwargs
 | 
			
		||||
    ):
 | 
			
		||||
        """
 | 
			
		||||
        Create a bitmap for the text.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -384,8 +397,9 @@ class FreeTypeFont(object):
 | 
			
		|||
        self.font.render(text, im.id, mode == "1", direction, features, language)
 | 
			
		||||
        return im, offset
 | 
			
		||||
 | 
			
		||||
    def font_variant(self, font=None, size=None, index=None, encoding=None,
 | 
			
		||||
                     layout_engine=None):
 | 
			
		||||
    def font_variant(
 | 
			
		||||
        self, font=None, size=None, index=None, encoding=None, layout_engine=None
 | 
			
		||||
    ):
 | 
			
		||||
        """
 | 
			
		||||
        Create a copy of this FreeTypeFont object,
 | 
			
		||||
        using any specified arguments to override the settings.
 | 
			
		||||
| 
						 | 
				
			
			@ -400,7 +414,7 @@ class FreeTypeFont(object):
 | 
			
		|||
            size=self.size if size is None else size,
 | 
			
		||||
            index=self.index if index is None else index,
 | 
			
		||||
            encoding=self.encoding if encoding is None else encoding,
 | 
			
		||||
            layout_engine=layout_engine or self.layout_engine
 | 
			
		||||
            layout_engine=layout_engine or self.layout_engine,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -447,8 +461,7 @@ def load(filename):
 | 
			
		|||
    return f
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def truetype(font=None, size=10, index=0, encoding="",
 | 
			
		||||
             layout_engine=None):
 | 
			
		||||
def truetype(font=None, size=10, index=0, encoding="", layout_engine=None):
 | 
			
		||||
    """
 | 
			
		||||
    Load a TrueType or OpenType font from a file or file-like object,
 | 
			
		||||
    and create a font object.
 | 
			
		||||
| 
						 | 
				
			
			@ -472,8 +485,10 @@ def truetype(font=None, size=10, index=0, encoding="",
 | 
			
		|||
    :return: A font object.
 | 
			
		||||
    :exception IOError: If the file could not be read.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def freetype(font):
 | 
			
		||||
        return FreeTypeFont(font, size, index, encoding, layout_engine)
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        return freetype(font)
 | 
			
		||||
    except IOError:
 | 
			
		||||
| 
						 | 
				
			
			@ -487,17 +502,19 @@ def truetype(font=None, size=10, index=0, encoding="",
 | 
			
		|||
            windir = os.environ.get("WINDIR")
 | 
			
		||||
            if windir:
 | 
			
		||||
                dirs.append(os.path.join(windir, "fonts"))
 | 
			
		||||
        elif sys.platform in ('linux', 'linux2'):
 | 
			
		||||
        elif sys.platform in ("linux", "linux2"):
 | 
			
		||||
            lindirs = os.environ.get("XDG_DATA_DIRS", "")
 | 
			
		||||
            if not lindirs:
 | 
			
		||||
                # According to the freedesktop spec, XDG_DATA_DIRS should
 | 
			
		||||
                # default to /usr/share
 | 
			
		||||
                lindirs = '/usr/share'
 | 
			
		||||
            dirs += [os.path.join(lindir, "fonts")
 | 
			
		||||
                     for lindir in lindirs.split(":")]
 | 
			
		||||
        elif sys.platform == 'darwin':
 | 
			
		||||
            dirs += ['/Library/Fonts', '/System/Library/Fonts',
 | 
			
		||||
                     os.path.expanduser('~/Library/Fonts')]
 | 
			
		||||
                lindirs = "/usr/share"
 | 
			
		||||
            dirs += [os.path.join(lindir, "fonts") for lindir in lindirs.split(":")]
 | 
			
		||||
        elif sys.platform == "darwin":
 | 
			
		||||
            dirs += [
 | 
			
		||||
                "/Library/Fonts",
 | 
			
		||||
                "/System/Library/Fonts",
 | 
			
		||||
                os.path.expanduser("~/Library/Fonts"),
 | 
			
		||||
            ]
 | 
			
		||||
 | 
			
		||||
        ext = os.path.splitext(ttf_filename)[1]
 | 
			
		||||
        first_font_with_a_different_extension = None
 | 
			
		||||
| 
						 | 
				
			
			@ -506,13 +523,11 @@ def truetype(font=None, size=10, index=0, encoding="",
 | 
			
		|||
                for walkfilename in walkfilenames:
 | 
			
		||||
                    if ext and walkfilename == ttf_filename:
 | 
			
		||||
                        return freetype(os.path.join(walkroot, walkfilename))
 | 
			
		||||
                    elif (not ext and
 | 
			
		||||
                          os.path.splitext(walkfilename)[0] == ttf_filename):
 | 
			
		||||
                    elif not ext and os.path.splitext(walkfilename)[0] == ttf_filename:
 | 
			
		||||
                        fontpath = os.path.join(walkroot, walkfilename)
 | 
			
		||||
                        if os.path.splitext(fontpath)[1] == '.ttf':
 | 
			
		||||
                        if os.path.splitext(fontpath)[1] == ".ttf":
 | 
			
		||||
                            return freetype(fontpath)
 | 
			
		||||
                        if not ext \
 | 
			
		||||
                           and first_font_with_a_different_extension is None:
 | 
			
		||||
                        if not ext and first_font_with_a_different_extension is None:
 | 
			
		||||
                            first_font_with_a_different_extension = fontpath
 | 
			
		||||
        if first_font_with_a_different_extension:
 | 
			
		||||
            return freetype(first_font_with_a_different_extension)
 | 
			
		||||
| 
						 | 
				
			
			@ -551,10 +566,13 @@ def load_default():
 | 
			
		|||
    """
 | 
			
		||||
    from io import BytesIO
 | 
			
		||||
    import base64
 | 
			
		||||
 | 
			
		||||
    f = ImageFont()
 | 
			
		||||
    f._load_pilfont_data(
 | 
			
		||||
        # courB08
 | 
			
		||||
        BytesIO(base64.b64decode(b'''
 | 
			
		||||
        BytesIO(
 | 
			
		||||
            base64.b64decode(
 | 
			
		||||
                b"""
 | 
			
		||||
UElMZm9udAo7Ozs7OzsxMDsKREFUQQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
 | 
			
		||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
 | 
			
		||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
 | 
			
		||||
| 
						 | 
				
			
			@ -646,7 +664,13 @@ AJsAEQAGAAAAAP/6AAX//wCbAAoAoAAPAAYAAAAA//oABQABAKAACgClABEABgAA////+AAGAAAA
 | 
			
		|||
pQAKAKwAEgAGAAD////4AAYAAACsAAoAswASAAYAAP////gABgAAALMACgC6ABIABgAA////+QAG
 | 
			
		||||
AAAAugAKAMEAEQAGAAD////4AAYAAgDBAAoAyAAUAAYAAP////kABQACAMgACgDOABMABgAA////
 | 
			
		||||
+QAGAAIAzgAKANUAEw==
 | 
			
		||||
''')), Image.open(BytesIO(base64.b64decode(b'''
 | 
			
		||||
"""
 | 
			
		||||
            )
 | 
			
		||||
        ),
 | 
			
		||||
        Image.open(
 | 
			
		||||
            BytesIO(
 | 
			
		||||
                base64.b64decode(
 | 
			
		||||
                    b"""
 | 
			
		||||
iVBORw0KGgoAAAANSUhEUgAAAx4AAAAUAQAAAAArMtZoAAAEwElEQVR4nABlAJr/AHVE4czCI/4u
 | 
			
		||||
Mc4b7vuds/xzjz5/3/7u/n9vMe7vnfH/9++vPn/xyf5zhxzjt8GHw8+2d83u8x27199/nxuQ6Od9
 | 
			
		||||
M43/5z2I+9n9ZtmDBwMQECDRQw/eQIQohJXxpBCNVE6QCCAAAAD//wBlAJr/AgALyj1t/wINwq0g
 | 
			
		||||
| 
						 | 
				
			
			@ -670,5 +694,9 @@ evta/58PTEWzr21hufPjA8N+qlnBwAAAAAD//2JiWLci5v1+HmFXDqcnULE/MxgYGBj+f6CaJQAA
 | 
			
		|||
AAD//2Ji2FrkY3iYpYC5qDeGgeEMAwPDvwQBBoYvcTwOVLMEAAAA//9isDBgkP///0EOg9z35v//
 | 
			
		||||
Gc/eeW7BwPj5+QGZhANUswMAAAD//2JgqGBgYGBgqEMXlvhMPUsAAAAA//8iYDd1AAAAAP//AwDR
 | 
			
		||||
w7IkEbzhVQAAAABJRU5ErkJggg==
 | 
			
		||||
'''))))
 | 
			
		||||
"""
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
        ),
 | 
			
		||||
    )
 | 
			
		||||
    return f
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,6 +18,7 @@
 | 
			
		|||
from . import Image
 | 
			
		||||
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
if sys.platform == "win32":
 | 
			
		||||
    grabber = Image.core.grabscreen
 | 
			
		||||
elif sys.platform == "darwin":
 | 
			
		||||
| 
						 | 
				
			
			@ -30,18 +31,23 @@ else:
 | 
			
		|||
 | 
			
		||||
def grab(bbox=None, include_layered_windows=False):
 | 
			
		||||
    if sys.platform == "darwin":
 | 
			
		||||
        fh, filepath = tempfile.mkstemp('.png')
 | 
			
		||||
        fh, filepath = tempfile.mkstemp(".png")
 | 
			
		||||
        os.close(fh)
 | 
			
		||||
        subprocess.call(['screencapture', '-x', filepath])
 | 
			
		||||
        subprocess.call(["screencapture", "-x", filepath])
 | 
			
		||||
        im = Image.open(filepath)
 | 
			
		||||
        im.load()
 | 
			
		||||
        os.unlink(filepath)
 | 
			
		||||
    else:
 | 
			
		||||
        size, data = grabber(include_layered_windows)
 | 
			
		||||
        im = Image.frombytes(
 | 
			
		||||
            "RGB", size, data,
 | 
			
		||||
            "RGB",
 | 
			
		||||
            size,
 | 
			
		||||
            data,
 | 
			
		||||
            # RGB, 32-bit line padding, origin lower left corner
 | 
			
		||||
            "raw", "BGR", (size[0]*3 + 3) & -4, -1
 | 
			
		||||
            "raw",
 | 
			
		||||
            "BGR",
 | 
			
		||||
            (size[0] * 3 + 3) & -4,
 | 
			
		||||
            -1,
 | 
			
		||||
        )
 | 
			
		||||
    if bbox:
 | 
			
		||||
        im = im.crop(bbox)
 | 
			
		||||
| 
						 | 
				
			
			@ -50,15 +56,16 @@ def grab(bbox=None, include_layered_windows=False):
 | 
			
		|||
 | 
			
		||||
def grabclipboard():
 | 
			
		||||
    if sys.platform == "darwin":
 | 
			
		||||
        fh, filepath = tempfile.mkstemp('.jpg')
 | 
			
		||||
        fh, filepath = tempfile.mkstemp(".jpg")
 | 
			
		||||
        os.close(fh)
 | 
			
		||||
        commands = [
 | 
			
		||||
            "set theFile to (open for access POSIX file \""
 | 
			
		||||
            + filepath + "\" with write permission)",
 | 
			
		||||
            'set theFile to (open for access POSIX file "'
 | 
			
		||||
            + filepath
 | 
			
		||||
            + '" with write permission)',
 | 
			
		||||
            "try",
 | 
			
		||||
            "    write (the clipboard as JPEG picture) to theFile",
 | 
			
		||||
            "end try",
 | 
			
		||||
            "close access theFile"
 | 
			
		||||
            "close access theFile",
 | 
			
		||||
        ]
 | 
			
		||||
        script = ["osascript"]
 | 
			
		||||
        for command in commands:
 | 
			
		||||
| 
						 | 
				
			
			@ -76,5 +83,6 @@ def grabclipboard():
 | 
			
		|||
        if isinstance(data, bytes):
 | 
			
		||||
            from . import BmpImagePlugin
 | 
			
		||||
            import io
 | 
			
		||||
 | 
			
		||||
            return BmpImagePlugin.DibImageFile(io.BytesIO(data))
 | 
			
		||||
        return data
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,6 +22,7 @@ try:
 | 
			
		|||
    import builtins
 | 
			
		||||
except ImportError:
 | 
			
		||||
    import __builtin__
 | 
			
		||||
 | 
			
		||||
    builtins = __builtin__
 | 
			
		||||
 | 
			
		||||
VERBOSE = 0
 | 
			
		||||
| 
						 | 
				
			
			@ -61,7 +62,7 @@ class _Operand(object):
 | 
			
		|||
            out = Image.new(mode or im1.mode, im1.size, None)
 | 
			
		||||
            im1.load()
 | 
			
		||||
            try:
 | 
			
		||||
                op = getattr(_imagingmath, op+"_"+im1.mode)
 | 
			
		||||
                op = getattr(_imagingmath, op + "_" + im1.mode)
 | 
			
		||||
            except AttributeError:
 | 
			
		||||
                raise TypeError("bad operand type for '%s'" % op)
 | 
			
		||||
            _imagingmath.unop(op, out.im.id, im1.im.id)
 | 
			
		||||
| 
						 | 
				
			
			@ -78,8 +79,7 @@ class _Operand(object):
 | 
			
		|||
                    raise ValueError("mode mismatch")
 | 
			
		||||
            if im1.size != im2.size:
 | 
			
		||||
                # crop both arguments to a common size
 | 
			
		||||
                size = (min(im1.size[0], im2.size[0]),
 | 
			
		||||
                        min(im1.size[1], im2.size[1]))
 | 
			
		||||
                size = (min(im1.size[0], im2.size[0]), min(im1.size[1], im2.size[1]))
 | 
			
		||||
                if im1.size != size:
 | 
			
		||||
                    im1 = im1.crop((0, 0) + size)
 | 
			
		||||
                if im2.size != size:
 | 
			
		||||
| 
						 | 
				
			
			@ -90,7 +90,7 @@ class _Operand(object):
 | 
			
		|||
            im1.load()
 | 
			
		||||
            im2.load()
 | 
			
		||||
            try:
 | 
			
		||||
                op = getattr(_imagingmath, op+"_"+im1.mode)
 | 
			
		||||
                op = getattr(_imagingmath, op + "_" + im1.mode)
 | 
			
		||||
            except AttributeError:
 | 
			
		||||
                raise TypeError("bad operand type for '%s'" % op)
 | 
			
		||||
            _imagingmath.binop(op, out.im.id, im1.im.id, im2.im.id)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,13 +37,13 @@ def getmode(mode):
 | 
			
		|||
        # initialize mode cache
 | 
			
		||||
 | 
			
		||||
        from . import Image
 | 
			
		||||
 | 
			
		||||
        modes = {}
 | 
			
		||||
        # core modes
 | 
			
		||||
        for m, (basemode, basetype, bands) in Image._MODEINFO.items():
 | 
			
		||||
            modes[m] = ModeDescriptor(m, bands, basemode, basetype)
 | 
			
		||||
        # extra experimental modes
 | 
			
		||||
        modes["RGBa"] = ModeDescriptor("RGBa",
 | 
			
		||||
                                       ("R", "G", "B", "a"), "RGB", "L")
 | 
			
		||||
        modes["RGBa"] = ModeDescriptor("RGBa", ("R", "G", "B", "a"), "RGB", "L")
 | 
			
		||||
        modes["LA"] = ModeDescriptor("LA", ("L", "A"), "L", "L")
 | 
			
		||||
        modes["La"] = ModeDescriptor("La", ("L", "a"), "L", "L")
 | 
			
		||||
        modes["PA"] = ModeDescriptor("PA", ("P", "A"), "RGB", "L")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,6 +12,19 @@ import re
 | 
			
		|||
 | 
			
		||||
LUT_SIZE = 1 << 9
 | 
			
		||||
 | 
			
		||||
# fmt: off
 | 
			
		||||
ROTATION_MATRIX = [
 | 
			
		||||
    6, 3, 0,
 | 
			
		||||
    7, 4, 1,
 | 
			
		||||
    8, 5, 2,
 | 
			
		||||
]
 | 
			
		||||
MIRROR_MATRIX = [
 | 
			
		||||
    2, 1, 0,
 | 
			
		||||
    5, 4, 3,
 | 
			
		||||
    8, 7, 6,
 | 
			
		||||
]
 | 
			
		||||
# fmt: on
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LutBuilder(object):
 | 
			
		||||
    """A class for building a MorphLut from a descriptive language
 | 
			
		||||
| 
						 | 
				
			
			@ -48,6 +61,7 @@ class LutBuilder(object):
 | 
			
		|||
          lut = lb.build_lut()
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, patterns=None, op_name=None):
 | 
			
		||||
        if patterns is not None:
 | 
			
		||||
            self.patterns = patterns
 | 
			
		||||
| 
						 | 
				
			
			@ -56,20 +70,19 @@ class LutBuilder(object):
 | 
			
		|||
        self.lut = None
 | 
			
		||||
        if op_name is not None:
 | 
			
		||||
            known_patterns = {
 | 
			
		||||
                'corner': ['1:(... ... ...)->0',
 | 
			
		||||
                           '4:(00. 01. ...)->1'],
 | 
			
		||||
                'dilation4': ['4:(... .0. .1.)->1'],
 | 
			
		||||
                'dilation8': ['4:(... .0. .1.)->1',
 | 
			
		||||
                              '4:(... .0. ..1)->1'],
 | 
			
		||||
                'erosion4': ['4:(... .1. .0.)->0'],
 | 
			
		||||
                'erosion8': ['4:(... .1. .0.)->0',
 | 
			
		||||
                             '4:(... .1. ..0)->0'],
 | 
			
		||||
                'edge': ['1:(... ... ...)->0',
 | 
			
		||||
                         '4:(.0. .1. ...)->1',
 | 
			
		||||
                         '4:(01. .1. ...)->1']
 | 
			
		||||
                "corner": ["1:(... ... ...)->0", "4:(00. 01. ...)->1"],
 | 
			
		||||
                "dilation4": ["4:(... .0. .1.)->1"],
 | 
			
		||||
                "dilation8": ["4:(... .0. .1.)->1", "4:(... .0. ..1)->1"],
 | 
			
		||||
                "erosion4": ["4:(... .1. .0.)->0"],
 | 
			
		||||
                "erosion8": ["4:(... .1. .0.)->0", "4:(... .1. ..0)->0"],
 | 
			
		||||
                "edge": [
 | 
			
		||||
                    "1:(... ... ...)->0",
 | 
			
		||||
                    "4:(.0. .1. ...)->1",
 | 
			
		||||
                    "4:(01. .1. ...)->1",
 | 
			
		||||
                ],
 | 
			
		||||
            }
 | 
			
		||||
            if op_name not in known_patterns:
 | 
			
		||||
                raise Exception('Unknown pattern '+op_name+'!')
 | 
			
		||||
                raise Exception("Unknown pattern " + op_name + "!")
 | 
			
		||||
 | 
			
		||||
            self.patterns = known_patterns[op_name]
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -88,8 +101,8 @@ class LutBuilder(object):
 | 
			
		|||
        """string_permute takes a pattern and a permutation and returns the
 | 
			
		||||
        string permuted according to the permutation list.
 | 
			
		||||
        """
 | 
			
		||||
        assert(len(permutation) == 9)
 | 
			
		||||
        return ''.join(pattern[p] for p in permutation)
 | 
			
		||||
        assert len(permutation) == 9
 | 
			
		||||
        return "".join(pattern[p] for p in permutation)
 | 
			
		||||
 | 
			
		||||
    def _pattern_permute(self, basic_pattern, options, basic_result):
 | 
			
		||||
        """pattern_permute takes a basic pattern and its result and clones
 | 
			
		||||
| 
						 | 
				
			
			@ -98,32 +111,25 @@ class LutBuilder(object):
 | 
			
		|||
        patterns = [(basic_pattern, basic_result)]
 | 
			
		||||
 | 
			
		||||
        # rotations
 | 
			
		||||
        if '4' in options:
 | 
			
		||||
        if "4" in options:
 | 
			
		||||
            res = patterns[-1][1]
 | 
			
		||||
            for i in range(4):
 | 
			
		||||
                patterns.append(
 | 
			
		||||
                    (self._string_permute(patterns[-1][0], [6, 3, 0,
 | 
			
		||||
                                                            7, 4, 1,
 | 
			
		||||
                                                            8, 5, 2]), res))
 | 
			
		||||
                    (self._string_permute(patterns[-1][0], ROTATION_MATRIX), res)
 | 
			
		||||
                )
 | 
			
		||||
        # mirror
 | 
			
		||||
        if 'M' in options:
 | 
			
		||||
        if "M" in options:
 | 
			
		||||
            n = len(patterns)
 | 
			
		||||
            for pattern, res in patterns[0:n]:
 | 
			
		||||
                patterns.append(
 | 
			
		||||
                    (self._string_permute(pattern, [2, 1, 0,
 | 
			
		||||
                                                    5, 4, 3,
 | 
			
		||||
                                                    8, 7, 6]), res))
 | 
			
		||||
                patterns.append((self._string_permute(pattern, MIRROR_MATRIX), res))
 | 
			
		||||
 | 
			
		||||
        # negate
 | 
			
		||||
        if 'N' in options:
 | 
			
		||||
        if "N" in options:
 | 
			
		||||
            n = len(patterns)
 | 
			
		||||
            for pattern, res in patterns[0:n]:
 | 
			
		||||
                # Swap 0 and 1
 | 
			
		||||
                pattern = (pattern
 | 
			
		||||
                           .replace('0', 'Z')
 | 
			
		||||
                           .replace('1', '0')
 | 
			
		||||
                           .replace('Z', '1'))
 | 
			
		||||
                res = 1-int(res)
 | 
			
		||||
                pattern = pattern.replace("0", "Z").replace("1", "0").replace("Z", "1")
 | 
			
		||||
                res = 1 - int(res)
 | 
			
		||||
                patterns.append((pattern, res))
 | 
			
		||||
 | 
			
		||||
        return patterns
 | 
			
		||||
| 
						 | 
				
			
			@ -138,22 +144,21 @@ class LutBuilder(object):
 | 
			
		|||
 | 
			
		||||
        # Parse and create symmetries of the patterns strings
 | 
			
		||||
        for p in self.patterns:
 | 
			
		||||
            m = re.search(
 | 
			
		||||
                r'(\w*):?\s*\((.+?)\)\s*->\s*(\d)', p.replace('\n', ''))
 | 
			
		||||
            m = re.search(r"(\w*):?\s*\((.+?)\)\s*->\s*(\d)", p.replace("\n", ""))
 | 
			
		||||
            if not m:
 | 
			
		||||
                raise Exception('Syntax error in pattern "'+p+'"')
 | 
			
		||||
                raise Exception('Syntax error in pattern "' + p + '"')
 | 
			
		||||
            options = m.group(1)
 | 
			
		||||
            pattern = m.group(2)
 | 
			
		||||
            result = int(m.group(3))
 | 
			
		||||
 | 
			
		||||
            # Get rid of spaces
 | 
			
		||||
            pattern = pattern.replace(' ', '').replace('\n', '')
 | 
			
		||||
            pattern = pattern.replace(" ", "").replace("\n", "")
 | 
			
		||||
 | 
			
		||||
            patterns += self._pattern_permute(pattern, options, result)
 | 
			
		||||
 | 
			
		||||
        # compile the patterns into regular expressions for speed
 | 
			
		||||
        for i, pattern in enumerate(patterns):
 | 
			
		||||
            p = pattern[0].replace('.', 'X').replace('X', '[01]')
 | 
			
		||||
            p = pattern[0].replace(".", "X").replace("X", "[01]")
 | 
			
		||||
            p = re.compile(p)
 | 
			
		||||
            patterns[i] = (p, pattern[1])
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -163,7 +168,7 @@ class LutBuilder(object):
 | 
			
		|||
        for i in range(LUT_SIZE):
 | 
			
		||||
            # Build the bit pattern
 | 
			
		||||
            bitpattern = bin(i)[2:]
 | 
			
		||||
            bitpattern = ('0'*(9-len(bitpattern)) + bitpattern)[::-1]
 | 
			
		||||
            bitpattern = ("0" * (9 - len(bitpattern)) + bitpattern)[::-1]
 | 
			
		||||
 | 
			
		||||
            for p, r in patterns:
 | 
			
		||||
                if p.match(bitpattern):
 | 
			
		||||
| 
						 | 
				
			
			@ -175,10 +180,7 @@ class LutBuilder(object):
 | 
			
		|||
class MorphOp(object):
 | 
			
		||||
    """A class for binary morphological operators"""
 | 
			
		||||
 | 
			
		||||
    def __init__(self,
 | 
			
		||||
                 lut=None,
 | 
			
		||||
                 op_name=None,
 | 
			
		||||
                 patterns=None):
 | 
			
		||||
    def __init__(self, lut=None, op_name=None, patterns=None):
 | 
			
		||||
        """Create a binary morphological operator"""
 | 
			
		||||
        self.lut = lut
 | 
			
		||||
        if op_name is not None:
 | 
			
		||||
| 
						 | 
				
			
			@ -192,13 +194,12 @@ class MorphOp(object):
 | 
			
		|||
        Returns a tuple of the number of changed pixels and the
 | 
			
		||||
        morphed image"""
 | 
			
		||||
        if self.lut is None:
 | 
			
		||||
            raise Exception('No operator loaded')
 | 
			
		||||
            raise Exception("No operator loaded")
 | 
			
		||||
 | 
			
		||||
        if image.mode != 'L':
 | 
			
		||||
            raise Exception('Image must be binary, meaning it must use mode L')
 | 
			
		||||
        if image.mode != "L":
 | 
			
		||||
            raise Exception("Image must be binary, meaning it must use mode L")
 | 
			
		||||
        outimage = Image.new(image.mode, image.size, None)
 | 
			
		||||
        count = _imagingmorph.apply(
 | 
			
		||||
            bytes(self.lut), image.im.id, outimage.im.id)
 | 
			
		||||
        count = _imagingmorph.apply(bytes(self.lut), image.im.id, outimage.im.id)
 | 
			
		||||
        return count, outimage
 | 
			
		||||
 | 
			
		||||
    def match(self, image):
 | 
			
		||||
| 
						 | 
				
			
			@ -208,10 +209,10 @@ class MorphOp(object):
 | 
			
		|||
        Returns a list of tuples of (x,y) coordinates
 | 
			
		||||
        of all matching pixels. See :ref:`coordinate-system`."""
 | 
			
		||||
        if self.lut is None:
 | 
			
		||||
            raise Exception('No operator loaded')
 | 
			
		||||
            raise Exception("No operator loaded")
 | 
			
		||||
 | 
			
		||||
        if image.mode != 'L':
 | 
			
		||||
            raise Exception('Image must be binary, meaning it must use mode L')
 | 
			
		||||
        if image.mode != "L":
 | 
			
		||||
            raise Exception("Image must be binary, meaning it must use mode L")
 | 
			
		||||
        return _imagingmorph.match(bytes(self.lut), image.im.id)
 | 
			
		||||
 | 
			
		||||
    def get_on_pixels(self, image):
 | 
			
		||||
| 
						 | 
				
			
			@ -220,24 +221,24 @@ class MorphOp(object):
 | 
			
		|||
        Returns a list of tuples of (x,y) coordinates
 | 
			
		||||
        of all matching pixels. See :ref:`coordinate-system`."""
 | 
			
		||||
 | 
			
		||||
        if image.mode != 'L':
 | 
			
		||||
            raise Exception('Image must be binary, meaning it must use mode L')
 | 
			
		||||
        if image.mode != "L":
 | 
			
		||||
            raise Exception("Image must be binary, meaning it must use mode L")
 | 
			
		||||
        return _imagingmorph.get_on_pixels(image.im.id)
 | 
			
		||||
 | 
			
		||||
    def load_lut(self, filename):
 | 
			
		||||
        """Load an operator from an mrl file"""
 | 
			
		||||
        with open(filename, 'rb') as f:
 | 
			
		||||
        with open(filename, "rb") as f:
 | 
			
		||||
            self.lut = bytearray(f.read())
 | 
			
		||||
 | 
			
		||||
        if len(self.lut) != LUT_SIZE:
 | 
			
		||||
            self.lut = None
 | 
			
		||||
            raise Exception('Wrong size operator file!')
 | 
			
		||||
            raise Exception("Wrong size operator file!")
 | 
			
		||||
 | 
			
		||||
    def save_lut(self, filename):
 | 
			
		||||
        """Save an operator to an mrl file"""
 | 
			
		||||
        if self.lut is None:
 | 
			
		||||
            raise Exception('No operator loaded')
 | 
			
		||||
        with open(filename, 'wb') as f:
 | 
			
		||||
            raise Exception("No operator loaded")
 | 
			
		||||
        with open(filename, "wb") as f:
 | 
			
		||||
            f.write(self.lut)
 | 
			
		||||
 | 
			
		||||
    def set_lut(self, lut):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,6 +26,7 @@ import functools
 | 
			
		|||
#
 | 
			
		||||
# helpers
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _border(border):
 | 
			
		||||
    if isinstance(border, tuple):
 | 
			
		||||
        if len(border) == 2:
 | 
			
		||||
| 
						 | 
				
			
			@ -40,6 +41,7 @@ def _border(border):
 | 
			
		|||
def _color(color, mode):
 | 
			
		||||
    if isStringType(color):
 | 
			
		||||
        from . import ImageColor
 | 
			
		||||
 | 
			
		||||
        color = ImageColor.getcolor(color, mode)
 | 
			
		||||
    return color
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -55,6 +57,7 @@ def _lut(image, lut):
 | 
			
		|||
    else:
 | 
			
		||||
        raise IOError("not supported for this image mode")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# actions
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -75,7 +78,7 @@ def autocontrast(image, cutoff=0, ignore=None):
 | 
			
		|||
    histogram = image.histogram()
 | 
			
		||||
    lut = []
 | 
			
		||||
    for layer in range(0, len(histogram), 256):
 | 
			
		||||
        h = histogram[layer:layer+256]
 | 
			
		||||
        h = histogram[layer : layer + 256]
 | 
			
		||||
        if ignore is not None:
 | 
			
		||||
            # get rid of outliers
 | 
			
		||||
            try:
 | 
			
		||||
| 
						 | 
				
			
			@ -135,8 +138,7 @@ def autocontrast(image, cutoff=0, ignore=None):
 | 
			
		|||
    return _lut(image, lut)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def colorize(image, black, white, mid=None, blackpoint=0,
 | 
			
		||||
             whitepoint=255, midpoint=127):
 | 
			
		||||
def colorize(image, black, white, mid=None, blackpoint=0, whitepoint=255, midpoint=127):
 | 
			
		||||
    """
 | 
			
		||||
    Colorize grayscale image.
 | 
			
		||||
    This function calculates a color wedge which maps all black pixels in
 | 
			
		||||
| 
						 | 
				
			
			@ -276,9 +278,7 @@ def crop(image, border=0):
 | 
			
		|||
    :return: An image.
 | 
			
		||||
    """
 | 
			
		||||
    left, top, right, bottom = _border(border)
 | 
			
		||||
    return image.crop(
 | 
			
		||||
        (left, top, image.size[0]-right, image.size[1]-bottom)
 | 
			
		||||
        )
 | 
			
		||||
    return image.crop((left, top, image.size[0] - right, image.size[1] - bottom))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def scale(image, factor, resample=Image.NEAREST):
 | 
			
		||||
| 
						 | 
				
			
			@ -298,8 +298,7 @@ def scale(image, factor, resample=Image.NEAREST):
 | 
			
		|||
    elif factor <= 0:
 | 
			
		||||
        raise ValueError("the factor must be greater than 0")
 | 
			
		||||
    else:
 | 
			
		||||
        size = (int(round(factor * image.width)),
 | 
			
		||||
                int(round(factor * image.height)))
 | 
			
		||||
        size = (int(round(factor * image.width)), int(round(factor * image.height)))
 | 
			
		||||
        return image.resize(size, resample)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -314,9 +313,7 @@ def deform(image, deformer, resample=Image.BILINEAR):
 | 
			
		|||
       in the PIL.Image.transform function.
 | 
			
		||||
    :return: An image.
 | 
			
		||||
    """
 | 
			
		||||
    return image.transform(
 | 
			
		||||
        image.size, Image.MESH, deformer.getmesh(image), resample
 | 
			
		||||
        )
 | 
			
		||||
    return image.transform(image.size, Image.MESH, deformer.getmesh(image), resample)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def equalize(image, mask=None):
 | 
			
		||||
| 
						 | 
				
			
			@ -335,7 +332,7 @@ def equalize(image, mask=None):
 | 
			
		|||
    h = image.histogram(mask)
 | 
			
		||||
    lut = []
 | 
			
		||||
    for b in range(0, len(h), 256):
 | 
			
		||||
        histo = [_f for _f in h[b:b+256] if _f]
 | 
			
		||||
        histo = [_f for _f in h[b : b + 256] if _f]
 | 
			
		||||
        if len(histo) <= 1:
 | 
			
		||||
            lut.extend(list(range(256)))
 | 
			
		||||
        else:
 | 
			
		||||
| 
						 | 
				
			
			@ -346,7 +343,7 @@ def equalize(image, mask=None):
 | 
			
		|||
                n = step // 2
 | 
			
		||||
                for i in range(256):
 | 
			
		||||
                    lut.append(n // step)
 | 
			
		||||
                    n = n + h[i+b]
 | 
			
		||||
                    n = n + h[i + b]
 | 
			
		||||
    return _lut(image, lut)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -417,8 +414,10 @@ def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)):
 | 
			
		|||
    # number of pixels to trim off on Top and Bottom, Left and Right
 | 
			
		||||
    bleed_pixels = (bleed * image.size[0], bleed * image.size[1])
 | 
			
		||||
 | 
			
		||||
    live_size = (image.size[0] - bleed_pixels[0] * 2,
 | 
			
		||||
                 image.size[1] - bleed_pixels[1] * 2)
 | 
			
		||||
    live_size = (
 | 
			
		||||
        image.size[0] - bleed_pixels[0] * 2,
 | 
			
		||||
        image.size[1] - bleed_pixels[1] * 2,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # calculate the aspect ratio of the live_size
 | 
			
		||||
    live_size_ratio = float(live_size[0]) / live_size[1]
 | 
			
		||||
| 
						 | 
				
			
			@ -437,13 +436,10 @@ def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)):
 | 
			
		|||
        crop_height = live_size[0] / output_ratio
 | 
			
		||||
 | 
			
		||||
    # make the crop
 | 
			
		||||
    crop_left = bleed_pixels[0] + (live_size[0]-crop_width) * centering[0]
 | 
			
		||||
    crop_top = bleed_pixels[1] + (live_size[1]-crop_height) * centering[1]
 | 
			
		||||
    crop_left = bleed_pixels[0] + (live_size[0] - crop_width) * centering[0]
 | 
			
		||||
    crop_top = bleed_pixels[1] + (live_size[1] - crop_height) * centering[1]
 | 
			
		||||
 | 
			
		||||
    crop = (
 | 
			
		||||
        crop_left, crop_top,
 | 
			
		||||
        crop_left + crop_width, crop_top + crop_height
 | 
			
		||||
    )
 | 
			
		||||
    crop = (crop_left, crop_top, crop_left + crop_width, crop_top + crop_height)
 | 
			
		||||
 | 
			
		||||
    # resize the image and return it
 | 
			
		||||
    return image.resize(size, method, box=crop)
 | 
			
		||||
| 
						 | 
				
			
			@ -478,7 +474,7 @@ def invert(image):
 | 
			
		|||
    """
 | 
			
		||||
    lut = []
 | 
			
		||||
    for i in range(256):
 | 
			
		||||
        lut.append(255-i)
 | 
			
		||||
        lut.append(255 - i)
 | 
			
		||||
    return _lut(image, lut)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -501,7 +497,7 @@ def posterize(image, bits):
 | 
			
		|||
    :return: An image.
 | 
			
		||||
    """
 | 
			
		||||
    lut = []
 | 
			
		||||
    mask = ~(2**(8-bits)-1)
 | 
			
		||||
    mask = ~(2 ** (8 - bits) - 1)
 | 
			
		||||
    for i in range(256):
 | 
			
		||||
        lut.append(i & mask)
 | 
			
		||||
    return _lut(image, lut)
 | 
			
		||||
| 
						 | 
				
			
			@ -520,7 +516,7 @@ def solarize(image, threshold=128):
 | 
			
		|||
        if i < threshold:
 | 
			
		||||
            lut.append(i)
 | 
			
		||||
        else:
 | 
			
		||||
            lut.append(255-i)
 | 
			
		||||
            lut.append(255 - i)
 | 
			
		||||
    return _lut(image, lut)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -541,7 +537,7 @@ def exif_transpose(image):
 | 
			
		|||
        5: Image.TRANSPOSE,
 | 
			
		||||
        6: Image.ROTATE_270,
 | 
			
		||||
        7: Image.TRANSVERSE,
 | 
			
		||||
        8: Image.ROTATE_90
 | 
			
		||||
        8: Image.ROTATE_90,
 | 
			
		||||
    }.get(orientation)
 | 
			
		||||
    if method is not None:
 | 
			
		||||
        transposed_image = image.transpose(method)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,11 +38,12 @@ class ImagePalette(object):
 | 
			
		|||
    def __init__(self, mode="RGB", palette=None, size=0):
 | 
			
		||||
        self.mode = mode
 | 
			
		||||
        self.rawmode = None  # if set, palette contains raw data
 | 
			
		||||
        self.palette = palette or bytearray(range(256))*len(self.mode)
 | 
			
		||||
        self.palette = palette or bytearray(range(256)) * len(self.mode)
 | 
			
		||||
        self.colors = {}
 | 
			
		||||
        self.dirty = None
 | 
			
		||||
        if ((size == 0 and len(self.mode)*256 != len(self.palette)) or
 | 
			
		||||
                (size != 0 and size != len(self.palette))):
 | 
			
		||||
        if (size == 0 and len(self.mode) * 256 != len(self.palette)) or (
 | 
			
		||||
            size != 0 and size != len(self.palette)
 | 
			
		||||
        ):
 | 
			
		||||
            raise ValueError("wrong palette size")
 | 
			
		||||
 | 
			
		||||
    def copy(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -78,7 +79,7 @@ class ImagePalette(object):
 | 
			
		|||
        if isinstance(self.palette, bytes):
 | 
			
		||||
            return self.palette
 | 
			
		||||
        arr = array.array("B", self.palette)
 | 
			
		||||
        if hasattr(arr, 'tobytes'):
 | 
			
		||||
        if hasattr(arr, "tobytes"):
 | 
			
		||||
            return arr.tobytes()
 | 
			
		||||
        return arr.tostring()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -104,8 +105,8 @@ class ImagePalette(object):
 | 
			
		|||
                    raise ValueError("cannot allocate more than 256 colors")
 | 
			
		||||
                self.colors[color] = index
 | 
			
		||||
                self.palette[index] = color[0]
 | 
			
		||||
                self.palette[index+256] = color[1]
 | 
			
		||||
                self.palette[index+512] = color[2]
 | 
			
		||||
                self.palette[index + 256] = color[1]
 | 
			
		||||
                self.palette[index + 512] = color[2]
 | 
			
		||||
                self.dirty = 1
 | 
			
		||||
                return index
 | 
			
		||||
        else:
 | 
			
		||||
| 
						 | 
				
			
			@ -124,7 +125,7 @@ class ImagePalette(object):
 | 
			
		|||
        fp.write("# Mode: %s\n" % self.mode)
 | 
			
		||||
        for i in range(256):
 | 
			
		||||
            fp.write("%d" % i)
 | 
			
		||||
            for j in range(i*len(self.mode), (i+1)*len(self.mode)):
 | 
			
		||||
            for j in range(i * len(self.mode), (i + 1) * len(self.mode)):
 | 
			
		||||
                try:
 | 
			
		||||
                    fp.write(" %d" % self.palette[j])
 | 
			
		||||
                except IndexError:
 | 
			
		||||
| 
						 | 
				
			
			@ -136,6 +137,7 @@ class ImagePalette(object):
 | 
			
		|||
# --------------------------------------------------------------------
 | 
			
		||||
# Internal
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def raw(rawmode, data):
 | 
			
		||||
    palette = ImagePalette()
 | 
			
		||||
    palette.rawmode = rawmode
 | 
			
		||||
| 
						 | 
				
			
			@ -147,11 +149,12 @@ def raw(rawmode, data):
 | 
			
		|||
# --------------------------------------------------------------------
 | 
			
		||||
# Factories
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def make_linear_lut(black, white):
 | 
			
		||||
    lut = []
 | 
			
		||||
    if black == 0:
 | 
			
		||||
        for i in range(256):
 | 
			
		||||
            lut.append(white*i//255)
 | 
			
		||||
            lut.append(white * i // 255)
 | 
			
		||||
    else:
 | 
			
		||||
        raise NotImplementedError  # FIXME
 | 
			
		||||
    return lut
 | 
			
		||||
| 
						 | 
				
			
			@ -172,8 +175,9 @@ def negative(mode="RGB"):
 | 
			
		|||
 | 
			
		||||
def random(mode="RGB"):
 | 
			
		||||
    from random import randint
 | 
			
		||||
 | 
			
		||||
    palette = []
 | 
			
		||||
    for i in range(256*len(mode)):
 | 
			
		||||
    for i in range(256 * len(mode)):
 | 
			
		||||
        palette.append(randint(0, 255))
 | 
			
		||||
    return ImagePalette(mode, palette)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -199,7 +203,7 @@ def load(filename):
 | 
			
		|||
        for paletteHandler in [
 | 
			
		||||
            GimpPaletteFile.GimpPaletteFile,
 | 
			
		||||
            GimpGradientFile.GimpGradientFile,
 | 
			
		||||
            PaletteFile.PaletteFile
 | 
			
		||||
            PaletteFile.PaletteFile,
 | 
			
		||||
        ]:
 | 
			
		||||
            try:
 | 
			
		||||
                fp.seek(0)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,12 +22,7 @@ from io import BytesIO
 | 
			
		|||
import sys
 | 
			
		||||
import warnings
 | 
			
		||||
 | 
			
		||||
qt_versions = [
 | 
			
		||||
    ['5', 'PyQt5'],
 | 
			
		||||
    ['side2', 'PySide2'],
 | 
			
		||||
    ['4', 'PyQt4'],
 | 
			
		||||
    ['side', 'PySide']
 | 
			
		||||
]
 | 
			
		||||
qt_versions = [["5", "PyQt5"], ["side2", "PySide2"], ["4", "PyQt4"], ["side", "PySide"]]
 | 
			
		||||
 | 
			
		||||
WARNING_TEXT = (
 | 
			
		||||
    "Support for EOL {} is deprecated and will be removed in a future version. "
 | 
			
		||||
| 
						 | 
				
			
			@ -35,22 +30,21 @@ WARNING_TEXT = (
 | 
			
		|||
)
 | 
			
		||||
 | 
			
		||||
# If a version has already been imported, attempt it first
 | 
			
		||||
qt_versions.sort(key=lambda qt_version: qt_version[1] in sys.modules,
 | 
			
		||||
                 reverse=True)
 | 
			
		||||
qt_versions.sort(key=lambda qt_version: qt_version[1] in sys.modules, reverse=True)
 | 
			
		||||
for qt_version, qt_module in qt_versions:
 | 
			
		||||
    try:
 | 
			
		||||
        if qt_module == 'PyQt5':
 | 
			
		||||
        if qt_module == "PyQt5":
 | 
			
		||||
            from PyQt5.QtGui import QImage, qRgba, QPixmap
 | 
			
		||||
            from PyQt5.QtCore import QBuffer, QIODevice
 | 
			
		||||
        elif qt_module == 'PySide2':
 | 
			
		||||
        elif qt_module == "PySide2":
 | 
			
		||||
            from PySide2.QtGui import QImage, qRgba, QPixmap
 | 
			
		||||
            from PySide2.QtCore import QBuffer, QIODevice
 | 
			
		||||
        elif qt_module == 'PyQt4':
 | 
			
		||||
        elif qt_module == "PyQt4":
 | 
			
		||||
            from PyQt4.QtGui import QImage, qRgba, QPixmap
 | 
			
		||||
            from PyQt4.QtCore import QBuffer, QIODevice
 | 
			
		||||
 | 
			
		||||
            warnings.warn(WARNING_TEXT.format(qt_module), DeprecationWarning)
 | 
			
		||||
        elif qt_module == 'PySide':
 | 
			
		||||
        elif qt_module == "PySide":
 | 
			
		||||
            from PySide.QtGui import QImage, qRgba, QPixmap
 | 
			
		||||
            from PySide.QtCore import QBuffer, QIODevice
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -68,7 +62,7 @@ def rgb(r, g, b, a=255):
 | 
			
		|||
    """(Internal) Turns an RGB color into a Qt compatible color integer."""
 | 
			
		||||
    # use qRgb to pack the colors, and then turn the resulting long
 | 
			
		||||
    # into a negative integer with the same bitpattern.
 | 
			
		||||
    return (qRgba(r, g, b, a) & 0xffffffff)
 | 
			
		||||
    return qRgba(r, g, b, a) & 0xFFFFFFFF
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def fromqimage(im):
 | 
			
		||||
| 
						 | 
				
			
			@ -81,9 +75,9 @@ def fromqimage(im):
 | 
			
		|||
    # preserve alpha channel with png
 | 
			
		||||
    # otherwise ppm is more friendly with Image.open
 | 
			
		||||
    if im.hasAlphaChannel():
 | 
			
		||||
        im.save(buffer, 'png')
 | 
			
		||||
        im.save(buffer, "png")
 | 
			
		||||
    else:
 | 
			
		||||
        im.save(buffer, 'ppm')
 | 
			
		||||
        im.save(buffer, "ppm")
 | 
			
		||||
 | 
			
		||||
    b = BytesIO()
 | 
			
		||||
    try:
 | 
			
		||||
| 
						 | 
				
			
			@ -116,11 +110,7 @@ def align8to32(bytes, width, mode):
 | 
			
		|||
    converts each scanline of data from 8 bit to 32 bit aligned
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    bits_per_pixel = {
 | 
			
		||||
        '1': 1,
 | 
			
		||||
        'L': 8,
 | 
			
		||||
        'P': 8,
 | 
			
		||||
    }[mode]
 | 
			
		||||
    bits_per_pixel = {"1": 1, "L": 8, "P": 8}[mode]
 | 
			
		||||
 | 
			
		||||
    # calculate bytes per line and the extra padding if needed
 | 
			
		||||
    bits_per_line = bits_per_pixel * width
 | 
			
		||||
| 
						 | 
				
			
			@ -135,10 +125,12 @@ def align8to32(bytes, width, mode):
 | 
			
		|||
 | 
			
		||||
    new_data = []
 | 
			
		||||
    for i in range(len(bytes) // bytes_per_line):
 | 
			
		||||
        new_data.append(bytes[i*bytes_per_line:(i+1)*bytes_per_line]
 | 
			
		||||
                        + b'\x00' * extra_padding)
 | 
			
		||||
        new_data.append(
 | 
			
		||||
            bytes[i * bytes_per_line : (i + 1) * bytes_per_line]
 | 
			
		||||
            + b"\x00" * extra_padding
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    return b''.join(new_data)
 | 
			
		||||
    return b"".join(new_data)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _toqclass_helper(im):
 | 
			
		||||
| 
						 | 
				
			
			@ -167,7 +159,7 @@ def _toqclass_helper(im):
 | 
			
		|||
        colortable = []
 | 
			
		||||
        palette = im.getpalette()
 | 
			
		||||
        for i in range(0, len(palette), 3):
 | 
			
		||||
            colortable.append(rgb(*palette[i:i+3]))
 | 
			
		||||
            colortable.append(rgb(*palette[i : i + 3]))
 | 
			
		||||
    elif im.mode == "RGB":
 | 
			
		||||
        data = im.tobytes("raw", "BGRX")
 | 
			
		||||
        format = QImage.Format_RGB32
 | 
			
		||||
| 
						 | 
				
			
			@ -183,14 +175,12 @@ def _toqclass_helper(im):
 | 
			
		|||
        raise ValueError("unsupported image mode %r" % im.mode)
 | 
			
		||||
 | 
			
		||||
    __data = data or align8to32(im.tobytes(), im.size[0], im.mode)
 | 
			
		||||
    return {
 | 
			
		||||
        'data': __data, 'im': im, 'format': format, 'colortable': colortable
 | 
			
		||||
    }
 | 
			
		||||
    return {"data": __data, "im": im, "format": format, "colortable": colortable}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if qt_is_installed:
 | 
			
		||||
    class ImageQt(QImage):
 | 
			
		||||
 | 
			
		||||
    class ImageQt(QImage):
 | 
			
		||||
        def __init__(self, im):
 | 
			
		||||
            """
 | 
			
		||||
            An PIL image wrapper for Qt.  This is a subclass of PyQt's QImage
 | 
			
		||||
| 
						 | 
				
			
			@ -204,12 +194,16 @@ if qt_is_installed:
 | 
			
		|||
            # All QImage constructors that take data operate on an existing
 | 
			
		||||
            # buffer, so this buffer has to hang on for the life of the image.
 | 
			
		||||
            # Fixes https://github.com/python-pillow/Pillow/issues/1370
 | 
			
		||||
            self.__data = im_data['data']
 | 
			
		||||
            QImage.__init__(self,
 | 
			
		||||
                            self.__data, im_data['im'].size[0],
 | 
			
		||||
                            im_data['im'].size[1], im_data['format'])
 | 
			
		||||
            if im_data['colortable']:
 | 
			
		||||
                self.setColorTable(im_data['colortable'])
 | 
			
		||||
            self.__data = im_data["data"]
 | 
			
		||||
            QImage.__init__(
 | 
			
		||||
                self,
 | 
			
		||||
                self.__data,
 | 
			
		||||
                im_data["im"].size[0],
 | 
			
		||||
                im_data["im"].size[1],
 | 
			
		||||
                im_data["format"],
 | 
			
		||||
            )
 | 
			
		||||
            if im_data["colortable"]:
 | 
			
		||||
                self.setColorTable(im_data["colortable"])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def toqimage(im):
 | 
			
		||||
| 
						 | 
				
			
			@ -222,8 +216,8 @@ def toqpixmap(im):
 | 
			
		|||
    # result = QPixmap(im_data['im'].size[0], im_data['im'].size[1])
 | 
			
		||||
    # result.loadFromData(im_data['data'])
 | 
			
		||||
    # Fix some strange bug that causes
 | 
			
		||||
    if im.mode == 'RGB':
 | 
			
		||||
        im = im.convert('RGBA')
 | 
			
		||||
    if im.mode == "RGB":
 | 
			
		||||
        im = im.convert("RGBA")
 | 
			
		||||
 | 
			
		||||
    qimage = toqimage(im)
 | 
			
		||||
    return QPixmap.fromImage(qimage)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -101,6 +101,7 @@ class Viewer(object):
 | 
			
		|||
        os.system(self.get_command(file, **options))
 | 
			
		||||
        return 1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# --------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -110,9 +111,11 @@ if sys.platform == "win32":
 | 
			
		|||
        format = "BMP"
 | 
			
		||||
 | 
			
		||||
        def get_command(self, file, **options):
 | 
			
		||||
            return ('start "Pillow" /WAIT "%s" '
 | 
			
		||||
                    '&& ping -n 2 127.0.0.1 >NUL '
 | 
			
		||||
                    '&& del /f "%s"' % (file, file))
 | 
			
		||||
            return (
 | 
			
		||||
                'start "Pillow" /WAIT "%s" '
 | 
			
		||||
                "&& ping -n 2 127.0.0.1 >NUL "
 | 
			
		||||
                '&& del /f "%s"' % (file, file)
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    register(WindowsViewer)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -120,28 +123,35 @@ elif sys.platform == "darwin":
 | 
			
		|||
 | 
			
		||||
    class MacViewer(Viewer):
 | 
			
		||||
        format = "PNG"
 | 
			
		||||
        options = {'compress_level': 1}
 | 
			
		||||
        options = {"compress_level": 1}
 | 
			
		||||
 | 
			
		||||
        def get_command(self, file, **options):
 | 
			
		||||
            # on darwin open returns immediately resulting in the temp
 | 
			
		||||
            # file removal while app is opening
 | 
			
		||||
            command = "open -a /Applications/Preview.app"
 | 
			
		||||
            command = "(%s %s; sleep 20; rm -f %s)&" % (command, quote(file),
 | 
			
		||||
                                                        quote(file))
 | 
			
		||||
            command = "(%s %s; sleep 20; rm -f %s)&" % (
 | 
			
		||||
                command,
 | 
			
		||||
                quote(file),
 | 
			
		||||
                quote(file),
 | 
			
		||||
            )
 | 
			
		||||
            return command
 | 
			
		||||
 | 
			
		||||
        def show_file(self, file, **options):
 | 
			
		||||
            """Display given file"""
 | 
			
		||||
            fd, path = tempfile.mkstemp()
 | 
			
		||||
            with os.fdopen(fd, 'w') as f:
 | 
			
		||||
            with os.fdopen(fd, "w") as f:
 | 
			
		||||
                f.write(file)
 | 
			
		||||
            with open(path, "r") as f:
 | 
			
		||||
                subprocess.Popen([
 | 
			
		||||
                    'im=$(cat);'
 | 
			
		||||
                    'open -a /Applications/Preview.app $im;'
 | 
			
		||||
                    'sleep 20;'
 | 
			
		||||
                    'rm -f $im'
 | 
			
		||||
                ], shell=True, stdin=f)
 | 
			
		||||
                subprocess.Popen(
 | 
			
		||||
                    [
 | 
			
		||||
                        "im=$(cat);"
 | 
			
		||||
                        "open -a /Applications/Preview.app $im;"
 | 
			
		||||
                        "sleep 20;"
 | 
			
		||||
                        "rm -f $im"
 | 
			
		||||
                    ],
 | 
			
		||||
                    shell=True,
 | 
			
		||||
                    stdin=f,
 | 
			
		||||
                )
 | 
			
		||||
            os.remove(path)
 | 
			
		||||
            return 1
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -163,7 +173,7 @@ else:
 | 
			
		|||
 | 
			
		||||
    class UnixViewer(Viewer):
 | 
			
		||||
        format = "PNG"
 | 
			
		||||
        options = {'compress_level': 1}
 | 
			
		||||
        options = {"compress_level": 1}
 | 
			
		||||
 | 
			
		||||
        def get_command(self, file, **options):
 | 
			
		||||
            command = self.get_command_ex(file, **options)[0]
 | 
			
		||||
| 
						 | 
				
			
			@ -172,15 +182,13 @@ else:
 | 
			
		|||
        def show_file(self, file, **options):
 | 
			
		||||
            """Display given file"""
 | 
			
		||||
            fd, path = tempfile.mkstemp()
 | 
			
		||||
            with os.fdopen(fd, 'w') as f:
 | 
			
		||||
            with os.fdopen(fd, "w") as f:
 | 
			
		||||
                f.write(file)
 | 
			
		||||
            with open(path, "r") as f:
 | 
			
		||||
                command = self.get_command_ex(file, **options)[0]
 | 
			
		||||
                subprocess.Popen([
 | 
			
		||||
                    'im=$(cat);' +
 | 
			
		||||
                    command+' $im;'
 | 
			
		||||
                    'rm -f $im'
 | 
			
		||||
                ], shell=True, stdin=f)
 | 
			
		||||
                subprocess.Popen(
 | 
			
		||||
                    ["im=$(cat);" + command + " $im;" "rm -f $im"], shell=True, stdin=f
 | 
			
		||||
                )
 | 
			
		||||
            os.remove(path)
 | 
			
		||||
            return 1
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,7 +27,6 @@ import functools
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
class Stat(object):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, image_or_list, mask=None):
 | 
			
		||||
        try:
 | 
			
		||||
            if mask:
 | 
			
		||||
| 
						 | 
				
			
			@ -71,7 +70,7 @@ class Stat(object):
 | 
			
		|||
 | 
			
		||||
        v = []
 | 
			
		||||
        for i in range(0, len(self.h), 256):
 | 
			
		||||
            v.append(functools.reduce(operator.add, self.h[i:i+256]))
 | 
			
		||||
            v.append(functools.reduce(operator.add, self.h[i : i + 256]))
 | 
			
		||||
        return v
 | 
			
		||||
 | 
			
		||||
    def _getsum(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -110,10 +109,10 @@ class Stat(object):
 | 
			
		|||
        v = []
 | 
			
		||||
        for i in self.bands:
 | 
			
		||||
            s = 0
 | 
			
		||||
            half = self.count[i]//2
 | 
			
		||||
            half = self.count[i] // 2
 | 
			
		||||
            b = i * 256
 | 
			
		||||
            for j in range(256):
 | 
			
		||||
                s = s + self.h[b+j]
 | 
			
		||||
                s = s + self.h[b + j]
 | 
			
		||||
                if s > half:
 | 
			
		||||
                    break
 | 
			
		||||
            v.append(j)
 | 
			
		||||
| 
						 | 
				
			
			@ -133,7 +132,7 @@ class Stat(object):
 | 
			
		|||
        v = []
 | 
			
		||||
        for i in self.bands:
 | 
			
		||||
            n = self.count[i]
 | 
			
		||||
            v.append((self.sum2[i]-(self.sum[i]**2.0)/n)/n)
 | 
			
		||||
            v.append((self.sum2[i] - (self.sum[i] ** 2.0) / n) / n)
 | 
			
		||||
        return v
 | 
			
		||||
 | 
			
		||||
    def _getstddev(self):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -67,6 +67,7 @@ def _get_image_from_kw(kw):
 | 
			
		|||
# --------------------------------------------------------------------
 | 
			
		||||
# PhotoImage
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PhotoImage(object):
 | 
			
		||||
    """
 | 
			
		||||
    A Tkinter-compatible photo image.  This can be used
 | 
			
		||||
| 
						 | 
				
			
			@ -183,17 +184,18 @@ class PhotoImage(object):
 | 
			
		|||
            # activate Tkinter hook
 | 
			
		||||
            try:
 | 
			
		||||
                from . import _imagingtk
 | 
			
		||||
 | 
			
		||||
                try:
 | 
			
		||||
                    if hasattr(tk, 'interp'):
 | 
			
		||||
                    if hasattr(tk, "interp"):
 | 
			
		||||
                        # Required for PyPy, which always has CFFI installed
 | 
			
		||||
                        from cffi import FFI
 | 
			
		||||
 | 
			
		||||
                        ffi = FFI()
 | 
			
		||||
 | 
			
		||||
                        # PyPy is using an FFI CDATA element
 | 
			
		||||
                        # (Pdb) self.tk.interp
 | 
			
		||||
                        #  <cdata 'Tcl_Interp *' 0x3061b50>
 | 
			
		||||
                        _imagingtk.tkinit(
 | 
			
		||||
                            int(ffi.cast("uintptr_t", tk.interp)), 1)
 | 
			
		||||
                        _imagingtk.tkinit(int(ffi.cast("uintptr_t", tk.interp)), 1)
 | 
			
		||||
                    else:
 | 
			
		||||
                        _imagingtk.tkinit(tk.interpaddr(), 1)
 | 
			
		||||
                except AttributeError:
 | 
			
		||||
| 
						 | 
				
			
			@ -202,6 +204,7 @@ class PhotoImage(object):
 | 
			
		|||
            except (ImportError, AttributeError, tkinter.TclError):
 | 
			
		||||
                raise  # configuration problem; cannot attach to Tkinter
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# --------------------------------------------------------------------
 | 
			
		||||
# BitmapImage
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -293,8 +296,7 @@ def _show(image, title):
 | 
			
		|||
                self.image = BitmapImage(im, foreground="white", master=master)
 | 
			
		||||
            else:
 | 
			
		||||
                self.image = PhotoImage(im, master=master)
 | 
			
		||||
            tkinter.Label.__init__(self, master, image=self.image,
 | 
			
		||||
                                   bg="black", bd=0)
 | 
			
		||||
            tkinter.Label.__init__(self, master, image=self.image, bg="black", bd=0)
 | 
			
		||||
 | 
			
		||||
    if not tkinter._default_root:
 | 
			
		||||
        raise IOError("tkinter not initialized")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,6 +46,7 @@ class AffineTransform(Transform):
 | 
			
		|||
    :param matrix: A 6-tuple (a, b, c, d, e, f) containing the first two rows
 | 
			
		||||
        from an affine transform matrix.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    method = Image.AFFINE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -67,6 +68,7 @@ class ExtentTransform(Transform):
 | 
			
		|||
    :param bbox: A 4-tuple (x0, y0, x1, y1) which specifies two points in the
 | 
			
		||||
        input image's coordinate system. See :ref:`coordinate-system`.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    method = Image.EXTENT
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -83,6 +85,7 @@ class QuadTransform(Transform):
 | 
			
		|||
        upper left, lower left, lower right, and upper right corner of the
 | 
			
		||||
        source quadrilateral.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    method = Image.QUAD
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -95,4 +98,5 @@ class MeshTransform(Transform):
 | 
			
		|||
 | 
			
		||||
    :param data: A list of (bbox, quad) tuples.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    method = Image.MESH
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,6 +26,7 @@ class HDC(object):
 | 
			
		|||
    :py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose`
 | 
			
		||||
    methods.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, dc):
 | 
			
		||||
        self.dc = dc
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -39,6 +40,7 @@ class HWND(object):
 | 
			
		|||
    :py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose`
 | 
			
		||||
    methods, instead of a DC.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, wnd):
 | 
			
		||||
        self.wnd = wnd
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,6 +33,7 @@ field = re.compile(br"([a-z]*) ([^ \r\n]*)")
 | 
			
		|||
##
 | 
			
		||||
# Image plugin for IM Tools images.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ImtImageFile(ImageFile.ImageFile):
 | 
			
		||||
 | 
			
		||||
    format = "IMT"
 | 
			
		||||
| 
						 | 
				
			
			@ -55,12 +56,12 @@ class ImtImageFile(ImageFile.ImageFile):
 | 
			
		|||
            if not s:
 | 
			
		||||
                break
 | 
			
		||||
 | 
			
		||||
            if s == b'\x0C':
 | 
			
		||||
            if s == b"\x0C":
 | 
			
		||||
 | 
			
		||||
                # image data begins
 | 
			
		||||
                self.tile = [("raw", (0, 0)+self.size,
 | 
			
		||||
                             self.fp.tell(),
 | 
			
		||||
                             (self.mode, 0, 1))]
 | 
			
		||||
                self.tile = [
 | 
			
		||||
                    ("raw", (0, 0) + self.size, self.fp.tell(), (self.mode, 0, 1))
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
                break
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,10 +26,7 @@ import tempfile
 | 
			
		|||
# PIL.__version__ instead.
 | 
			
		||||
__version__ = "0.3"
 | 
			
		||||
 | 
			
		||||
COMPRESSION = {
 | 
			
		||||
    1: "raw",
 | 
			
		||||
    5: "jpeg"
 | 
			
		||||
}
 | 
			
		||||
COMPRESSION = {1: "raw", 5: "jpeg"}
 | 
			
		||||
 | 
			
		||||
PAD = o8(0) * 4
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -37,13 +34,14 @@ 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("%02x" % i8(i), end=" ")
 | 
			
		||||
    print()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -51,6 +49,7 @@ def dump(c):
 | 
			
		|||
# 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"
 | 
			
		||||
| 
						 | 
				
			
			@ -79,7 +78,7 @@ class IptcImageFile(ImageFile.ImageFile):
 | 
			
		|||
        elif size == 128:
 | 
			
		||||
            size = 0
 | 
			
		||||
        elif size > 128:
 | 
			
		||||
            size = i(self.fp.read(size-128))
 | 
			
		||||
            size = i(self.fp.read(size - 128))
 | 
			
		||||
        else:
 | 
			
		||||
            size = i16(s[3:])
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -109,7 +108,7 @@ class IptcImageFile(ImageFile.ImageFile):
 | 
			
		|||
        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
 | 
			
		||||
            id = i8(self.info[(3, 65)][0]) - 1
 | 
			
		||||
        else:
 | 
			
		||||
            id = 0
 | 
			
		||||
        if layers == 1 and not component:
 | 
			
		||||
| 
						 | 
				
			
			@ -130,8 +129,9 @@ class IptcImageFile(ImageFile.ImageFile):
 | 
			
		|||
 | 
			
		||||
        # tile
 | 
			
		||||
        if tag == (8, 10):
 | 
			
		||||
            self.tile = [("iptc", (compression, offset),
 | 
			
		||||
                         (0, 0, self.size[0], self.size[1]))]
 | 
			
		||||
            self.tile = [
 | 
			
		||||
                ("iptc", (compression, offset), (0, 0, self.size[0], self.size[1]))
 | 
			
		||||
            ]
 | 
			
		||||
 | 
			
		||||
    def load(self):
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -216,6 +216,7 @@ def getiptcinfo(im):
 | 
			
		|||
    # create an IptcImagePlugin object without initializing it
 | 
			
		||||
    class FakeImage(object):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    im = FakeImage()
 | 
			
		||||
    im.__class__ = IptcImageFile
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,30 +27,29 @@ def _parse_codestream(fp):
 | 
			
		|||
    count from the SIZ marker segment, returning a PIL (size, mode) tuple."""
 | 
			
		||||
 | 
			
		||||
    hdr = fp.read(2)
 | 
			
		||||
    lsiz = struct.unpack('>H', hdr)[0]
 | 
			
		||||
    lsiz = struct.unpack(">H", hdr)[0]
 | 
			
		||||
    siz = hdr + fp.read(lsiz - 2)
 | 
			
		||||
    lsiz, rsiz, xsiz, ysiz, xosiz, yosiz, xtsiz, ytsiz, \
 | 
			
		||||
        xtosiz, ytosiz, csiz \
 | 
			
		||||
        = struct.unpack_from('>HHIIIIIIIIH', siz)
 | 
			
		||||
    ssiz = [None]*csiz
 | 
			
		||||
    xrsiz = [None]*csiz
 | 
			
		||||
    yrsiz = [None]*csiz
 | 
			
		||||
    lsiz, rsiz, xsiz, ysiz, xosiz, yosiz, _, _, _, _, csiz = struct.unpack_from(
 | 
			
		||||
        ">HHIIIIIIIIH", siz
 | 
			
		||||
    )
 | 
			
		||||
    ssiz = [None] * csiz
 | 
			
		||||
    xrsiz = [None] * csiz
 | 
			
		||||
    yrsiz = [None] * csiz
 | 
			
		||||
    for i in range(csiz):
 | 
			
		||||
        ssiz[i], xrsiz[i], yrsiz[i] \
 | 
			
		||||
            = struct.unpack_from('>BBB', siz, 36 + 3 * i)
 | 
			
		||||
        ssiz[i], xrsiz[i], yrsiz[i] = struct.unpack_from(">BBB", siz, 36 + 3 * i)
 | 
			
		||||
 | 
			
		||||
    size = (xsiz - xosiz, ysiz - yosiz)
 | 
			
		||||
    if csiz == 1:
 | 
			
		||||
        if (yrsiz[0] & 0x7f) > 8:
 | 
			
		||||
            mode = 'I;16'
 | 
			
		||||
        if (yrsiz[0] & 0x7F) > 8:
 | 
			
		||||
            mode = "I;16"
 | 
			
		||||
        else:
 | 
			
		||||
            mode = 'L'
 | 
			
		||||
            mode = "L"
 | 
			
		||||
    elif csiz == 2:
 | 
			
		||||
        mode = 'LA'
 | 
			
		||||
        mode = "LA"
 | 
			
		||||
    elif csiz == 3:
 | 
			
		||||
        mode = 'RGB'
 | 
			
		||||
        mode = "RGB"
 | 
			
		||||
    elif csiz == 4:
 | 
			
		||||
        mode = 'RGBA'
 | 
			
		||||
        mode = "RGBA"
 | 
			
		||||
    else:
 | 
			
		||||
        mode = None
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -65,28 +64,28 @@ def _parse_jp2_header(fp):
 | 
			
		|||
    header = None
 | 
			
		||||
    mimetype = None
 | 
			
		||||
    while True:
 | 
			
		||||
        lbox, tbox = struct.unpack('>I4s', fp.read(8))
 | 
			
		||||
        lbox, tbox = struct.unpack(">I4s", fp.read(8))
 | 
			
		||||
        if lbox == 1:
 | 
			
		||||
            lbox = struct.unpack('>Q', fp.read(8))[0]
 | 
			
		||||
            lbox = struct.unpack(">Q", fp.read(8))[0]
 | 
			
		||||
            hlen = 16
 | 
			
		||||
        else:
 | 
			
		||||
            hlen = 8
 | 
			
		||||
 | 
			
		||||
        if lbox < hlen:
 | 
			
		||||
            raise SyntaxError('Invalid JP2 header length')
 | 
			
		||||
            raise SyntaxError("Invalid JP2 header length")
 | 
			
		||||
 | 
			
		||||
        if tbox == b'jp2h':
 | 
			
		||||
        if tbox == b"jp2h":
 | 
			
		||||
            header = fp.read(lbox - hlen)
 | 
			
		||||
            break
 | 
			
		||||
        elif tbox == b'ftyp':
 | 
			
		||||
            if fp.read(4) == b'jpx ':
 | 
			
		||||
                mimetype = 'image/jpx'
 | 
			
		||||
        elif tbox == b"ftyp":
 | 
			
		||||
            if fp.read(4) == b"jpx ":
 | 
			
		||||
                mimetype = "image/jpx"
 | 
			
		||||
            fp.seek(lbox - hlen - 4, os.SEEK_CUR)
 | 
			
		||||
        else:
 | 
			
		||||
            fp.seek(lbox - hlen, os.SEEK_CUR)
 | 
			
		||||
 | 
			
		||||
    if header is None:
 | 
			
		||||
        raise SyntaxError('could not find JP2 header')
 | 
			
		||||
        raise SyntaxError("could not find JP2 header")
 | 
			
		||||
 | 
			
		||||
    size = None
 | 
			
		||||
    mode = None
 | 
			
		||||
| 
						 | 
				
			
			@ -95,58 +94,57 @@ def _parse_jp2_header(fp):
 | 
			
		|||
 | 
			
		||||
    hio = io.BytesIO(header)
 | 
			
		||||
    while True:
 | 
			
		||||
        lbox, tbox = struct.unpack('>I4s', hio.read(8))
 | 
			
		||||
        lbox, tbox = struct.unpack(">I4s", hio.read(8))
 | 
			
		||||
        if lbox == 1:
 | 
			
		||||
            lbox = struct.unpack('>Q', hio.read(8))[0]
 | 
			
		||||
            lbox = struct.unpack(">Q", hio.read(8))[0]
 | 
			
		||||
            hlen = 16
 | 
			
		||||
        else:
 | 
			
		||||
            hlen = 8
 | 
			
		||||
 | 
			
		||||
        content = hio.read(lbox - hlen)
 | 
			
		||||
 | 
			
		||||
        if tbox == b'ihdr':
 | 
			
		||||
            height, width, nc, bpc, c, unkc, ipr \
 | 
			
		||||
                = struct.unpack('>IIHBBBB', content)
 | 
			
		||||
        if tbox == b"ihdr":
 | 
			
		||||
            height, width, nc, bpc, c, unkc, ipr = struct.unpack(">IIHBBBB", content)
 | 
			
		||||
            size = (width, height)
 | 
			
		||||
            if unkc:
 | 
			
		||||
                if nc == 1 and (bpc & 0x7f) > 8:
 | 
			
		||||
                    mode = 'I;16'
 | 
			
		||||
                if nc == 1 and (bpc & 0x7F) > 8:
 | 
			
		||||
                    mode = "I;16"
 | 
			
		||||
                elif nc == 1:
 | 
			
		||||
                    mode = 'L'
 | 
			
		||||
                    mode = "L"
 | 
			
		||||
                elif nc == 2:
 | 
			
		||||
                    mode = 'LA'
 | 
			
		||||
                    mode = "LA"
 | 
			
		||||
                elif nc == 3:
 | 
			
		||||
                    mode = 'RGB'
 | 
			
		||||
                    mode = "RGB"
 | 
			
		||||
                elif nc == 4:
 | 
			
		||||
                    mode = 'RGBA'
 | 
			
		||||
                    mode = "RGBA"
 | 
			
		||||
                break
 | 
			
		||||
        elif tbox == b'colr':
 | 
			
		||||
            meth, prec, approx = struct.unpack_from('>BBB', content)
 | 
			
		||||
        elif tbox == b"colr":
 | 
			
		||||
            meth, prec, approx = struct.unpack_from(">BBB", content)
 | 
			
		||||
            if meth == 1:
 | 
			
		||||
                cs = struct.unpack_from('>I', content, 3)[0]
 | 
			
		||||
                cs = struct.unpack_from(">I", content, 3)[0]
 | 
			
		||||
                if cs == 16:  # sRGB
 | 
			
		||||
                    if nc == 1 and (bpc & 0x7f) > 8:
 | 
			
		||||
                        mode = 'I;16'
 | 
			
		||||
                    if nc == 1 and (bpc & 0x7F) > 8:
 | 
			
		||||
                        mode = "I;16"
 | 
			
		||||
                    elif nc == 1:
 | 
			
		||||
                        mode = 'L'
 | 
			
		||||
                        mode = "L"
 | 
			
		||||
                    elif nc == 3:
 | 
			
		||||
                        mode = 'RGB'
 | 
			
		||||
                        mode = "RGB"
 | 
			
		||||
                    elif nc == 4:
 | 
			
		||||
                        mode = 'RGBA'
 | 
			
		||||
                        mode = "RGBA"
 | 
			
		||||
                    break
 | 
			
		||||
                elif cs == 17:  # grayscale
 | 
			
		||||
                    if nc == 1 and (bpc & 0x7f) > 8:
 | 
			
		||||
                        mode = 'I;16'
 | 
			
		||||
                    if nc == 1 and (bpc & 0x7F) > 8:
 | 
			
		||||
                        mode = "I;16"
 | 
			
		||||
                    elif nc == 1:
 | 
			
		||||
                        mode = 'L'
 | 
			
		||||
                        mode = "L"
 | 
			
		||||
                    elif nc == 2:
 | 
			
		||||
                        mode = 'LA'
 | 
			
		||||
                        mode = "LA"
 | 
			
		||||
                    break
 | 
			
		||||
                elif cs == 18:  # sYCC
 | 
			
		||||
                    if nc == 3:
 | 
			
		||||
                        mode = 'RGB'
 | 
			
		||||
                        mode = "RGB"
 | 
			
		||||
                    elif nc == 4:
 | 
			
		||||
                        mode = 'RGBA'
 | 
			
		||||
                        mode = "RGBA"
 | 
			
		||||
                    break
 | 
			
		||||
 | 
			
		||||
    if size is None or mode is None:
 | 
			
		||||
| 
						 | 
				
			
			@ -154,6 +152,7 @@ def _parse_jp2_header(fp):
 | 
			
		|||
 | 
			
		||||
    return (size, mode, mimetype)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
# Image plugin for JPEG2000 images.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -164,21 +163,21 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
 | 
			
		|||
 | 
			
		||||
    def _open(self):
 | 
			
		||||
        sig = self.fp.read(4)
 | 
			
		||||
        if sig == b'\xff\x4f\xff\x51':
 | 
			
		||||
        if sig == b"\xff\x4f\xff\x51":
 | 
			
		||||
            self.codec = "j2k"
 | 
			
		||||
            self._size, self.mode = _parse_codestream(self.fp)
 | 
			
		||||
        else:
 | 
			
		||||
            sig = sig + self.fp.read(8)
 | 
			
		||||
 | 
			
		||||
            if sig == b'\x00\x00\x00\x0cjP  \x0d\x0a\x87\x0a':
 | 
			
		||||
            if sig == b"\x00\x00\x00\x0cjP  \x0d\x0a\x87\x0a":
 | 
			
		||||
                self.codec = "jp2"
 | 
			
		||||
                header = _parse_jp2_header(self.fp)
 | 
			
		||||
                self._size, self.mode, self.custom_mimetype = header
 | 
			
		||||
            else:
 | 
			
		||||
                raise SyntaxError('not a JPEG 2000 file')
 | 
			
		||||
                raise SyntaxError("not a JPEG 2000 file")
 | 
			
		||||
 | 
			
		||||
        if self.size is None or self.mode is None:
 | 
			
		||||
            raise SyntaxError('unable to determine size/mode')
 | 
			
		||||
            raise SyntaxError("unable to determine size/mode")
 | 
			
		||||
 | 
			
		||||
        self.reduce = 0
 | 
			
		||||
        self.layers = 0
 | 
			
		||||
| 
						 | 
				
			
			@ -199,15 +198,23 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
 | 
			
		|||
            except Exception:
 | 
			
		||||
                length = -1
 | 
			
		||||
 | 
			
		||||
        self.tile = [('jpeg2k', (0, 0) + self.size, 0,
 | 
			
		||||
                      (self.codec, self.reduce, self.layers, fd, length))]
 | 
			
		||||
        self.tile = [
 | 
			
		||||
            (
 | 
			
		||||
                "jpeg2k",
 | 
			
		||||
                (0, 0) + self.size,
 | 
			
		||||
                0,
 | 
			
		||||
                (self.codec, self.reduce, self.layers, fd, length),
 | 
			
		||||
            )
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
    def load(self):
 | 
			
		||||
        if self.reduce:
 | 
			
		||||
            power = 1 << self.reduce
 | 
			
		||||
            adjust = power >> 1
 | 
			
		||||
            self._size = (int((self.size[0] + adjust) / power),
 | 
			
		||||
                          int((self.size[1] + adjust) / power))
 | 
			
		||||
            self._size = (
 | 
			
		||||
                int((self.size[0] + adjust) / power),
 | 
			
		||||
                int((self.size[1] + adjust) / power),
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        if self.tile:
 | 
			
		||||
            # Update the reduce and layers settings
 | 
			
		||||
| 
						 | 
				
			
			@ -219,40 +226,47 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
def _accept(prefix):
 | 
			
		||||
    return (prefix[:4] == b'\xff\x4f\xff\x51' or
 | 
			
		||||
            prefix[:12] == b'\x00\x00\x00\x0cjP  \x0d\x0a\x87\x0a')
 | 
			
		||||
    return (
 | 
			
		||||
        prefix[:4] == b"\xff\x4f\xff\x51"
 | 
			
		||||
        or prefix[:12] == b"\x00\x00\x00\x0cjP  \x0d\x0a\x87\x0a"
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------
 | 
			
		||||
# Save support
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _save(im, fp, filename):
 | 
			
		||||
    if filename.endswith('.j2k'):
 | 
			
		||||
        kind = 'j2k'
 | 
			
		||||
    if filename.endswith(".j2k"):
 | 
			
		||||
        kind = "j2k"
 | 
			
		||||
    else:
 | 
			
		||||
        kind = 'jp2'
 | 
			
		||||
        kind = "jp2"
 | 
			
		||||
 | 
			
		||||
    # Get the keyword arguments
 | 
			
		||||
    info = im.encoderinfo
 | 
			
		||||
 | 
			
		||||
    offset = info.get('offset', None)
 | 
			
		||||
    tile_offset = info.get('tile_offset', None)
 | 
			
		||||
    tile_size = info.get('tile_size', None)
 | 
			
		||||
    quality_mode = info.get('quality_mode', 'rates')
 | 
			
		||||
    quality_layers = info.get('quality_layers', None)
 | 
			
		||||
    offset = info.get("offset", None)
 | 
			
		||||
    tile_offset = info.get("tile_offset", None)
 | 
			
		||||
    tile_size = info.get("tile_size", None)
 | 
			
		||||
    quality_mode = info.get("quality_mode", "rates")
 | 
			
		||||
    quality_layers = info.get("quality_layers", None)
 | 
			
		||||
    if quality_layers is not None and not (
 | 
			
		||||
        isinstance(quality_layers, (list, tuple)) and
 | 
			
		||||
        all([isinstance(quality_layer, (int, float))
 | 
			
		||||
             for quality_layer in quality_layers])
 | 
			
		||||
        isinstance(quality_layers, (list, tuple))
 | 
			
		||||
        and all(
 | 
			
		||||
            [
 | 
			
		||||
                isinstance(quality_layer, (int, float))
 | 
			
		||||
                for quality_layer in quality_layers
 | 
			
		||||
            ]
 | 
			
		||||
        )
 | 
			
		||||
    ):
 | 
			
		||||
        raise ValueError('quality_layers must be a sequence of numbers')
 | 
			
		||||
        raise ValueError("quality_layers must be a sequence of numbers")
 | 
			
		||||
 | 
			
		||||
    num_resolutions = info.get('num_resolutions', 0)
 | 
			
		||||
    cblk_size = info.get('codeblock_size', None)
 | 
			
		||||
    precinct_size = info.get('precinct_size', None)
 | 
			
		||||
    irreversible = info.get('irreversible', False)
 | 
			
		||||
    progression = info.get('progression', 'LRCP')
 | 
			
		||||
    cinema_mode = info.get('cinema_mode', 'no')
 | 
			
		||||
    num_resolutions = info.get("num_resolutions", 0)
 | 
			
		||||
    cblk_size = info.get("codeblock_size", None)
 | 
			
		||||
    precinct_size = info.get("precinct_size", None)
 | 
			
		||||
    irreversible = info.get("irreversible", False)
 | 
			
		||||
    progression = info.get("progression", "LRCP")
 | 
			
		||||
    cinema_mode = info.get("cinema_mode", "no")
 | 
			
		||||
    fd = -1
 | 
			
		||||
 | 
			
		||||
    if hasattr(fp, "fileno"):
 | 
			
		||||
| 
						 | 
				
			
			@ -273,10 +287,11 @@ def _save(im, fp, filename):
 | 
			
		|||
        irreversible,
 | 
			
		||||
        progression,
 | 
			
		||||
        cinema_mode,
 | 
			
		||||
        fd
 | 
			
		||||
        fd,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    ImageFile._save(im, fp, [('jpeg2k', (0, 0)+im.size, 0, kind)])
 | 
			
		||||
    ImageFile._save(im, fp, [("jpeg2k", (0, 0) + im.size, 0, kind)])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------
 | 
			
		||||
# Registry stuff
 | 
			
		||||
| 
						 | 
				
			
			@ -285,7 +300,8 @@ def _save(im, fp, filename):
 | 
			
		|||
Image.register_open(Jpeg2KImageFile.format, Jpeg2KImageFile, _accept)
 | 
			
		||||
Image.register_save(Jpeg2KImageFile.format, _save)
 | 
			
		||||
 | 
			
		||||
Image.register_extensions(Jpeg2KImageFile.format,
 | 
			
		||||
                          [".jp2", ".j2k", ".jpc", ".jpf", ".jpx", ".j2c"])
 | 
			
		||||
Image.register_extensions(
 | 
			
		||||
    Jpeg2KImageFile.format, [".jp2", ".j2k", ".jpc", ".jpf", ".jpx", ".j2c"]
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
Image.register_mime(Jpeg2KImageFile.format, 'image/jp2')
 | 
			
		||||
Image.register_mime(Jpeg2KImageFile.format, "image/jp2")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -51,8 +51,9 @@ __version__ = "0.6"
 | 
			
		|||
#
 | 
			
		||||
# Parser
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def Skip(self, marker):
 | 
			
		||||
    n = i16(self.fp.read(2))-2
 | 
			
		||||
    n = i16(self.fp.read(2)) - 2
 | 
			
		||||
    ImageFile._safe_read(self.fp, n)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -61,7 +62,7 @@ def APP(self, marker):
 | 
			
		|||
    # Application marker.  Store these in the APP dictionary.
 | 
			
		||||
    # Also look for well-known application markers.
 | 
			
		||||
 | 
			
		||||
    n = i16(self.fp.read(2))-2
 | 
			
		||||
    n = i16(self.fp.read(2)) - 2
 | 
			
		||||
    s = ImageFile._safe_read(self.fp, n)
 | 
			
		||||
 | 
			
		||||
    app = "APP%d" % (marker & 15)
 | 
			
		||||
| 
						 | 
				
			
			@ -110,7 +111,7 @@ def APP(self, marker):
 | 
			
		|||
            # parse the image resource block
 | 
			
		||||
            offset = 0
 | 
			
		||||
            photoshop = {}
 | 
			
		||||
            while blocks[offset:offset+4] == b"8BIM":
 | 
			
		||||
            while blocks[offset : offset + 4] == b"8BIM":
 | 
			
		||||
                offset += 4
 | 
			
		||||
                # resource code
 | 
			
		||||
                code = i16(blocks, offset)
 | 
			
		||||
| 
						 | 
				
			
			@ -124,13 +125,13 @@ def APP(self, marker):
 | 
			
		|||
                # resource data block
 | 
			
		||||
                size = i32(blocks, offset)
 | 
			
		||||
                offset += 4
 | 
			
		||||
                data = blocks[offset:offset+size]
 | 
			
		||||
                data = blocks[offset : offset + size]
 | 
			
		||||
                if code == 0x03ED:  # ResolutionInfo
 | 
			
		||||
                    data = {
 | 
			
		||||
                        'XResolution': i32(data[:4]) / 65536,
 | 
			
		||||
                        'DisplayedUnitsX': i16(data[4:8]),
 | 
			
		||||
                        'YResolution': i32(data[8:12]) / 65536,
 | 
			
		||||
                        'DisplayedUnitsY': i16(data[12:]),
 | 
			
		||||
                        "XResolution": i32(data[:4]) / 65536,
 | 
			
		||||
                        "DisplayedUnitsX": i16(data[4:8]),
 | 
			
		||||
                        "YResolution": i32(data[8:12]) / 65536,
 | 
			
		||||
                        "DisplayedUnitsY": i16(data[12:]),
 | 
			
		||||
                    }
 | 
			
		||||
                photoshop[code] = data
 | 
			
		||||
                offset = offset + size
 | 
			
		||||
| 
						 | 
				
			
			@ -177,7 +178,7 @@ def APP(self, marker):
 | 
			
		|||
def COM(self, marker):
 | 
			
		||||
    #
 | 
			
		||||
    # Comment marker.  Store these in the APP dictionary.
 | 
			
		||||
    n = i16(self.fp.read(2))-2
 | 
			
		||||
    n = i16(self.fp.read(2)) - 2
 | 
			
		||||
    s = ImageFile._safe_read(self.fp, n)
 | 
			
		||||
 | 
			
		||||
    self.app["COM"] = s  # compatibility
 | 
			
		||||
| 
						 | 
				
			
			@ -192,7 +193,7 @@ def SOF(self, marker):
 | 
			
		|||
    # mode.  Note that this could be made a bit brighter, by
 | 
			
		||||
    # looking for JFIF and Adobe APP markers.
 | 
			
		||||
 | 
			
		||||
    n = i16(self.fp.read(2))-2
 | 
			
		||||
    n = i16(self.fp.read(2)) - 2
 | 
			
		||||
    s = ImageFile._safe_read(self.fp, n)
 | 
			
		||||
    self._size = i16(s[3:]), i16(s[1:])
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -227,9 +228,9 @@ def SOF(self, marker):
 | 
			
		|||
        self.icclist = None
 | 
			
		||||
 | 
			
		||||
    for i in range(6, len(s), 3):
 | 
			
		||||
        t = s[i:i+3]
 | 
			
		||||
        t = s[i : i + 3]
 | 
			
		||||
        # 4-tuples: id, vsamp, hsamp, qtable
 | 
			
		||||
        self.layer.append((t[0], i8(t[1])//16, i8(t[1]) & 15, i8(t[2])))
 | 
			
		||||
        self.layer.append((t[0], i8(t[1]) // 16, i8(t[1]) & 15, i8(t[2])))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def DQT(self, marker):
 | 
			
		||||
| 
						 | 
				
			
			@ -241,13 +242,13 @@ def DQT(self, marker):
 | 
			
		|||
    # FIXME: The quantization tables can be used to estimate the
 | 
			
		||||
    # compression quality.
 | 
			
		||||
 | 
			
		||||
    n = i16(self.fp.read(2))-2
 | 
			
		||||
    n = i16(self.fp.read(2)) - 2
 | 
			
		||||
    s = ImageFile._safe_read(self.fp, n)
 | 
			
		||||
    while len(s):
 | 
			
		||||
        if len(s) < 65:
 | 
			
		||||
            raise SyntaxError("bad quantization table marker")
 | 
			
		||||
        v = i8(s[0])
 | 
			
		||||
        if v//16 == 0:
 | 
			
		||||
        if v // 16 == 0:
 | 
			
		||||
            self.quantization[v & 15] = array.array("B", s[1:65])
 | 
			
		||||
            s = s[65:]
 | 
			
		||||
        else:
 | 
			
		||||
| 
						 | 
				
			
			@ -321,7 +322,7 @@ MARKER = {
 | 
			
		|||
    0xFFFB: ("JPG11", "Extension 11", None),
 | 
			
		||||
    0xFFFC: ("JPG12", "Extension 12", None),
 | 
			
		||||
    0xFFFD: ("JPG13", "Extension 13", None),
 | 
			
		||||
    0xFFFE: ("COM", "Comment", COM)
 | 
			
		||||
    0xFFFE: ("COM", "Comment", COM),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -332,6 +333,7 @@ def _accept(prefix):
 | 
			
		|||
##
 | 
			
		||||
# Image plugin for JPEG and JFIF images.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class JpegImageFile(ImageFile.ImageFile):
 | 
			
		||||
 | 
			
		||||
    format = "JPEG"
 | 
			
		||||
| 
						 | 
				
			
			@ -375,8 +377,7 @@ class JpegImageFile(ImageFile.ImageFile):
 | 
			
		|||
                    rawmode = self.mode
 | 
			
		||||
                    if self.mode == "CMYK":
 | 
			
		||||
                        rawmode = "CMYK;I"  # assume adobe conventions
 | 
			
		||||
                    self.tile = [("jpeg", (0, 0) + self.size, 0,
 | 
			
		||||
                                 (rawmode, ""))]
 | 
			
		||||
                    self.tile = [("jpeg", (0, 0) + self.size, 0, (rawmode, ""))]
 | 
			
		||||
                    # self.__offset = self.fp.tell()
 | 
			
		||||
                    break
 | 
			
		||||
                s = self.fp.read(1)
 | 
			
		||||
| 
						 | 
				
			
			@ -424,8 +425,13 @@ class JpegImageFile(ImageFile.ImageFile):
 | 
			
		|||
            for s in [8, 4, 2, 1]:
 | 
			
		||||
                if scale >= s:
 | 
			
		||||
                    break
 | 
			
		||||
            e = e[0], e[1], (e[2]-e[0]+s-1)//s+e[0], (e[3]-e[1]+s-1)//s+e[1]
 | 
			
		||||
            self._size = ((self.size[0]+s-1)//s, (self.size[1]+s-1)//s)
 | 
			
		||||
            e = (
 | 
			
		||||
                e[0],
 | 
			
		||||
                e[1],
 | 
			
		||||
                (e[2] - e[0] + s - 1) // s + e[0],
 | 
			
		||||
                (e[3] - e[1] + s - 1) // s + e[1],
 | 
			
		||||
            )
 | 
			
		||||
            self._size = ((self.size[0] + s - 1) // s, (self.size[1] + s - 1) // s)
 | 
			
		||||
            scale = s
 | 
			
		||||
 | 
			
		||||
        self.tile = [(d, e, o, a)]
 | 
			
		||||
| 
						 | 
				
			
			@ -440,6 +446,7 @@ class JpegImageFile(ImageFile.ImageFile):
 | 
			
		|||
        import subprocess
 | 
			
		||||
        import tempfile
 | 
			
		||||
        import os
 | 
			
		||||
 | 
			
		||||
        f, path = tempfile.mkstemp()
 | 
			
		||||
        os.close(f)
 | 
			
		||||
        if os.path.exists(self.filename):
 | 
			
		||||
| 
						 | 
				
			
			@ -505,7 +512,7 @@ def _getmp(self):
 | 
			
		|||
        return None
 | 
			
		||||
    file_contents = io.BytesIO(data)
 | 
			
		||||
    head = file_contents.read(8)
 | 
			
		||||
    endianness = '>' if head[:4] == b'\x4d\x4d\x00\x2a' else '<'
 | 
			
		||||
    endianness = ">" if head[:4] == b"\x4d\x4d\x00\x2a" else "<"
 | 
			
		||||
    # process dictionary
 | 
			
		||||
    try:
 | 
			
		||||
        info = TiffImagePlugin.ImageFileDirectory_v2(head)
 | 
			
		||||
| 
						 | 
				
			
			@ -525,37 +532,33 @@ def _getmp(self):
 | 
			
		|||
        rawmpentries = mp[0xB002]
 | 
			
		||||
        for entrynum in range(0, quant):
 | 
			
		||||
            unpackedentry = struct.unpack_from(
 | 
			
		||||
                '{}LLLHH'.format(endianness), rawmpentries, entrynum * 16)
 | 
			
		||||
            labels = ('Attribute', 'Size', 'DataOffset', 'EntryNo1',
 | 
			
		||||
                      'EntryNo2')
 | 
			
		||||
                "{}LLLHH".format(endianness), rawmpentries, entrynum * 16
 | 
			
		||||
            )
 | 
			
		||||
            labels = ("Attribute", "Size", "DataOffset", "EntryNo1", "EntryNo2")
 | 
			
		||||
            mpentry = dict(zip(labels, unpackedentry))
 | 
			
		||||
            mpentryattr = {
 | 
			
		||||
                'DependentParentImageFlag': bool(mpentry['Attribute'] &
 | 
			
		||||
                                                 (1 << 31)),
 | 
			
		||||
                'DependentChildImageFlag': bool(mpentry['Attribute'] &
 | 
			
		||||
                                                (1 << 30)),
 | 
			
		||||
                'RepresentativeImageFlag': bool(mpentry['Attribute'] &
 | 
			
		||||
                                                (1 << 29)),
 | 
			
		||||
                'Reserved': (mpentry['Attribute'] & (3 << 27)) >> 27,
 | 
			
		||||
                'ImageDataFormat': (mpentry['Attribute'] & (7 << 24)) >> 24,
 | 
			
		||||
                'MPType': mpentry['Attribute'] & 0x00FFFFFF
 | 
			
		||||
                "DependentParentImageFlag": bool(mpentry["Attribute"] & (1 << 31)),
 | 
			
		||||
                "DependentChildImageFlag": bool(mpentry["Attribute"] & (1 << 30)),
 | 
			
		||||
                "RepresentativeImageFlag": bool(mpentry["Attribute"] & (1 << 29)),
 | 
			
		||||
                "Reserved": (mpentry["Attribute"] & (3 << 27)) >> 27,
 | 
			
		||||
                "ImageDataFormat": (mpentry["Attribute"] & (7 << 24)) >> 24,
 | 
			
		||||
                "MPType": mpentry["Attribute"] & 0x00FFFFFF,
 | 
			
		||||
            }
 | 
			
		||||
            if mpentryattr['ImageDataFormat'] == 0:
 | 
			
		||||
                mpentryattr['ImageDataFormat'] = 'JPEG'
 | 
			
		||||
            if mpentryattr["ImageDataFormat"] == 0:
 | 
			
		||||
                mpentryattr["ImageDataFormat"] = "JPEG"
 | 
			
		||||
            else:
 | 
			
		||||
                raise SyntaxError("unsupported picture format in MPO")
 | 
			
		||||
            mptypemap = {
 | 
			
		||||
                0x000000: 'Undefined',
 | 
			
		||||
                0x010001: 'Large Thumbnail (VGA Equivalent)',
 | 
			
		||||
                0x010002: 'Large Thumbnail (Full HD Equivalent)',
 | 
			
		||||
                0x020001: 'Multi-Frame Image (Panorama)',
 | 
			
		||||
                0x020002: 'Multi-Frame Image: (Disparity)',
 | 
			
		||||
                0x020003: 'Multi-Frame Image: (Multi-Angle)',
 | 
			
		||||
                0x030000: 'Baseline MP Primary Image'
 | 
			
		||||
                0x000000: "Undefined",
 | 
			
		||||
                0x010001: "Large Thumbnail (VGA Equivalent)",
 | 
			
		||||
                0x010002: "Large Thumbnail (Full HD Equivalent)",
 | 
			
		||||
                0x020001: "Multi-Frame Image (Panorama)",
 | 
			
		||||
                0x020002: "Multi-Frame Image: (Disparity)",
 | 
			
		||||
                0x020003: "Multi-Frame Image: (Multi-Angle)",
 | 
			
		||||
                0x030000: "Baseline MP Primary Image",
 | 
			
		||||
            }
 | 
			
		||||
            mpentryattr['MPType'] = mptypemap.get(mpentryattr['MPType'],
 | 
			
		||||
                                                  'Unknown')
 | 
			
		||||
            mpentry['Attribute'] = mpentryattr
 | 
			
		||||
            mpentryattr["MPType"] = mptypemap.get(mpentryattr["MPType"], "Unknown")
 | 
			
		||||
            mpentry["Attribute"] = mpentryattr
 | 
			
		||||
            mpentries.append(mpentry)
 | 
			
		||||
        mp[0xB002] = mpentries
 | 
			
		||||
    except KeyError:
 | 
			
		||||
| 
						 | 
				
			
			@ -578,19 +581,24 @@ RAWMODE = {
 | 
			
		|||
    "YCbCr": "YCbCr",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
zigzag_index = (0,  1,  5,  6, 14, 15, 27, 28,  # noqa: E128
 | 
			
		||||
# fmt: off
 | 
			
		||||
zigzag_index = (
 | 
			
		||||
    0,  1,  5,  6, 14, 15, 27, 28,
 | 
			
		||||
    2,  4,  7, 13, 16, 26, 29, 42,
 | 
			
		||||
    3,  8, 12, 17, 25, 30, 41, 43,
 | 
			
		||||
    9, 11, 18, 24, 31, 40, 44, 53,
 | 
			
		||||
    10, 19, 23, 32, 39, 45, 52, 54,
 | 
			
		||||
    20, 22, 33, 38, 46, 51, 55, 60,
 | 
			
		||||
    21, 34, 37, 47, 50, 56, 59, 61,
 | 
			
		||||
               35, 36, 48, 49, 57, 58, 62, 63)
 | 
			
		||||
    35, 36, 48, 49, 57, 58, 62, 63,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
samplings = {(1, 1, 1, 1, 1, 1): 0,
 | 
			
		||||
samplings = {
 | 
			
		||||
    (1, 1, 1, 1, 1, 1): 0,
 | 
			
		||||
    (2, 1, 1, 1, 1, 1): 1,
 | 
			
		||||
    (2, 2, 1, 1, 1, 1): 2,
 | 
			
		||||
             }
 | 
			
		||||
}
 | 
			
		||||
# fmt: on
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def convert_dict_qtables(qtables):
 | 
			
		||||
| 
						 | 
				
			
			@ -608,7 +616,7 @@ def get_sampling(im):
 | 
			
		|||
    # NOTE: currently Pillow can't encode JPEG to YCCK format.
 | 
			
		||||
    # If YCCK support is added in the future, subsampling code will have
 | 
			
		||||
    # to be updated (here and in JpegEncode.c) to deal with 4 layers.
 | 
			
		||||
    if not hasattr(im, 'layers') or im.layers in (1, 4):
 | 
			
		||||
    if not hasattr(im, "layers") or im.layers in (1, 4):
 | 
			
		||||
        return -1
 | 
			
		||||
    sampling = im.layer[0][1:3] + im.layer[1][1:3] + im.layer[2][1:3]
 | 
			
		||||
    return samplings.get(sampling, -1)
 | 
			
		||||
| 
						 | 
				
			
			@ -636,15 +644,15 @@ def _save(im, fp, filename):
 | 
			
		|||
    elif quality in presets:
 | 
			
		||||
        preset = presets[quality]
 | 
			
		||||
        quality = 0
 | 
			
		||||
        subsampling = preset.get('subsampling', -1)
 | 
			
		||||
        qtables = preset.get('quantization')
 | 
			
		||||
        subsampling = preset.get("subsampling", -1)
 | 
			
		||||
        qtables = preset.get("quantization")
 | 
			
		||||
    elif not isinstance(quality, int):
 | 
			
		||||
        raise ValueError("Invalid quality setting")
 | 
			
		||||
    else:
 | 
			
		||||
        if subsampling in presets:
 | 
			
		||||
            subsampling = presets[subsampling].get('subsampling', -1)
 | 
			
		||||
            subsampling = presets[subsampling].get("subsampling", -1)
 | 
			
		||||
        if isStringType(qtables) and qtables in presets:
 | 
			
		||||
            qtables = presets[qtables].get('quantization')
 | 
			
		||||
            qtables = presets[qtables].get("quantization")
 | 
			
		||||
 | 
			
		||||
    if subsampling == "4:4:4":
 | 
			
		||||
        subsampling = 0
 | 
			
		||||
| 
						 | 
				
			
			@ -658,8 +666,7 @@ def _save(im, fp, filename):
 | 
			
		|||
        subsampling = 2
 | 
			
		||||
    elif subsampling == "keep":
 | 
			
		||||
        if im.format != "JPEG":
 | 
			
		||||
            raise ValueError(
 | 
			
		||||
                "Cannot use 'keep' when original image is not a JPEG")
 | 
			
		||||
            raise ValueError("Cannot use 'keep' when original image is not a JPEG")
 | 
			
		||||
        subsampling = get_sampling(im)
 | 
			
		||||
 | 
			
		||||
    def validate_qtables(qtables):
 | 
			
		||||
| 
						 | 
				
			
			@ -667,12 +674,15 @@ def _save(im, fp, filename):
 | 
			
		|||
            return qtables
 | 
			
		||||
        if isStringType(qtables):
 | 
			
		||||
            try:
 | 
			
		||||
                lines = [int(num) for line in qtables.splitlines()
 | 
			
		||||
                         for num in line.split('#', 1)[0].split()]
 | 
			
		||||
                lines = [
 | 
			
		||||
                    int(num)
 | 
			
		||||
                    for line in qtables.splitlines()
 | 
			
		||||
                    for num in line.split("#", 1)[0].split()
 | 
			
		||||
                ]
 | 
			
		||||
            except ValueError:
 | 
			
		||||
                raise ValueError("Invalid quantization table")
 | 
			
		||||
            else:
 | 
			
		||||
                qtables = [lines[s:s+64] for s in range(0, len(lines), 64)]
 | 
			
		||||
                qtables = [lines[s : s + 64] for s in range(0, len(lines), 64)]
 | 
			
		||||
        if isinstance(qtables, (tuple, list, dict)):
 | 
			
		||||
            if isinstance(qtables, dict):
 | 
			
		||||
                qtables = convert_dict_qtables(qtables)
 | 
			
		||||
| 
						 | 
				
			
			@ -684,7 +694,7 @@ def _save(im, fp, filename):
 | 
			
		|||
                try:
 | 
			
		||||
                    if len(table) != 64:
 | 
			
		||||
                        raise TypeError
 | 
			
		||||
                    table = array.array('B', table)
 | 
			
		||||
                    table = array.array("B", table)
 | 
			
		||||
                except TypeError:
 | 
			
		||||
                    raise ValueError("Invalid quantization table")
 | 
			
		||||
                else:
 | 
			
		||||
| 
						 | 
				
			
			@ -693,8 +703,7 @@ def _save(im, fp, filename):
 | 
			
		|||
 | 
			
		||||
    if qtables == "keep":
 | 
			
		||||
        if im.format != "JPEG":
 | 
			
		||||
            raise ValueError(
 | 
			
		||||
                "Cannot use 'keep' when original image is not a JPEG")
 | 
			
		||||
            raise ValueError("Cannot use 'keep' when original image is not a JPEG")
 | 
			
		||||
        qtables = getattr(im, "quantization", None)
 | 
			
		||||
    qtables = validate_qtables(qtables)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -712,15 +721,20 @@ def _save(im, fp, filename):
 | 
			
		|||
        i = 1
 | 
			
		||||
        for marker in markers:
 | 
			
		||||
            size = struct.pack(">H", 2 + ICC_OVERHEAD_LEN + len(marker))
 | 
			
		||||
            extra += (b"\xFF\xE2" + size + b"ICC_PROFILE\0" + o8(i) +
 | 
			
		||||
                      o8(len(markers)) + marker)
 | 
			
		||||
            extra += (
 | 
			
		||||
                b"\xFF\xE2"
 | 
			
		||||
                + size
 | 
			
		||||
                + b"ICC_PROFILE\0"
 | 
			
		||||
                + o8(i)
 | 
			
		||||
                + o8(len(markers))
 | 
			
		||||
                + marker
 | 
			
		||||
            )
 | 
			
		||||
            i += 1
 | 
			
		||||
 | 
			
		||||
    # "progressive" is the official name, but older documentation
 | 
			
		||||
    # says "progression"
 | 
			
		||||
    # FIXME: issue a warning if the wrong form is used (post-1.1.7)
 | 
			
		||||
    progressive = (info.get("progressive", False) or
 | 
			
		||||
                   info.get("progression", False))
 | 
			
		||||
    progressive = info.get("progressive", False) or info.get("progression", False)
 | 
			
		||||
 | 
			
		||||
    optimize = info.get("optimize", False)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -735,11 +749,12 @@ def _save(im, fp, filename):
 | 
			
		|||
        info.get("smooth", 0),
 | 
			
		||||
        optimize,
 | 
			
		||||
        info.get("streamtype", 0),
 | 
			
		||||
        dpi[0], dpi[1],
 | 
			
		||||
        dpi[0],
 | 
			
		||||
        dpi[1],
 | 
			
		||||
        subsampling,
 | 
			
		||||
        qtables,
 | 
			
		||||
        extra,
 | 
			
		||||
        exif
 | 
			
		||||
        exif,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # if we optimize, libjpeg needs a buffer big enough to hold the whole image
 | 
			
		||||
| 
						 | 
				
			
			@ -749,7 +764,7 @@ def _save(im, fp, filename):
 | 
			
		|||
    bufsize = 0
 | 
			
		||||
    if optimize or progressive:
 | 
			
		||||
        # CMYK can be bigger
 | 
			
		||||
        if im.mode == 'CMYK':
 | 
			
		||||
        if im.mode == "CMYK":
 | 
			
		||||
            bufsize = 4 * im.size[0] * im.size[1]
 | 
			
		||||
        # keep sets quality to 0, but the actual value may be high.
 | 
			
		||||
        elif quality >= 95 or quality == 0:
 | 
			
		||||
| 
						 | 
				
			
			@ -759,16 +774,16 @@ def _save(im, fp, filename):
 | 
			
		|||
 | 
			
		||||
    # The EXIF info needs to be written as one block, + APP1, + one spare byte.
 | 
			
		||||
    # Ensure that our buffer is big enough. Same with the icc_profile block.
 | 
			
		||||
    bufsize = max(ImageFile.MAXBLOCK, bufsize, len(exif) + 5,
 | 
			
		||||
                  len(extra) + 1)
 | 
			
		||||
    bufsize = max(ImageFile.MAXBLOCK, bufsize, len(exif) + 5, len(extra) + 1)
 | 
			
		||||
 | 
			
		||||
    ImageFile._save(im, fp, [("jpeg", (0, 0)+im.size, 0, rawmode)], bufsize)
 | 
			
		||||
    ImageFile._save(im, fp, [("jpeg", (0, 0) + im.size, 0, rawmode)], bufsize)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _save_cjpeg(im, fp, filename):
 | 
			
		||||
    # ALTERNATIVE: handle JPEGs via the IJG command line utilities.
 | 
			
		||||
    import os
 | 
			
		||||
    import subprocess
 | 
			
		||||
 | 
			
		||||
    tempfile = im._dump()
 | 
			
		||||
    subprocess.check_call(["cjpeg", "-outfile", filename, tempfile])
 | 
			
		||||
    try:
 | 
			
		||||
| 
						 | 
				
			
			@ -786,14 +801,17 @@ def jpeg_factory(fp=None, filename=None):
 | 
			
		|||
        if mpheader[45057] > 1:
 | 
			
		||||
            # It's actually an MPO
 | 
			
		||||
            from .MpoImagePlugin import MpoImageFile
 | 
			
		||||
 | 
			
		||||
            # Don't reload everything, just convert it.
 | 
			
		||||
            im = MpoImageFile.adopt(im, mpheader)
 | 
			
		||||
    except (TypeError, IndexError):
 | 
			
		||||
        # It is really a JPEG
 | 
			
		||||
        pass
 | 
			
		||||
    except SyntaxError:
 | 
			
		||||
        warnings.warn("Image appears to be a malformed MPO file, it will be "
 | 
			
		||||
                      "interpreted as a base JPEG file")
 | 
			
		||||
        warnings.warn(
 | 
			
		||||
            "Image appears to be a malformed MPO file, it will be "
 | 
			
		||||
            "interpreted as a base JPEG file"
 | 
			
		||||
        )
 | 
			
		||||
    return im
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -803,7 +821,6 @@ def jpeg_factory(fp=None, filename=None):
 | 
			
		|||
Image.register_open(JpegImageFile.format, jpeg_factory, _accept)
 | 
			
		||||
Image.register_save(JpegImageFile.format, _save)
 | 
			
		||||
 | 
			
		||||
Image.register_extensions(JpegImageFile.format,
 | 
			
		||||
                          [".jfif", ".jpe", ".jpg", ".jpeg"])
 | 
			
		||||
Image.register_extensions(JpegImageFile.format, [".jfif", ".jpe", ".jpg", ".jpeg"])
 | 
			
		||||
 | 
			
		||||
Image.register_mime(JpegImageFile.format, "image/jpeg")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -67,6 +67,7 @@ https://web.archive.org/web/20120328125543/http://www.jpegcameras.com/libjpeg/li
 | 
			
		|||
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
# fmt: off
 | 
			
		||||
presets = {  # noqa: E128
 | 
			
		||||
            'web_low':      {'subsampling':  2,  # "4:2:0"
 | 
			
		||||
                             'quantization': [
 | 
			
		||||
| 
						 | 
				
			
			@ -240,3 +241,4 @@ presets = {  # noqa: E128
 | 
			
		|||
                                15, 12, 12, 12, 12, 12, 12, 12]
 | 
			
		||||
                             ]},
 | 
			
		||||
}
 | 
			
		||||
# fmt: on
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,6 +31,7 @@ def _accept(s):
 | 
			
		|||
##
 | 
			
		||||
# Image plugin for McIdas area images.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class McIdasImageFile(ImageFile.ImageFile):
 | 
			
		||||
 | 
			
		||||
    format = "MCIDAS"
 | 
			
		||||
| 
						 | 
				
			
			@ -64,7 +65,7 @@ class McIdasImageFile(ImageFile.ImageFile):
 | 
			
		|||
        self._size = w[10], w[9]
 | 
			
		||||
 | 
			
		||||
        offset = w[34] + w[15]
 | 
			
		||||
        stride = w[15] + w[10]*w[11]*w[14]
 | 
			
		||||
        stride = w[15] + w[10] * w[11] * w[14]
 | 
			
		||||
 | 
			
		||||
        self.tile = [("raw", (0, 0) + self.size, offset, (rawmode, stride, 1))]
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,6 +37,7 @@ def _accept(prefix):
 | 
			
		|||
##
 | 
			
		||||
# Image plugin for Microsoft's Image Composer file format.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MicImageFile(TiffImagePlugin.TiffImageFile):
 | 
			
		||||
 | 
			
		||||
    format = "MIC"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,8 +25,8 @@ __version__ = "0.1"
 | 
			
		|||
#
 | 
			
		||||
# Bitstream parser
 | 
			
		||||
 | 
			
		||||
class BitStream(object):
 | 
			
		||||
 | 
			
		||||
class BitStream(object):
 | 
			
		||||
    def __init__(self, fp):
 | 
			
		||||
        self.fp = fp
 | 
			
		||||
        self.bits = 0
 | 
			
		||||
| 
						 | 
				
			
			@ -61,6 +61,7 @@ class BitStream(object):
 | 
			
		|||
# Image plugin for MPEG streams.  This plugin can identify a stream,
 | 
			
		||||
# but it cannot read it.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MpegImageFile(ImageFile.ImageFile):
 | 
			
		||||
 | 
			
		||||
    format = "MPEG"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,6 +38,7 @@ def _save(im, fp, filename):
 | 
			
		|||
##
 | 
			
		||||
# Image plugin for MPO images.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MpoImageFile(JpegImagePlugin.JpegImageFile):
 | 
			
		||||
 | 
			
		||||
    format = "MPO"
 | 
			
		||||
| 
						 | 
				
			
			@ -52,13 +53,14 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
 | 
			
		|||
    def _after_jpeg_open(self, mpheader=None):
 | 
			
		||||
        self.mpinfo = mpheader if mpheader is not None else self._getmp()
 | 
			
		||||
        self.__framecount = self.mpinfo[0xB001]
 | 
			
		||||
        self.__mpoffsets = [mpent['DataOffset'] + self.info['mpoffset']
 | 
			
		||||
                            for mpent in self.mpinfo[0xB002]]
 | 
			
		||||
        self.__mpoffsets = [
 | 
			
		||||
            mpent["DataOffset"] + self.info["mpoffset"] for mpent in self.mpinfo[0xB002]
 | 
			
		||||
        ]
 | 
			
		||||
        self.__mpoffsets[0] = 0
 | 
			
		||||
        # Note that the following assertion will only be invalid if something
 | 
			
		||||
        # gets broken within JpegImagePlugin.
 | 
			
		||||
        assert self.__framecount == len(self.__mpoffsets)
 | 
			
		||||
        del self.info['mpoffset']  # no longer needed
 | 
			
		||||
        del self.info["mpoffset"]  # no longer needed
 | 
			
		||||
        self.__fp = self.fp  # FIXME: hack
 | 
			
		||||
        self.__fp.seek(self.__mpoffsets[0])  # get ready to read first frame
 | 
			
		||||
        self.__frame = 0
 | 
			
		||||
| 
						 | 
				
			
			@ -87,7 +89,7 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
 | 
			
		|||
        if "parsed_exif" in self.info:
 | 
			
		||||
            del self.info["parsed_exif"]
 | 
			
		||||
        if i16(self.fp.read(2)) == 0xFFE1:  # APP1
 | 
			
		||||
            n = i16(self.fp.read(2))-2
 | 
			
		||||
            n = i16(self.fp.read(2)) - 2
 | 
			
		||||
            self.info["exif"] = ImageFile._safe_read(self.fp, n)
 | 
			
		||||
 | 
			
		||||
            exif = self._getexif()
 | 
			
		||||
| 
						 | 
				
			
			@ -96,9 +98,7 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
 | 
			
		|||
        elif "exif" in self.info:
 | 
			
		||||
            del self.info["exif"]
 | 
			
		||||
 | 
			
		||||
        self.tile = [
 | 
			
		||||
            ("jpeg", (0, 0) + self.size, self.offset, (self.mode, ""))
 | 
			
		||||
        ]
 | 
			
		||||
        self.tile = [("jpeg", (0, 0) + self.size, self.offset, (self.mode, ""))]
 | 
			
		||||
        self.__frame = frame
 | 
			
		||||
 | 
			
		||||
    def tell(self):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,6 +45,7 @@ def _accept(prefix):
 | 
			
		|||
# Image plugin for Windows MSP images.  This plugin supports both
 | 
			
		||||
# uncompressed (Windows 1.0).
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MspImageFile(ImageFile.ImageFile):
 | 
			
		||||
 | 
			
		||||
    format = "MSP"
 | 
			
		||||
| 
						 | 
				
			
			@ -60,7 +61,7 @@ class MspImageFile(ImageFile.ImageFile):
 | 
			
		|||
        # Header checksum
 | 
			
		||||
        checksum = 0
 | 
			
		||||
        for i in range(0, 32, 2):
 | 
			
		||||
            checksum = checksum ^ i16(s[i:i+2])
 | 
			
		||||
            checksum = checksum ^ i16(s[i : i + 2])
 | 
			
		||||
        if checksum != 0:
 | 
			
		||||
            raise SyntaxError("bad MSP checksum")
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -68,9 +69,9 @@ class MspImageFile(ImageFile.ImageFile):
 | 
			
		|||
        self._size = i16(s[4:]), i16(s[6:])
 | 
			
		||||
 | 
			
		||||
        if s[:4] == b"DanM":
 | 
			
		||||
            self.tile = [("raw", (0, 0)+self.size, 32, ("1", 0, 1))]
 | 
			
		||||
            self.tile = [("raw", (0, 0) + self.size, 32, ("1", 0, 1))]
 | 
			
		||||
        else:
 | 
			
		||||
            self.tile = [("MSP", (0, 0)+self.size, 32, None)]
 | 
			
		||||
            self.tile = [("MSP", (0, 0) + self.size, 32, None)]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MspDecoder(ImageFile.PyDecoder):
 | 
			
		||||
| 
						 | 
				
			
			@ -113,11 +114,12 @@ class MspDecoder(ImageFile.PyDecoder):
 | 
			
		|||
    def decode(self, buffer):
 | 
			
		||||
 | 
			
		||||
        img = io.BytesIO()
 | 
			
		||||
        blank_line = bytearray((0xff,)*((self.state.xsize+7)//8))
 | 
			
		||||
        blank_line = bytearray((0xFF,) * ((self.state.xsize + 7) // 8))
 | 
			
		||||
        try:
 | 
			
		||||
            self.fd.seek(32)
 | 
			
		||||
            rowmap = struct.unpack_from("<%dH" % (self.state.ysize),
 | 
			
		||||
                                        self.fd.read(self.state.ysize*2))
 | 
			
		||||
            rowmap = struct.unpack_from(
 | 
			
		||||
                "<%dH" % (self.state.ysize), self.fd.read(self.state.ysize * 2)
 | 
			
		||||
            )
 | 
			
		||||
        except struct.error:
 | 
			
		||||
            raise IOError("Truncated MSP file in row map")
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -129,8 +131,8 @@ class MspDecoder(ImageFile.PyDecoder):
 | 
			
		|||
                row = self.fd.read(rowlen)
 | 
			
		||||
                if len(row) != rowlen:
 | 
			
		||||
                    raise IOError(
 | 
			
		||||
                        "Truncated MSP file, expected %d bytes on row %s",
 | 
			
		||||
                        (rowlen, x))
 | 
			
		||||
                        "Truncated MSP file, expected %d bytes on row %s", (rowlen, x)
 | 
			
		||||
                    )
 | 
			
		||||
                idx = 0
 | 
			
		||||
                while idx < rowlen:
 | 
			
		||||
                    runtype = i8(row[idx])
 | 
			
		||||
| 
						 | 
				
			
			@ -141,7 +143,7 @@ class MspDecoder(ImageFile.PyDecoder):
 | 
			
		|||
                        idx += 2
 | 
			
		||||
                    else:
 | 
			
		||||
                        runcount = runtype
 | 
			
		||||
                        img.write(row[idx:idx+runcount])
 | 
			
		||||
                        img.write(row[idx : idx + runcount])
 | 
			
		||||
                        idx += runcount
 | 
			
		||||
 | 
			
		||||
            except struct.error:
 | 
			
		||||
| 
						 | 
				
			
			@ -152,7 +154,7 @@ class MspDecoder(ImageFile.PyDecoder):
 | 
			
		|||
        return 0, 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Image.register_decoder('MSP', MspDecoder)
 | 
			
		||||
Image.register_decoder("MSP", MspDecoder)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
| 
						 | 
				
			
			@ -183,7 +185,7 @@ def _save(im, fp, filename):
 | 
			
		|||
        fp.write(o16(h))
 | 
			
		||||
 | 
			
		||||
    # image body
 | 
			
		||||
    ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 32, ("1", 0, 1))])
 | 
			
		||||
    ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 32, ("1", 0, 1))])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,16 +38,18 @@ class PSDraw(object):
 | 
			
		|||
        if not py3 or self.fp == sys.stdout:
 | 
			
		||||
            self.fp.write(to_write)
 | 
			
		||||
        else:
 | 
			
		||||
            self.fp.write(bytes(to_write, 'UTF-8'))
 | 
			
		||||
            self.fp.write(bytes(to_write, "UTF-8"))
 | 
			
		||||
 | 
			
		||||
    def begin_document(self, id=None):
 | 
			
		||||
        """Set up printing of a document. (Write Postscript DSC header.)"""
 | 
			
		||||
        # FIXME: incomplete
 | 
			
		||||
        self._fp_write("%!PS-Adobe-3.0\n"
 | 
			
		||||
        self._fp_write(
 | 
			
		||||
            "%!PS-Adobe-3.0\n"
 | 
			
		||||
            "save\n"
 | 
			
		||||
            "/showpage { } def\n"
 | 
			
		||||
            "%%EndComments\n"
 | 
			
		||||
                       "%%BeginDocument\n")
 | 
			
		||||
            "%%BeginDocument\n"
 | 
			
		||||
        )
 | 
			
		||||
        # self._fp_write(ERROR_PS)  # debugging!
 | 
			
		||||
        self._fp_write(EDROFF_PS)
 | 
			
		||||
        self._fp_write(VDI_PS)
 | 
			
		||||
| 
						 | 
				
			
			@ -56,9 +58,7 @@ class PSDraw(object):
 | 
			
		|||
 | 
			
		||||
    def end_document(self):
 | 
			
		||||
        """Ends printing. (Write Postscript DSC footer.)"""
 | 
			
		||||
        self._fp_write("%%EndDocument\n"
 | 
			
		||||
                       "restore showpage\n"
 | 
			
		||||
                       "%%End\n")
 | 
			
		||||
        self._fp_write("%%EndDocument\nrestore showpage\n%%End\n")
 | 
			
		||||
        if hasattr(self.fp, "flush"):
 | 
			
		||||
            self.fp.flush()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -71,8 +71,7 @@ class PSDraw(object):
 | 
			
		|||
        """
 | 
			
		||||
        if font not in self.isofont:
 | 
			
		||||
            # reencode font
 | 
			
		||||
            self._fp_write("/PSDraw-%s ISOLatin1Encoding /%s E\n" %
 | 
			
		||||
                           (font, font))
 | 
			
		||||
            self._fp_write("/PSDraw-%s ISOLatin1Encoding /%s E\n" % (font, font))
 | 
			
		||||
            self.isofont[font] = 1
 | 
			
		||||
        # rough
 | 
			
		||||
        self._fp_write("/F0 %d /PSDraw-%s F\n" % (size, font))
 | 
			
		||||
| 
						 | 
				
			
			@ -142,6 +141,7 @@ class PSDraw(object):
 | 
			
		|||
        EpsImagePlugin._save(im, self.fp, None, 0)
 | 
			
		||||
        self._fp_write("\ngrestore\n")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# --------------------------------------------------------------------
 | 
			
		||||
# Postscript driver
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,6 +19,7 @@ from ._binary import o8
 | 
			
		|||
##
 | 
			
		||||
# File handler for Teragon-style palette files.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PaletteFile(object):
 | 
			
		||||
 | 
			
		||||
    rawmode = "RGB"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,6 +14,7 @@ from ._binary import o8, o16be as o16b
 | 
			
		|||
# PIL.__version__ instead.
 | 
			
		||||
__version__ = "1.0"
 | 
			
		||||
 | 
			
		||||
# fmt: off
 | 
			
		||||
_Palm8BitColormapValues = (  # noqa: E131
 | 
			
		||||
    (255, 255, 255), (255, 204, 255), (255, 153, 255), (255, 102, 255),
 | 
			
		||||
    (255,  51, 255), (255,   0, 255), (255, 255, 204), (255, 204, 204),
 | 
			
		||||
| 
						 | 
				
			
			@ -79,6 +80,7 @@ _Palm8BitColormapValues = (  # noqa: E131
 | 
			
		|||
      (0,   0,   0),   (0,   0,   0),   (0,   0,   0),   (0,   0,   0),
 | 
			
		||||
      (0,   0,   0),   (0,   0,   0),   (0,   0,   0),   (0,   0,   0),
 | 
			
		||||
      (0,   0,   0),   (0,   0,   0),   (0,   0,   0),   (0,   0,   0))
 | 
			
		||||
# fmt: on
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# so build a prototype image to be used for palette resampling
 | 
			
		||||
| 
						 | 
				
			
			@ -88,7 +90,7 @@ def build_prototype_image():
 | 
			
		|||
    palettedata = ()
 | 
			
		||||
    for colormapValue in _Palm8BitColormapValues:
 | 
			
		||||
        palettedata += colormapValue
 | 
			
		||||
    palettedata += (0, 0, 0)*(256 - len(_Palm8BitColormapValues))
 | 
			
		||||
    palettedata += (0, 0, 0) * (256 - len(_Palm8BitColormapValues))
 | 
			
		||||
    image.putpalette(palettedata)
 | 
			
		||||
    return image
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -100,17 +102,9 @@ Palm8BitColormapImage = build_prototype_image()
 | 
			
		|||
#
 | 
			
		||||
# --------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
_FLAGS = {
 | 
			
		||||
    "custom-colormap": 0x4000,
 | 
			
		||||
    "is-compressed":   0x8000,
 | 
			
		||||
    "has-transparent": 0x2000,
 | 
			
		||||
    }
 | 
			
		||||
_FLAGS = {"custom-colormap": 0x4000, "is-compressed": 0x8000, "has-transparent": 0x2000}
 | 
			
		||||
 | 
			
		||||
_COMPRESSION_TYPES = {
 | 
			
		||||
    "none":     0xFF,
 | 
			
		||||
    "rle":      0x01,
 | 
			
		||||
    "scanline": 0x00,
 | 
			
		||||
    }
 | 
			
		||||
_COMPRESSION_TYPES = {"none": 0xFF, "rle": 0x01, "scanline": 0x00}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
| 
						 | 
				
			
			@ -119,6 +113,7 @@ _COMPRESSION_TYPES = {
 | 
			
		|||
##
 | 
			
		||||
# (Internal) Image save plugin for the Palm format.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _save(im, fp, filename):
 | 
			
		||||
 | 
			
		||||
    if im.mode == "P":
 | 
			
		||||
| 
						 | 
				
			
			@ -137,13 +132,14 @@ def _save(im, fp, filename):
 | 
			
		|||
            # Palm does greyscale from white (0) to black (1)
 | 
			
		||||
            bpp = im.encoderinfo["bpp"]
 | 
			
		||||
            im = im.point(
 | 
			
		||||
                lambda x, shift=8-bpp, maxval=(1 << bpp)-1: maxval - (x >> shift))
 | 
			
		||||
                lambda x, shift=8 - bpp, maxval=(1 << bpp) - 1: maxval - (x >> shift)
 | 
			
		||||
            )
 | 
			
		||||
        elif im.info.get("bpp") in (1, 2, 4):
 | 
			
		||||
            # here we assume that even though the inherent mode is 8-bit grayscale,
 | 
			
		||||
            # only the lower bpp bits are significant.
 | 
			
		||||
            # We invert them to match the Palm.
 | 
			
		||||
            bpp = im.info["bpp"]
 | 
			
		||||
            im = im.point(lambda x, maxval=(1 << bpp)-1: maxval - (x & maxval))
 | 
			
		||||
            im = im.point(lambda x, maxval=(1 << bpp) - 1: maxval - (x & maxval))
 | 
			
		||||
        else:
 | 
			
		||||
            raise IOError("cannot write mode %s as Palm" % im.mode)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -172,7 +168,7 @@ def _save(im, fp, filename):
 | 
			
		|||
    cols = im.size[0]
 | 
			
		||||
    rows = im.size[1]
 | 
			
		||||
 | 
			
		||||
    rowbytes = int((cols + (16//bpp - 1)) / (16 // bpp)) * 2
 | 
			
		||||
    rowbytes = int((cols + (16 // bpp - 1)) / (16 // bpp)) * 2
 | 
			
		||||
    transparent_index = 0
 | 
			
		||||
    compression_type = _COMPRESSION_TYPES["none"]
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -204,20 +200,21 @@ def _save(im, fp, filename):
 | 
			
		|||
        fp.write(o16b(256))
 | 
			
		||||
        for i in range(256):
 | 
			
		||||
            fp.write(o8(i))
 | 
			
		||||
            if colormapmode == 'RGB':
 | 
			
		||||
            if colormapmode == "RGB":
 | 
			
		||||
                fp.write(
 | 
			
		||||
                    o8(colormap[3 * i]) +
 | 
			
		||||
                    o8(colormap[3 * i + 1]) +
 | 
			
		||||
                    o8(colormap[3 * i + 2]))
 | 
			
		||||
            elif colormapmode == 'RGBA':
 | 
			
		||||
                    o8(colormap[3 * i])
 | 
			
		||||
                    + o8(colormap[3 * i + 1])
 | 
			
		||||
                    + o8(colormap[3 * i + 2])
 | 
			
		||||
                )
 | 
			
		||||
            elif colormapmode == "RGBA":
 | 
			
		||||
                fp.write(
 | 
			
		||||
                    o8(colormap[4 * i]) +
 | 
			
		||||
                    o8(colormap[4 * i + 1]) +
 | 
			
		||||
                    o8(colormap[4 * i + 2]))
 | 
			
		||||
                    o8(colormap[4 * i])
 | 
			
		||||
                    + o8(colormap[4 * i + 1])
 | 
			
		||||
                    + o8(colormap[4 * i + 2])
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
    # now convert data to raw form
 | 
			
		||||
    ImageFile._save(
 | 
			
		||||
        im, fp, [("raw", (0, 0)+im.size, 0, (rawmode, rowbytes, 1))])
 | 
			
		||||
    ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, rowbytes, 1))])
 | 
			
		||||
 | 
			
		||||
    if hasattr(fp, "flush"):
 | 
			
		||||
        fp.flush()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,6 +28,7 @@ __version__ = "0.1"
 | 
			
		|||
# image from the file; higher resolutions are encoded in a proprietary
 | 
			
		||||
# encoding.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PcdImageFile(ImageFile.ImageFile):
 | 
			
		||||
 | 
			
		||||
    format = "PCD"
 | 
			
		||||
| 
						 | 
				
			
			@ -51,7 +52,7 @@ class PcdImageFile(ImageFile.ImageFile):
 | 
			
		|||
 | 
			
		||||
        self.mode = "RGB"
 | 
			
		||||
        self._size = 768, 512  # FIXME: not correct for rotated images!
 | 
			
		||||
        self.tile = [("pcd", (0, 0)+self.size, 96*2048, None)]
 | 
			
		||||
        self.tile = [("pcd", (0, 0) + self.size, 96 * 2048, None)]
 | 
			
		||||
 | 
			
		||||
    def load_end(self):
 | 
			
		||||
        if self.tile_post_rotate:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,31 +25,32 @@ from ._binary import i8, i16le as l16, i32le as l32, i16be as b16, i32be as b32
 | 
			
		|||
 | 
			
		||||
PCF_MAGIC = 0x70636601  # "\x01fcp"
 | 
			
		||||
 | 
			
		||||
PCF_PROPERTIES = (1 << 0)
 | 
			
		||||
PCF_ACCELERATORS = (1 << 1)
 | 
			
		||||
PCF_METRICS = (1 << 2)
 | 
			
		||||
PCF_BITMAPS = (1 << 3)
 | 
			
		||||
PCF_INK_METRICS = (1 << 4)
 | 
			
		||||
PCF_BDF_ENCODINGS = (1 << 5)
 | 
			
		||||
PCF_SWIDTHS = (1 << 6)
 | 
			
		||||
PCF_GLYPH_NAMES = (1 << 7)
 | 
			
		||||
PCF_BDF_ACCELERATORS = (1 << 8)
 | 
			
		||||
PCF_PROPERTIES = 1 << 0
 | 
			
		||||
PCF_ACCELERATORS = 1 << 1
 | 
			
		||||
PCF_METRICS = 1 << 2
 | 
			
		||||
PCF_BITMAPS = 1 << 3
 | 
			
		||||
PCF_INK_METRICS = 1 << 4
 | 
			
		||||
PCF_BDF_ENCODINGS = 1 << 5
 | 
			
		||||
PCF_SWIDTHS = 1 << 6
 | 
			
		||||
PCF_GLYPH_NAMES = 1 << 7
 | 
			
		||||
PCF_BDF_ACCELERATORS = 1 << 8
 | 
			
		||||
 | 
			
		||||
BYTES_PER_ROW = [
 | 
			
		||||
    lambda bits: ((bits+7) >> 3),
 | 
			
		||||
    lambda bits: ((bits+15) >> 3) & ~1,
 | 
			
		||||
    lambda bits: ((bits+31) >> 3) & ~3,
 | 
			
		||||
    lambda bits: ((bits+63) >> 3) & ~7,
 | 
			
		||||
    lambda bits: ((bits + 7) >> 3),
 | 
			
		||||
    lambda bits: ((bits + 15) >> 3) & ~1,
 | 
			
		||||
    lambda bits: ((bits + 31) >> 3) & ~3,
 | 
			
		||||
    lambda bits: ((bits + 63) >> 3) & ~7,
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def sz(s, o):
 | 
			
		||||
    return s[o:s.index(b"\0", o)]
 | 
			
		||||
    return s[o : s.index(b"\0", o)]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
# Font file plugin for the X11 PCF format.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PcfFontFile(FontFile.FontFile):
 | 
			
		||||
 | 
			
		||||
    name = "name"
 | 
			
		||||
| 
						 | 
				
			
			@ -83,7 +84,7 @@ class PcfFontFile(FontFile.FontFile):
 | 
			
		|||
            ix = encoding[ch]
 | 
			
		||||
            if ix is not None:
 | 
			
		||||
                x, y, l, r, w, a, d, f = metrics[ix]
 | 
			
		||||
                glyph = (w, 0), (l, d-y, x+l, d), (0, 0, x, y), bitmaps[ix]
 | 
			
		||||
                glyph = (w, 0), (l, d - y, x + l, d), (0, 0, x, y), bitmaps[ix]
 | 
			
		||||
                self.glyph[ch] = glyph
 | 
			
		||||
 | 
			
		||||
    def _getformat(self, tag):
 | 
			
		||||
| 
						 | 
				
			
			@ -141,7 +142,7 @@ class PcfFontFile(FontFile.FontFile):
 | 
			
		|||
 | 
			
		||||
        append = metrics.append
 | 
			
		||||
 | 
			
		||||
        if (format & 0xff00) == 0x100:
 | 
			
		||||
        if (format & 0xFF00) == 0x100:
 | 
			
		||||
 | 
			
		||||
            # "compressed" metrics
 | 
			
		||||
            for i in range(i16(fp.read(2))):
 | 
			
		||||
| 
						 | 
				
			
			@ -152,10 +153,7 @@ class PcfFontFile(FontFile.FontFile):
 | 
			
		|||
                descent = i8(fp.read(1)) - 128
 | 
			
		||||
                xsize = right - left
 | 
			
		||||
                ysize = ascent + descent
 | 
			
		||||
                append(
 | 
			
		||||
                    (xsize, ysize, left, right, width,
 | 
			
		||||
                     ascent, descent, 0)
 | 
			
		||||
                    )
 | 
			
		||||
                append((xsize, ysize, left, right, width, ascent, descent, 0))
 | 
			
		||||
 | 
			
		||||
        else:
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -169,10 +167,7 @@ class PcfFontFile(FontFile.FontFile):
 | 
			
		|||
                attributes = i16(fp.read(2))
 | 
			
		||||
                xsize = right - left
 | 
			
		||||
                ysize = ascent + descent
 | 
			
		||||
                append(
 | 
			
		||||
                    (xsize, ysize, left, right, width,
 | 
			
		||||
                     ascent, descent, attributes)
 | 
			
		||||
                    )
 | 
			
		||||
                append((xsize, ysize, left, right, width, ascent, descent, attributes))
 | 
			
		||||
 | 
			
		||||
        return metrics
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -214,10 +209,8 @@ class PcfFontFile(FontFile.FontFile):
 | 
			
		|||
 | 
			
		||||
        for i in range(nbitmaps):
 | 
			
		||||
            x, y, l, r, w, a, d, f = metrics[i]
 | 
			
		||||
            b, e = offsets[i], offsets[i+1]
 | 
			
		||||
            bitmaps.append(
 | 
			
		||||
                Image.frombytes("1", (x, y), data[b:e], "raw", mode, pad(x))
 | 
			
		||||
                )
 | 
			
		||||
            b, e = offsets[i], offsets[i + 1]
 | 
			
		||||
            bitmaps.append(Image.frombytes("1", (x, y), data[b:e], "raw", mode, pad(x)))
 | 
			
		||||
 | 
			
		||||
        return bitmaps
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -239,7 +232,7 @@ class PcfFontFile(FontFile.FontFile):
 | 
			
		|||
            encodingOffset = i16(fp.read(2))
 | 
			
		||||
            if encodingOffset != 0xFFFF:
 | 
			
		||||
                try:
 | 
			
		||||
                    encoding[i+firstCol] = encodingOffset
 | 
			
		||||
                    encoding[i + firstCol] = encodingOffset
 | 
			
		||||
                except IndexError:
 | 
			
		||||
                    break  # only load ISO-8859-1 glyphs
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -44,6 +44,7 @@ def _accept(prefix):
 | 
			
		|||
##
 | 
			
		||||
# Image plugin for Paintbrush images.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PcxImageFile(ImageFile.ImageFile):
 | 
			
		||||
 | 
			
		||||
    format = "PCX"
 | 
			
		||||
| 
						 | 
				
			
			@ -57,7 +58,7 @@ class PcxImageFile(ImageFile.ImageFile):
 | 
			
		|||
            raise SyntaxError("not a PCX file")
 | 
			
		||||
 | 
			
		||||
        # image
 | 
			
		||||
        bbox = i16(s, 4), i16(s, 6), i16(s, 8)+1, i16(s, 10)+1
 | 
			
		||||
        bbox = i16(s, 4), i16(s, 6), i16(s, 8) + 1, i16(s, 10) + 1
 | 
			
		||||
        if bbox[2] <= bbox[0] or bbox[3] <= bbox[1]:
 | 
			
		||||
            raise SyntaxError("bad PCX image size")
 | 
			
		||||
        logger.debug("BBox: %s %s %s %s", *bbox)
 | 
			
		||||
| 
						 | 
				
			
			@ -67,8 +68,13 @@ class PcxImageFile(ImageFile.ImageFile):
 | 
			
		|||
        bits = i8(s[3])
 | 
			
		||||
        planes = i8(s[65])
 | 
			
		||||
        stride = i16(s, 66)
 | 
			
		||||
        logger.debug("PCX version %s, bits %s, planes %s, stride %s",
 | 
			
		||||
                     version, bits, planes, stride)
 | 
			
		||||
        logger.debug(
 | 
			
		||||
            "PCX version %s, bits %s, planes %s, stride %s",
 | 
			
		||||
            version,
 | 
			
		||||
            bits,
 | 
			
		||||
            planes,
 | 
			
		||||
            stride,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.info["dpi"] = i16(s, 12), i16(s, 14)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -88,7 +94,7 @@ class PcxImageFile(ImageFile.ImageFile):
 | 
			
		|||
            if len(s) == 769 and i8(s[0]) == 12:
 | 
			
		||||
                # check if the palette is linear greyscale
 | 
			
		||||
                for i in range(256):
 | 
			
		||||
                    if s[i*3+1:i*3+4] != o8(i)*3:
 | 
			
		||||
                    if s[i * 3 + 1 : i * 3 + 4] != o8(i) * 3:
 | 
			
		||||
                        mode = rawmode = "P"
 | 
			
		||||
                        break
 | 
			
		||||
                if mode == "P":
 | 
			
		||||
| 
						 | 
				
			
			@ -103,13 +109,14 @@ class PcxImageFile(ImageFile.ImageFile):
 | 
			
		|||
            raise IOError("unknown PCX mode")
 | 
			
		||||
 | 
			
		||||
        self.mode = mode
 | 
			
		||||
        self._size = bbox[2]-bbox[0], bbox[3]-bbox[1]
 | 
			
		||||
        self._size = bbox[2] - bbox[0], bbox[3] - bbox[1]
 | 
			
		||||
 | 
			
		||||
        bbox = (0, 0) + self.size
 | 
			
		||||
        logger.debug("size: %sx%s", *self.size)
 | 
			
		||||
 | 
			
		||||
        self.tile = [("pcx", bbox, self.fp.tell(), (rawmode, planes * stride))]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# --------------------------------------------------------------------
 | 
			
		||||
# save PCX files
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -138,8 +145,12 @@ def _save(im, fp, filename):
 | 
			
		|||
    # Ideally it should be passed in in the state, but the bytes value
 | 
			
		||||
    # gets overwritten.
 | 
			
		||||
 | 
			
		||||
    logger.debug("PcxImagePlugin._save: xwidth: %d, bits: %d, stride: %d",
 | 
			
		||||
                 im.size[0], bits, stride)
 | 
			
		||||
    logger.debug(
 | 
			
		||||
        "PcxImagePlugin._save: xwidth: %d, bits: %d, stride: %d",
 | 
			
		||||
        im.size[0],
 | 
			
		||||
        bits,
 | 
			
		||||
        stride,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # under windows, we could determine the current screen size with
 | 
			
		||||
    # "Image.core.display_mode()[1]", but I think that's overkill...
 | 
			
		||||
| 
						 | 
				
			
			@ -150,17 +161,30 @@ def _save(im, fp, filename):
 | 
			
		|||
 | 
			
		||||
    # PCX header
 | 
			
		||||
    fp.write(
 | 
			
		||||
        o8(10) + o8(version) + o8(1) + o8(bits) + o16(0) +
 | 
			
		||||
        o16(0) + o16(im.size[0]-1) + o16(im.size[1]-1) + o16(dpi[0]) +
 | 
			
		||||
        o16(dpi[1]) + b"\0"*24 + b"\xFF"*24 + b"\0" + o8(planes) +
 | 
			
		||||
        o16(stride) + o16(1) + o16(screen[0]) + o16(screen[1]) +
 | 
			
		||||
        b"\0"*54
 | 
			
		||||
        o8(10)
 | 
			
		||||
        + o8(version)
 | 
			
		||||
        + o8(1)
 | 
			
		||||
        + o8(bits)
 | 
			
		||||
        + o16(0)
 | 
			
		||||
        + o16(0)
 | 
			
		||||
        + o16(im.size[0] - 1)
 | 
			
		||||
        + o16(im.size[1] - 1)
 | 
			
		||||
        + o16(dpi[0])
 | 
			
		||||
        + o16(dpi[1])
 | 
			
		||||
        + b"\0" * 24
 | 
			
		||||
        + b"\xFF" * 24
 | 
			
		||||
        + b"\0"
 | 
			
		||||
        + o8(planes)
 | 
			
		||||
        + o16(stride)
 | 
			
		||||
        + o16(1)
 | 
			
		||||
        + o16(screen[0])
 | 
			
		||||
        + o16(screen[1])
 | 
			
		||||
        + b"\0" * 54
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    assert fp.tell() == 128
 | 
			
		||||
 | 
			
		||||
    ImageFile._save(im, fp, [("pcx", (0, 0)+im.size, 0,
 | 
			
		||||
                              (rawmode, bits*planes))])
 | 
			
		||||
    ImageFile._save(im, fp, [("pcx", (0, 0) + im.size, 0, (rawmode, bits * planes))])
 | 
			
		||||
 | 
			
		||||
    if im.mode == "P":
 | 
			
		||||
        # colour palette
 | 
			
		||||
| 
						 | 
				
			
			@ -170,7 +194,8 @@ def _save(im, fp, filename):
 | 
			
		|||
        # greyscale palette
 | 
			
		||||
        fp.write(o8(12))
 | 
			
		||||
        for i in range(256):
 | 
			
		||||
            fp.write(o8(i)*3)
 | 
			
		||||
            fp.write(o8(i) * 3)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# --------------------------------------------------------------------
 | 
			
		||||
# registry
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -48,6 +48,7 @@ def _save_all(im, fp, filename):
 | 
			
		|||
##
 | 
			
		||||
# (Internal) Image save plugin for the PDF format.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _save(im, fp, filename, save_all=False):
 | 
			
		||||
    is_appending = im.encoderinfo.get("append", False)
 | 
			
		||||
    if is_appending:
 | 
			
		||||
| 
						 | 
				
			
			@ -58,16 +59,16 @@ def _save(im, fp, filename, save_all=False):
 | 
			
		|||
    resolution = im.encoderinfo.get("resolution", 72.0)
 | 
			
		||||
 | 
			
		||||
    info = {
 | 
			
		||||
        "title": None if is_appending else os.path.splitext(
 | 
			
		||||
                                               os.path.basename(filename)
 | 
			
		||||
                                           )[0],
 | 
			
		||||
        "title": None
 | 
			
		||||
        if is_appending
 | 
			
		||||
        else os.path.splitext(os.path.basename(filename))[0],
 | 
			
		||||
        "author": None,
 | 
			
		||||
        "subject": None,
 | 
			
		||||
        "keywords": None,
 | 
			
		||||
        "creator": None,
 | 
			
		||||
        "producer": None,
 | 
			
		||||
        "creationDate": None if is_appending else time.gmtime(),
 | 
			
		||||
        "modDate": None if is_appending else time.gmtime()
 | 
			
		||||
        "modDate": None if is_appending else time.gmtime(),
 | 
			
		||||
    }
 | 
			
		||||
    for k, default in info.items():
 | 
			
		||||
        v = im.encoderinfo.get(k) if k in im.encoderinfo else default
 | 
			
		||||
| 
						 | 
				
			
			@ -142,7 +143,7 @@ def _save(im, fp, filename, save_all=False):
 | 
			
		|||
                    PdfParser.PdfName("Indexed"),
 | 
			
		||||
                    PdfParser.PdfName("DeviceRGB"),
 | 
			
		||||
                    255,
 | 
			
		||||
                    PdfParser.PdfBinary(palette)
 | 
			
		||||
                    PdfParser.PdfBinary(palette),
 | 
			
		||||
                ]
 | 
			
		||||
                procset = "ImageI"  # indexed color
 | 
			
		||||
            elif im.mode == "RGB":
 | 
			
		||||
| 
						 | 
				
			
			@ -168,14 +169,13 @@ def _save(im, fp, filename, save_all=False):
 | 
			
		|||
                    data = im.tobytes("raw", "1")
 | 
			
		||||
                    im = Image.new("L", im.size)
 | 
			
		||||
                    im.putdata(data)
 | 
			
		||||
                ImageFile._save(im, op, [("hex", (0, 0)+im.size, 0, im.mode)])
 | 
			
		||||
                ImageFile._save(im, op, [("hex", (0, 0) + im.size, 0, im.mode)])
 | 
			
		||||
            elif filter == "DCTDecode":
 | 
			
		||||
                Image.SAVE["JPEG"](im, op, filename)
 | 
			
		||||
            elif filter == "FlateDecode":
 | 
			
		||||
                ImageFile._save(im, op, [("zip", (0, 0)+im.size, 0, im.mode)])
 | 
			
		||||
                ImageFile._save(im, op, [("zip", (0, 0) + im.size, 0, im.mode)])
 | 
			
		||||
            elif filter == "RunLengthDecode":
 | 
			
		||||
                ImageFile._save(im, op,
 | 
			
		||||
                                [("packbits", (0, 0)+im.size, 0, im.mode)])
 | 
			
		||||
                ImageFile._save(im, op, [("packbits", (0, 0) + im.size, 0, im.mode)])
 | 
			
		||||
            else:
 | 
			
		||||
                raise ValueError("unsupported PDF filter (%s)" % filter)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -184,7 +184,8 @@ def _save(im, fp, filename, save_all=False):
 | 
			
		|||
 | 
			
		||||
            width, height = im.size
 | 
			
		||||
 | 
			
		||||
            existing_pdf.write_obj(image_refs[pageNumber],
 | 
			
		||||
            existing_pdf.write_obj(
 | 
			
		||||
                image_refs[pageNumber],
 | 
			
		||||
                stream=op.getvalue(),
 | 
			
		||||
                Type=PdfParser.PdfName("XObject"),
 | 
			
		||||
                Subtype=PdfParser.PdfName("Image"),
 | 
			
		||||
| 
						 | 
				
			
			@ -193,39 +194,36 @@ def _save(im, fp, filename, save_all=False):
 | 
			
		|||
                Filter=PdfParser.PdfName(filter),
 | 
			
		||||
                BitsPerComponent=bits,
 | 
			
		||||
                DecodeParams=params,
 | 
			
		||||
                                   ColorSpace=colorspace)
 | 
			
		||||
                ColorSpace=colorspace,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            #
 | 
			
		||||
            # page
 | 
			
		||||
 | 
			
		||||
            existing_pdf.write_page(page_refs[pageNumber],
 | 
			
		||||
            existing_pdf.write_page(
 | 
			
		||||
                page_refs[pageNumber],
 | 
			
		||||
                Resources=PdfParser.PdfDict(
 | 
			
		||||
                                        ProcSet=[
 | 
			
		||||
                                            PdfParser.PdfName("PDF"),
 | 
			
		||||
                                            PdfParser.PdfName(procset)
 | 
			
		||||
                                        ],
 | 
			
		||||
                                        XObject=PdfParser.PdfDict(
 | 
			
		||||
                                            image=image_refs[pageNumber]
 | 
			
		||||
                                        )
 | 
			
		||||
                    ProcSet=[PdfParser.PdfName("PDF"), PdfParser.PdfName(procset)],
 | 
			
		||||
                    XObject=PdfParser.PdfDict(image=image_refs[pageNumber]),
 | 
			
		||||
                ),
 | 
			
		||||
                MediaBox=[
 | 
			
		||||
                    0,
 | 
			
		||||
                    0,
 | 
			
		||||
                    int(width * 72.0 / resolution),
 | 
			
		||||
                                        int(height * 72.0 / resolution)
 | 
			
		||||
                    int(height * 72.0 / resolution),
 | 
			
		||||
                ],
 | 
			
		||||
                                    Contents=contents_refs[pageNumber])
 | 
			
		||||
                Contents=contents_refs[pageNumber],
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            #
 | 
			
		||||
            # page contents
 | 
			
		||||
 | 
			
		||||
            page_contents = PdfParser.make_bytes(
 | 
			
		||||
                "q %d 0 0 %d 0 0 cm /image Do Q\n" % (
 | 
			
		||||
                    int(width * 72.0 / resolution),
 | 
			
		||||
                    int(height * 72.0 / resolution)))
 | 
			
		||||
                "q %d 0 0 %d 0 0 cm /image Do Q\n"
 | 
			
		||||
                % (int(width * 72.0 / resolution), int(height * 72.0 / resolution))
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            existing_pdf.write_obj(contents_refs[pageNumber],
 | 
			
		||||
                                   stream=page_contents)
 | 
			
		||||
            existing_pdf.write_obj(contents_refs[pageNumber], stream=page_contents)
 | 
			
		||||
 | 
			
		||||
            pageNumber += 1
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -236,6 +234,7 @@ def _save(im, fp, filename, save_all=False):
 | 
			
		|||
        fp.flush()
 | 
			
		||||
    existing_pdf.close()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# --------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,9 +15,13 @@ except ImportError:
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
if py3:  # Python 3.x
 | 
			
		||||
 | 
			
		||||
    def make_bytes(s):
 | 
			
		||||
        return s.encode("us-ascii")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
else:  # Python 2.x
 | 
			
		||||
 | 
			
		||||
    def make_bytes(s):  # pragma: no cover
 | 
			
		||||
        return s  # pragma: no cover
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -74,8 +78,8 @@ PDFDocEncoding = {
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
def decode_text(b):
 | 
			
		||||
    if b[:len(codecs.BOM_UTF16_BE)] == codecs.BOM_UTF16_BE:
 | 
			
		||||
        return b[len(codecs.BOM_UTF16_BE):].decode("utf_16_be")
 | 
			
		||||
    if b[: len(codecs.BOM_UTF16_BE)] == codecs.BOM_UTF16_BE:
 | 
			
		||||
        return b[len(codecs.BOM_UTF16_BE) :].decode("utf_16_be")
 | 
			
		||||
    elif py3:  # Python 3.x
 | 
			
		||||
        return "".join(PDFDocEncoding.get(byte, chr(byte)) for byte in b)
 | 
			
		||||
    else:  # Python 2.x
 | 
			
		||||
| 
						 | 
				
			
			@ -85,6 +89,7 @@ def decode_text(b):
 | 
			
		|||
class PdfFormatError(RuntimeError):
 | 
			
		||||
    """An error that probably indicates a syntactic or semantic error in the
 | 
			
		||||
    PDF file structure"""
 | 
			
		||||
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -93,8 +98,9 @@ def check_format_condition(condition, error_message):
 | 
			
		|||
        raise PdfFormatError(error_message)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class IndirectReference(collections.namedtuple("IndirectReferenceTuple",
 | 
			
		||||
                        ["object_id", "generation"])):
 | 
			
		||||
class IndirectReference(
 | 
			
		||||
    collections.namedtuple("IndirectReferenceTuple", ["object_id", "generation"])
 | 
			
		||||
):
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return "%s %s R" % self
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -102,9 +108,11 @@ class IndirectReference(collections.namedtuple("IndirectReferenceTuple",
 | 
			
		|||
        return self.__str__().encode("us-ascii")
 | 
			
		||||
 | 
			
		||||
    def __eq__(self, other):
 | 
			
		||||
        return other.__class__ is self.__class__ and \
 | 
			
		||||
               other.object_id == self.object_id and \
 | 
			
		||||
               other.generation == self.generation
 | 
			
		||||
        return (
 | 
			
		||||
            other.__class__ is self.__class__
 | 
			
		||||
            and other.object_id == self.object_id
 | 
			
		||||
            and other.generation == self.generation
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def __ne__(self, other):
 | 
			
		||||
        return not (self == other)
 | 
			
		||||
| 
						 | 
				
			
			@ -150,26 +158,27 @@ class XrefTable:
 | 
			
		|||
        elif key in self.deleted_entries:
 | 
			
		||||
            generation = self.deleted_entries[key]
 | 
			
		||||
        else:
 | 
			
		||||
            raise IndexError("object ID " + str(key) +
 | 
			
		||||
                             " cannot be deleted because it doesn't exist")
 | 
			
		||||
            raise IndexError(
 | 
			
		||||
                "object ID " + str(key) + " cannot be deleted because it doesn't exist"
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    def __contains__(self, key):
 | 
			
		||||
        return key in self.existing_entries or key in self.new_entries
 | 
			
		||||
 | 
			
		||||
    def __len__(self):
 | 
			
		||||
        return len(set(self.existing_entries.keys()) |
 | 
			
		||||
                   set(self.new_entries.keys()) |
 | 
			
		||||
                   set(self.deleted_entries.keys()))
 | 
			
		||||
        return len(
 | 
			
		||||
            set(self.existing_entries.keys())
 | 
			
		||||
            | set(self.new_entries.keys())
 | 
			
		||||
            | set(self.deleted_entries.keys())
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def keys(self):
 | 
			
		||||
        return (
 | 
			
		||||
            set(self.existing_entries.keys()) -
 | 
			
		||||
            set(self.deleted_entries.keys())
 | 
			
		||||
            set(self.existing_entries.keys()) - set(self.deleted_entries.keys())
 | 
			
		||||
        ) | set(self.new_entries.keys())
 | 
			
		||||
 | 
			
		||||
    def write(self, f):
 | 
			
		||||
        keys = sorted(set(self.new_entries.keys()) |
 | 
			
		||||
                      set(self.deleted_entries.keys()))
 | 
			
		||||
        keys = sorted(set(self.new_entries.keys()) | set(self.deleted_entries.keys()))
 | 
			
		||||
        deleted_keys = sorted(set(self.deleted_entries.keys()))
 | 
			
		||||
        startxref = f.tell()
 | 
			
		||||
        f.write(b"xref\n")
 | 
			
		||||
| 
						 | 
				
			
			@ -177,7 +186,7 @@ class XrefTable:
 | 
			
		|||
            # find a contiguous sequence of object IDs
 | 
			
		||||
            prev = None
 | 
			
		||||
            for index, key in enumerate(keys):
 | 
			
		||||
                if prev is None or prev+1 == key:
 | 
			
		||||
                if prev is None or prev + 1 == key:
 | 
			
		||||
                    prev = key
 | 
			
		||||
                else:
 | 
			
		||||
                    contiguous_keys = keys[:index]
 | 
			
		||||
| 
						 | 
				
			
			@ -186,25 +195,27 @@ class XrefTable:
 | 
			
		|||
            else:
 | 
			
		||||
                contiguous_keys = keys
 | 
			
		||||
                keys = None
 | 
			
		||||
            f.write(make_bytes("%d %d\n" %
 | 
			
		||||
                               (contiguous_keys[0], len(contiguous_keys))))
 | 
			
		||||
            f.write(make_bytes("%d %d\n" % (contiguous_keys[0], len(contiguous_keys))))
 | 
			
		||||
            for object_id in contiguous_keys:
 | 
			
		||||
                if object_id in self.new_entries:
 | 
			
		||||
                    f.write(make_bytes("%010d %05d n \n" %
 | 
			
		||||
                                       self.new_entries[object_id]))
 | 
			
		||||
                    f.write(make_bytes("%010d %05d n \n" % self.new_entries[object_id]))
 | 
			
		||||
                else:
 | 
			
		||||
                    this_deleted_object_id = deleted_keys.pop(0)
 | 
			
		||||
                    check_format_condition(object_id == this_deleted_object_id,
 | 
			
		||||
                                           "expected the next deleted object "
 | 
			
		||||
                                           "ID to be %s, instead found %s" %
 | 
			
		||||
                                           (object_id, this_deleted_object_id))
 | 
			
		||||
                    check_format_condition(
 | 
			
		||||
                        object_id == this_deleted_object_id,
 | 
			
		||||
                        "expected the next deleted object ID to be %s, instead found %s"
 | 
			
		||||
                        % (object_id, this_deleted_object_id),
 | 
			
		||||
                    )
 | 
			
		||||
                    try:
 | 
			
		||||
                        next_in_linked_list = deleted_keys[0]
 | 
			
		||||
                    except IndexError:
 | 
			
		||||
                        next_in_linked_list = 0
 | 
			
		||||
                    f.write(make_bytes("%010d %05d f \n" %
 | 
			
		||||
                                       (next_in_linked_list,
 | 
			
		||||
                                        self.deleted_entries[object_id])))
 | 
			
		||||
                    f.write(
 | 
			
		||||
                        make_bytes(
 | 
			
		||||
                            "%010d %05d f \n"
 | 
			
		||||
                            % (next_in_linked_list, self.deleted_entries[object_id])
 | 
			
		||||
                        )
 | 
			
		||||
                    )
 | 
			
		||||
        return startxref
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -221,8 +232,9 @@ class PdfName:
 | 
			
		|||
        return self.name.decode("us-ascii")
 | 
			
		||||
 | 
			
		||||
    def __eq__(self, other):
 | 
			
		||||
        return (isinstance(other, PdfName) and other.name == self.name) or \
 | 
			
		||||
               other == self.name
 | 
			
		||||
        return (
 | 
			
		||||
            isinstance(other, PdfName) and other.name == self.name
 | 
			
		||||
        ) or other == self.name
 | 
			
		||||
 | 
			
		||||
    def __hash__(self):
 | 
			
		||||
        return hash(self.name)
 | 
			
		||||
| 
						 | 
				
			
			@ -282,18 +294,18 @@ class PdfDict(UserDict):
 | 
			
		|||
            if value.startswith("D:"):
 | 
			
		||||
                value = value[2:]
 | 
			
		||||
 | 
			
		||||
            relationship = 'Z'
 | 
			
		||||
            relationship = "Z"
 | 
			
		||||
            if len(value) > 17:
 | 
			
		||||
                relationship = value[14]
 | 
			
		||||
                offset = int(value[15:17]) * 60
 | 
			
		||||
                if len(value) > 20:
 | 
			
		||||
                    offset += int(value[18:20])
 | 
			
		||||
 | 
			
		||||
            format = '%Y%m%d%H%M%S'[:len(value) - 2]
 | 
			
		||||
            value = time.strptime(value[:len(format)+2], format)
 | 
			
		||||
            if relationship in ['+', '-']:
 | 
			
		||||
            format = "%Y%m%d%H%M%S"[: len(value) - 2]
 | 
			
		||||
            value = time.strptime(value[: len(format) + 2], format)
 | 
			
		||||
            if relationship in ["+", "-"]:
 | 
			
		||||
                offset *= 60
 | 
			
		||||
                if relationship == '+':
 | 
			
		||||
                if relationship == "+":
 | 
			
		||||
                    offset *= -1
 | 
			
		||||
                value = time.gmtime(calendar.timegm(value) + offset)
 | 
			
		||||
        return value
 | 
			
		||||
| 
						 | 
				
			
			@ -320,9 +332,12 @@ class PdfBinary:
 | 
			
		|||
        self.data = data
 | 
			
		||||
 | 
			
		||||
    if py3:  # Python 3.x
 | 
			
		||||
 | 
			
		||||
        def __bytes__(self):
 | 
			
		||||
            return make_bytes("<%s>" % "".join("%02X" % b for b in self.data))
 | 
			
		||||
 | 
			
		||||
    else:  # Python 2.x
 | 
			
		||||
 | 
			
		||||
        def __str__(self):
 | 
			
		||||
            return "<%s>" % "".join("%02X" % ord(b) for b in self.data)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -345,8 +360,8 @@ class PdfStream:
 | 
			
		|||
            return zlib.decompress(self.buf, bufsize=int(expected_length))
 | 
			
		||||
        else:
 | 
			
		||||
            raise NotImplementedError(
 | 
			
		||||
                "stream filter %s unknown/unsupported" %
 | 
			
		||||
                repr(self.dictionary.Filter))
 | 
			
		||||
                "stream filter %s unknown/unsupported" % repr(self.dictionary.Filter)
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def pdf_repr(x):
 | 
			
		||||
| 
						 | 
				
			
			@ -361,13 +376,14 @@ def pdf_repr(x):
 | 
			
		|||
    elif isinstance(x, int):
 | 
			
		||||
        return str(x).encode("us-ascii")
 | 
			
		||||
    elif isinstance(x, time.struct_time):
 | 
			
		||||
        return b'(D:'+time.strftime('%Y%m%d%H%M%SZ', x).encode("us-ascii")+b')'
 | 
			
		||||
        return b"(D:" + time.strftime("%Y%m%d%H%M%SZ", x).encode("us-ascii") + b")"
 | 
			
		||||
    elif isinstance(x, dict):
 | 
			
		||||
        return bytes(PdfDict(x))
 | 
			
		||||
    elif isinstance(x, list):
 | 
			
		||||
        return bytes(PdfArray(x))
 | 
			
		||||
    elif ((py3 and isinstance(x, str)) or
 | 
			
		||||
          (not py3 and isinstance(x, unicode))):  # noqa: F821
 | 
			
		||||
    elif (py3 and isinstance(x, str)) or (
 | 
			
		||||
        not py3 and isinstance(x, unicode)  # noqa: F821
 | 
			
		||||
    ):
 | 
			
		||||
        return pdf_repr(encode_text(x))
 | 
			
		||||
    elif isinstance(x, bytes):
 | 
			
		||||
        # XXX escape more chars? handle binary garbage
 | 
			
		||||
| 
						 | 
				
			
			@ -385,11 +401,9 @@ class PdfParser:
 | 
			
		|||
    Supports PDF up to 1.4
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, filename=None, f=None,
 | 
			
		||||
                 buf=None, start_offset=0, mode="rb"):
 | 
			
		||||
    def __init__(self, filename=None, f=None, buf=None, start_offset=0, mode="rb"):
 | 
			
		||||
        if buf and f:
 | 
			
		||||
            raise RuntimeError(
 | 
			
		||||
                "specify buf or f or filename, but not both buf and f")
 | 
			
		||||
            raise RuntimeError("specify buf or f or filename, but not both buf and f")
 | 
			
		||||
        self.filename = filename
 | 
			
		||||
        self.buf = buf
 | 
			
		||||
        self.f = f
 | 
			
		||||
| 
						 | 
				
			
			@ -463,13 +477,13 @@ class PdfParser:
 | 
			
		|||
        self.root_ref = self.next_object_id(self.f.tell())
 | 
			
		||||
        self.pages_ref = self.next_object_id(0)
 | 
			
		||||
        self.rewrite_pages()
 | 
			
		||||
        self.write_obj(self.root_ref,
 | 
			
		||||
                       Type=PdfName(b"Catalog"),
 | 
			
		||||
                       Pages=self.pages_ref)
 | 
			
		||||
        self.write_obj(self.pages_ref,
 | 
			
		||||
        self.write_obj(self.root_ref, Type=PdfName(b"Catalog"), Pages=self.pages_ref)
 | 
			
		||||
        self.write_obj(
 | 
			
		||||
            self.pages_ref,
 | 
			
		||||
            Type=PdfName(b"Pages"),
 | 
			
		||||
            Count=len(self.pages),
 | 
			
		||||
                       Kids=self.pages)
 | 
			
		||||
            Kids=self.pages,
 | 
			
		||||
        )
 | 
			
		||||
        return self.root_ref
 | 
			
		||||
 | 
			
		||||
    def rewrite_pages(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -515,8 +529,11 @@ class PdfParser:
 | 
			
		|||
        if self.info:
 | 
			
		||||
            trailer_dict[b"Info"] = self.info_ref
 | 
			
		||||
        self.last_xref_section_offset = start_xref
 | 
			
		||||
        self.f.write(b"trailer\n" + bytes(PdfDict(trailer_dict)) +
 | 
			
		||||
                     make_bytes("\nstartxref\n%d\n%%%%EOF" % start_xref))
 | 
			
		||||
        self.f.write(
 | 
			
		||||
            b"trailer\n"
 | 
			
		||||
            + bytes(PdfDict(trailer_dict))
 | 
			
		||||
            + make_bytes("\nstartxref\n%d\n%%%%EOF" % start_xref)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def write_page(self, ref, *objs, **dict_obj):
 | 
			
		||||
        if isinstance(ref, int):
 | 
			
		||||
| 
						 | 
				
			
			@ -578,12 +595,14 @@ class PdfParser:
 | 
			
		|||
        else:
 | 
			
		||||
            self.info = PdfDict(self.read_indirect(self.info_ref))
 | 
			
		||||
        check_format_condition(b"Type" in self.root, "/Type missing in Root")
 | 
			
		||||
        check_format_condition(self.root[b"Type"] == b"Catalog",
 | 
			
		||||
                               "/Type in Root is not /Catalog")
 | 
			
		||||
        check_format_condition(
 | 
			
		||||
            self.root[b"Type"] == b"Catalog", "/Type in Root is not /Catalog"
 | 
			
		||||
        )
 | 
			
		||||
        check_format_condition(b"Pages" in self.root, "/Pages missing in Root")
 | 
			
		||||
        check_format_condition(isinstance(self.root[b"Pages"],
 | 
			
		||||
                               IndirectReference),
 | 
			
		||||
                               "/Pages in Root is not an indirect reference")
 | 
			
		||||
        check_format_condition(
 | 
			
		||||
            isinstance(self.root[b"Pages"], IndirectReference),
 | 
			
		||||
            "/Pages in Root is not an indirect reference",
 | 
			
		||||
        )
 | 
			
		||||
        self.pages_ref = self.root[b"Pages"]
 | 
			
		||||
        self.page_tree_root = self.read_indirect(self.pages_ref)
 | 
			
		||||
        self.pages = self.linearize_page_tree(self.page_tree_root)
 | 
			
		||||
| 
						 | 
				
			
			@ -611,13 +630,34 @@ class PdfParser:
 | 
			
		|||
    newline_only = br"[\r\n]+"
 | 
			
		||||
    newline = whitespace_optional + newline_only + whitespace_optional
 | 
			
		||||
    re_trailer_end = re.compile(
 | 
			
		||||
        whitespace_mandatory + br"trailer" + whitespace_optional +
 | 
			
		||||
        br"\<\<(.*\>\>)" + newline + br"startxref" + newline + br"([0-9]+)" +
 | 
			
		||||
        newline + br"%%EOF" + whitespace_optional + br"$", re.DOTALL)
 | 
			
		||||
        whitespace_mandatory
 | 
			
		||||
        + br"trailer"
 | 
			
		||||
        + whitespace_optional
 | 
			
		||||
        + br"\<\<(.*\>\>)"
 | 
			
		||||
        + newline
 | 
			
		||||
        + br"startxref"
 | 
			
		||||
        + newline
 | 
			
		||||
        + br"([0-9]+)"
 | 
			
		||||
        + newline
 | 
			
		||||
        + br"%%EOF"
 | 
			
		||||
        + whitespace_optional
 | 
			
		||||
        + br"$",
 | 
			
		||||
        re.DOTALL,
 | 
			
		||||
    )
 | 
			
		||||
    re_trailer_prev = re.compile(
 | 
			
		||||
        whitespace_optional + br"trailer" + whitespace_optional +
 | 
			
		||||
        br"\<\<(.*?\>\>)" + newline + br"startxref" + newline + br"([0-9]+)" +
 | 
			
		||||
        newline + br"%%EOF" + whitespace_optional, re.DOTALL)
 | 
			
		||||
        whitespace_optional
 | 
			
		||||
        + br"trailer"
 | 
			
		||||
        + whitespace_optional
 | 
			
		||||
        + br"\<\<(.*?\>\>)"
 | 
			
		||||
        + newline
 | 
			
		||||
        + br"startxref"
 | 
			
		||||
        + newline
 | 
			
		||||
        + br"([0-9]+)"
 | 
			
		||||
        + newline
 | 
			
		||||
        + br"%%EOF"
 | 
			
		||||
        + whitespace_optional,
 | 
			
		||||
        re.DOTALL,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def read_trailer(self):
 | 
			
		||||
        search_start_offset = len(self.buf) - 16384
 | 
			
		||||
| 
						 | 
				
			
			@ -629,7 +669,7 @@ class PdfParser:
 | 
			
		|||
        last_match = m
 | 
			
		||||
        while m:
 | 
			
		||||
            last_match = m
 | 
			
		||||
            m = self.re_trailer_end.search(self.buf, m.start()+16)
 | 
			
		||||
            m = self.re_trailer_end.search(self.buf, m.start() + 16)
 | 
			
		||||
        if not m:
 | 
			
		||||
            m = last_match
 | 
			
		||||
        trailer_data = m.group(1)
 | 
			
		||||
| 
						 | 
				
			
			@ -641,26 +681,29 @@ class PdfParser:
 | 
			
		|||
            self.read_prev_trailer(self.trailer_dict[b"Prev"])
 | 
			
		||||
 | 
			
		||||
    def read_prev_trailer(self, xref_section_offset):
 | 
			
		||||
        trailer_offset = self.read_xref_table(
 | 
			
		||||
            xref_section_offset=xref_section_offset)
 | 
			
		||||
        trailer_offset = self.read_xref_table(xref_section_offset=xref_section_offset)
 | 
			
		||||
        m = self.re_trailer_prev.search(
 | 
			
		||||
            self.buf[trailer_offset:trailer_offset+16384])
 | 
			
		||||
            self.buf[trailer_offset : trailer_offset + 16384]
 | 
			
		||||
        )
 | 
			
		||||
        check_format_condition(m, "previous trailer not found")
 | 
			
		||||
        trailer_data = m.group(1)
 | 
			
		||||
        check_format_condition(int(m.group(2)) == xref_section_offset,
 | 
			
		||||
                               "xref section offset in previous trailer "
 | 
			
		||||
                               "doesn't match what was expected")
 | 
			
		||||
        check_format_condition(
 | 
			
		||||
            int(m.group(2)) == xref_section_offset,
 | 
			
		||||
            "xref section offset in previous trailer doesn't match what was expected",
 | 
			
		||||
        )
 | 
			
		||||
        trailer_dict = self.interpret_trailer(trailer_data)
 | 
			
		||||
        if b"Prev" in trailer_dict:
 | 
			
		||||
            self.read_prev_trailer(trailer_dict[b"Prev"])
 | 
			
		||||
 | 
			
		||||
    re_whitespace_optional = re.compile(whitespace_optional)
 | 
			
		||||
    re_name = re.compile(
 | 
			
		||||
        whitespace_optional + br"/([!-$&'*-.0-;=?-Z\\^-z|~]+)(?=" +
 | 
			
		||||
        delimiter_or_ws + br")")
 | 
			
		||||
        whitespace_optional
 | 
			
		||||
        + br"/([!-$&'*-.0-;=?-Z\\^-z|~]+)(?="
 | 
			
		||||
        + delimiter_or_ws
 | 
			
		||||
        + br")"
 | 
			
		||||
    )
 | 
			
		||||
    re_dict_start = re.compile(whitespace_optional + br"\<\<")
 | 
			
		||||
    re_dict_end = re.compile(
 | 
			
		||||
        whitespace_optional + br"\>\>" + whitespace_optional)
 | 
			
		||||
    re_dict_end = re.compile(whitespace_optional + br"\>\>" + whitespace_optional)
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def interpret_trailer(cls, trailer_data):
 | 
			
		||||
| 
						 | 
				
			
			@ -672,19 +715,21 @@ class PdfParser:
 | 
			
		|||
                m = cls.re_dict_end.match(trailer_data, offset)
 | 
			
		||||
                check_format_condition(
 | 
			
		||||
                    m and m.end() == len(trailer_data),
 | 
			
		||||
                    "name not found in trailer, remaining data: " +
 | 
			
		||||
                    repr(trailer_data[offset:]))
 | 
			
		||||
                    "name not found in trailer, remaining data: "
 | 
			
		||||
                    + repr(trailer_data[offset:]),
 | 
			
		||||
                )
 | 
			
		||||
                break
 | 
			
		||||
            key = cls.interpret_name(m.group(1))
 | 
			
		||||
            value, offset = cls.get_value(trailer_data, m.end())
 | 
			
		||||
            trailer[key] = value
 | 
			
		||||
        check_format_condition(
 | 
			
		||||
            b"Size" in trailer and isinstance(trailer[b"Size"], int),
 | 
			
		||||
            "/Size not in trailer or not an integer")
 | 
			
		||||
            "/Size not in trailer or not an integer",
 | 
			
		||||
        )
 | 
			
		||||
        check_format_condition(
 | 
			
		||||
            b"Root" in trailer and
 | 
			
		||||
            isinstance(trailer[b"Root"], IndirectReference),
 | 
			
		||||
            "/Root not in trailer or not an indirect reference")
 | 
			
		||||
            b"Root" in trailer and isinstance(trailer[b"Root"], IndirectReference),
 | 
			
		||||
            "/Root not in trailer or not an indirect reference",
 | 
			
		||||
        )
 | 
			
		||||
        return trailer
 | 
			
		||||
 | 
			
		||||
    re_hashes_in_name = re.compile(br"([^#]*)(#([0-9a-fA-F]{2}))?")
 | 
			
		||||
| 
						 | 
				
			
			@ -694,8 +739,7 @@ class PdfParser:
 | 
			
		|||
        name = b""
 | 
			
		||||
        for m in cls.re_hashes_in_name.finditer(raw):
 | 
			
		||||
            if m.group(3):
 | 
			
		||||
                name += m.group(1) + \
 | 
			
		||||
                    bytearray.fromhex(m.group(3).decode("us-ascii"))
 | 
			
		||||
                name += m.group(1) + bytearray.fromhex(m.group(3).decode("us-ascii"))
 | 
			
		||||
            else:
 | 
			
		||||
                name += m.group(1)
 | 
			
		||||
        if as_text:
 | 
			
		||||
| 
						 | 
				
			
			@ -703,37 +747,54 @@ class PdfParser:
 | 
			
		|||
        else:
 | 
			
		||||
            return bytes(name)
 | 
			
		||||
 | 
			
		||||
    re_null = re.compile(
 | 
			
		||||
        whitespace_optional + br"null(?=" + delimiter_or_ws + br")")
 | 
			
		||||
    re_true = re.compile(
 | 
			
		||||
        whitespace_optional + br"true(?=" + delimiter_or_ws + br")")
 | 
			
		||||
    re_false = re.compile(
 | 
			
		||||
        whitespace_optional + br"false(?=" + delimiter_or_ws + br")")
 | 
			
		||||
    re_null = re.compile(whitespace_optional + br"null(?=" + delimiter_or_ws + br")")
 | 
			
		||||
    re_true = re.compile(whitespace_optional + br"true(?=" + delimiter_or_ws + br")")
 | 
			
		||||
    re_false = re.compile(whitespace_optional + br"false(?=" + delimiter_or_ws + br")")
 | 
			
		||||
    re_int = re.compile(
 | 
			
		||||
        whitespace_optional + br"([-+]?[0-9]+)(?=" + delimiter_or_ws + br")")
 | 
			
		||||
        whitespace_optional + br"([-+]?[0-9]+)(?=" + delimiter_or_ws + br")"
 | 
			
		||||
    )
 | 
			
		||||
    re_real = re.compile(
 | 
			
		||||
        whitespace_optional + br"([-+]?([0-9]+\.[0-9]*|[0-9]*\.[0-9]+))(?=" +
 | 
			
		||||
        delimiter_or_ws + br")")
 | 
			
		||||
        whitespace_optional
 | 
			
		||||
        + br"([-+]?([0-9]+\.[0-9]*|[0-9]*\.[0-9]+))(?="
 | 
			
		||||
        + delimiter_or_ws
 | 
			
		||||
        + br")"
 | 
			
		||||
    )
 | 
			
		||||
    re_array_start = re.compile(whitespace_optional + br"\[")
 | 
			
		||||
    re_array_end = re.compile(whitespace_optional + br"]")
 | 
			
		||||
    re_string_hex = re.compile(
 | 
			
		||||
        whitespace_optional + br"\<(" + whitespace_or_hex + br"*)\>")
 | 
			
		||||
        whitespace_optional + br"\<(" + whitespace_or_hex + br"*)\>"
 | 
			
		||||
    )
 | 
			
		||||
    re_string_lit = re.compile(whitespace_optional + br"\(")
 | 
			
		||||
    re_indirect_reference = re.compile(
 | 
			
		||||
        whitespace_optional + br"([-+]?[0-9]+)" + whitespace_mandatory +
 | 
			
		||||
        br"([-+]?[0-9]+)" + whitespace_mandatory + br"R(?=" + delimiter_or_ws +
 | 
			
		||||
        br")")
 | 
			
		||||
        whitespace_optional
 | 
			
		||||
        + br"([-+]?[0-9]+)"
 | 
			
		||||
        + whitespace_mandatory
 | 
			
		||||
        + br"([-+]?[0-9]+)"
 | 
			
		||||
        + whitespace_mandatory
 | 
			
		||||
        + br"R(?="
 | 
			
		||||
        + delimiter_or_ws
 | 
			
		||||
        + br")"
 | 
			
		||||
    )
 | 
			
		||||
    re_indirect_def_start = re.compile(
 | 
			
		||||
        whitespace_optional + br"([-+]?[0-9]+)" + whitespace_mandatory +
 | 
			
		||||
        br"([-+]?[0-9]+)" + whitespace_mandatory + br"obj(?=" +
 | 
			
		||||
        delimiter_or_ws + br")")
 | 
			
		||||
        whitespace_optional
 | 
			
		||||
        + br"([-+]?[0-9]+)"
 | 
			
		||||
        + whitespace_mandatory
 | 
			
		||||
        + br"([-+]?[0-9]+)"
 | 
			
		||||
        + whitespace_mandatory
 | 
			
		||||
        + br"obj(?="
 | 
			
		||||
        + delimiter_or_ws
 | 
			
		||||
        + br")"
 | 
			
		||||
    )
 | 
			
		||||
    re_indirect_def_end = re.compile(
 | 
			
		||||
        whitespace_optional + br"endobj(?=" + delimiter_or_ws + br")")
 | 
			
		||||
        whitespace_optional + br"endobj(?=" + delimiter_or_ws + br")"
 | 
			
		||||
    )
 | 
			
		||||
    re_comment = re.compile(
 | 
			
		||||
        br"(" + whitespace_optional + br"%[^\r\n]*" + newline + br")*")
 | 
			
		||||
        br"(" + whitespace_optional + br"%[^\r\n]*" + newline + br")*"
 | 
			
		||||
    )
 | 
			
		||||
    re_stream_start = re.compile(whitespace_optional + br"stream\r?\n")
 | 
			
		||||
    re_stream_end = re.compile(
 | 
			
		||||
        whitespace_optional + br"endstream(?=" + delimiter_or_ws + br")")
 | 
			
		||||
        whitespace_optional + br"endstream(?=" + delimiter_or_ws + br")"
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def get_value(cls, data, offset, expect_indirect=None, max_nesting=-1):
 | 
			
		||||
| 
						 | 
				
			
			@ -746,32 +807,37 @@ class PdfParser:
 | 
			
		|||
        if m:
 | 
			
		||||
            check_format_condition(
 | 
			
		||||
                int(m.group(1)) > 0,
 | 
			
		||||
                "indirect object definition: object ID must be greater than 0")
 | 
			
		||||
                "indirect object definition: object ID must be greater than 0",
 | 
			
		||||
            )
 | 
			
		||||
            check_format_condition(
 | 
			
		||||
                int(m.group(2)) >= 0,
 | 
			
		||||
                "indirect object definition: generation must be non-negative")
 | 
			
		||||
                "indirect object definition: generation must be non-negative",
 | 
			
		||||
            )
 | 
			
		||||
            check_format_condition(
 | 
			
		||||
                expect_indirect is None or expect_indirect ==
 | 
			
		||||
                IndirectReference(int(m.group(1)), int(m.group(2))),
 | 
			
		||||
                "indirect object definition different than expected")
 | 
			
		||||
            object, offset = cls.get_value(
 | 
			
		||||
                data, m.end(), max_nesting=max_nesting-1)
 | 
			
		||||
                expect_indirect is None
 | 
			
		||||
                or expect_indirect
 | 
			
		||||
                == IndirectReference(int(m.group(1)), int(m.group(2))),
 | 
			
		||||
                "indirect object definition different than expected",
 | 
			
		||||
            )
 | 
			
		||||
            object, offset = cls.get_value(data, m.end(), max_nesting=max_nesting - 1)
 | 
			
		||||
            if offset is None:
 | 
			
		||||
                return object, None
 | 
			
		||||
            m = cls.re_indirect_def_end.match(data, offset)
 | 
			
		||||
            check_format_condition(
 | 
			
		||||
                m, "indirect object definition end not found")
 | 
			
		||||
            check_format_condition(m, "indirect object definition end not found")
 | 
			
		||||
            return object, m.end()
 | 
			
		||||
        check_format_condition(
 | 
			
		||||
            not expect_indirect, "indirect object definition not found")
 | 
			
		||||
            not expect_indirect, "indirect object definition not found"
 | 
			
		||||
        )
 | 
			
		||||
        m = cls.re_indirect_reference.match(data, offset)
 | 
			
		||||
        if m:
 | 
			
		||||
            check_format_condition(
 | 
			
		||||
                int(m.group(1)) > 0,
 | 
			
		||||
                "indirect object reference: object ID must be greater than 0")
 | 
			
		||||
                "indirect object reference: object ID must be greater than 0",
 | 
			
		||||
            )
 | 
			
		||||
            check_format_condition(
 | 
			
		||||
                int(m.group(2)) >= 0,
 | 
			
		||||
                "indirect object reference: generation must be non-negative")
 | 
			
		||||
                "indirect object reference: generation must be non-negative",
 | 
			
		||||
            )
 | 
			
		||||
            return IndirectReference(int(m.group(1)), int(m.group(2))), m.end()
 | 
			
		||||
        m = cls.re_dict_start.match(data, offset)
 | 
			
		||||
        if m:
 | 
			
		||||
| 
						 | 
				
			
			@ -779,12 +845,10 @@ class PdfParser:
 | 
			
		|||
            result = {}
 | 
			
		||||
            m = cls.re_dict_end.match(data, offset)
 | 
			
		||||
            while not m:
 | 
			
		||||
                key, offset = cls.get_value(
 | 
			
		||||
                    data, offset, max_nesting=max_nesting-1)
 | 
			
		||||
                key, offset = cls.get_value(data, offset, max_nesting=max_nesting - 1)
 | 
			
		||||
                if offset is None:
 | 
			
		||||
                    return result, None
 | 
			
		||||
                value, offset = cls.get_value(
 | 
			
		||||
                    data, offset, max_nesting=max_nesting-1)
 | 
			
		||||
                value, offset = cls.get_value(data, offset, max_nesting=max_nesting - 1)
 | 
			
		||||
                result[key] = value
 | 
			
		||||
                if offset is None:
 | 
			
		||||
                    return result, None
 | 
			
		||||
| 
						 | 
				
			
			@ -796,9 +860,10 @@ class PdfParser:
 | 
			
		|||
                    stream_len = int(result[b"Length"])
 | 
			
		||||
                except (TypeError, KeyError, ValueError):
 | 
			
		||||
                    raise PdfFormatError(
 | 
			
		||||
                        "bad or missing Length in stream dict (%r)" %
 | 
			
		||||
                        result.get(b"Length", None))
 | 
			
		||||
                stream_data = data[m.end():m.end() + stream_len]
 | 
			
		||||
                        "bad or missing Length in stream dict (%r)"
 | 
			
		||||
                        % result.get(b"Length", None)
 | 
			
		||||
                    )
 | 
			
		||||
                stream_data = data[m.end() : m.end() + stream_len]
 | 
			
		||||
                m = cls.re_stream_end.match(data, m.end() + stream_len)
 | 
			
		||||
                check_format_condition(m, "stream end not found")
 | 
			
		||||
                offset = m.end()
 | 
			
		||||
| 
						 | 
				
			
			@ -812,8 +877,7 @@ class PdfParser:
 | 
			
		|||
            result = []
 | 
			
		||||
            m = cls.re_array_end.match(data, offset)
 | 
			
		||||
            while not m:
 | 
			
		||||
                value, offset = cls.get_value(
 | 
			
		||||
                    data, offset, max_nesting=max_nesting-1)
 | 
			
		||||
                value, offset = cls.get_value(data, offset, max_nesting=max_nesting - 1)
 | 
			
		||||
                result.append(value)
 | 
			
		||||
                if offset is None:
 | 
			
		||||
                    return result, None
 | 
			
		||||
| 
						 | 
				
			
			@ -841,10 +905,9 @@ class PdfParser:
 | 
			
		|||
        m = cls.re_string_hex.match(data, offset)
 | 
			
		||||
        if m:
 | 
			
		||||
            # filter out whitespace
 | 
			
		||||
            hex_string = bytearray([
 | 
			
		||||
                b for b in m.group(1)
 | 
			
		||||
                if b in b"0123456789abcdefABCDEF"
 | 
			
		||||
            ])
 | 
			
		||||
            hex_string = bytearray(
 | 
			
		||||
                [b for b in m.group(1) if b in b"0123456789abcdefABCDEF"]
 | 
			
		||||
            )
 | 
			
		||||
            if len(hex_string) % 2 == 1:
 | 
			
		||||
                # append a 0 if the length is not even - yes, at the end
 | 
			
		||||
                hex_string.append(ord(b"0"))
 | 
			
		||||
| 
						 | 
				
			
			@ -853,11 +916,11 @@ class PdfParser:
 | 
			
		|||
        if m:
 | 
			
		||||
            return cls.get_literal_string(data, m.end())
 | 
			
		||||
        # return None, offset  # fallback (only for debugging)
 | 
			
		||||
        raise PdfFormatError(
 | 
			
		||||
            "unrecognized object: " + repr(data[offset:offset+32]))
 | 
			
		||||
        raise PdfFormatError("unrecognized object: " + repr(data[offset : offset + 32]))
 | 
			
		||||
 | 
			
		||||
    re_lit_str_token = re.compile(
 | 
			
		||||
        br"(\\[nrtbf()\\])|(\\[0-9]{1,3})|(\\(\r\n|\r|\n))|(\r\n|\r|\n)|(\()|(\))")
 | 
			
		||||
        br"(\\[nrtbf()\\])|(\\[0-9]{1,3})|(\\(\r\n|\r|\n))|(\r\n|\r|\n)|(\()|(\))"
 | 
			
		||||
    )
 | 
			
		||||
    escaped_chars = {
 | 
			
		||||
        b"n": b"\n",
 | 
			
		||||
        b"r": b"\r",
 | 
			
		||||
| 
						 | 
				
			
			@ -882,7 +945,7 @@ class PdfParser:
 | 
			
		|||
        nesting_depth = 0
 | 
			
		||||
        result = bytearray()
 | 
			
		||||
        for m in cls.re_lit_str_token.finditer(data, offset):
 | 
			
		||||
            result.extend(data[offset:m.start()])
 | 
			
		||||
            result.extend(data[offset : m.start()])
 | 
			
		||||
            if m.group(1):
 | 
			
		||||
                result.extend(cls.escaped_chars[m.group(1)[1]])
 | 
			
		||||
            elif m.group(2):
 | 
			
		||||
| 
						 | 
				
			
			@ -902,30 +965,36 @@ class PdfParser:
 | 
			
		|||
            offset = m.end()
 | 
			
		||||
        raise PdfFormatError("unfinished literal string")
 | 
			
		||||
 | 
			
		||||
    re_xref_section_start = re.compile(
 | 
			
		||||
        whitespace_optional + br"xref" + newline)
 | 
			
		||||
    re_xref_section_start = re.compile(whitespace_optional + br"xref" + newline)
 | 
			
		||||
    re_xref_subsection_start = re.compile(
 | 
			
		||||
        whitespace_optional + br"([0-9]+)" + whitespace_mandatory +
 | 
			
		||||
        br"([0-9]+)" + whitespace_optional + newline_only)
 | 
			
		||||
        whitespace_optional
 | 
			
		||||
        + br"([0-9]+)"
 | 
			
		||||
        + whitespace_mandatory
 | 
			
		||||
        + br"([0-9]+)"
 | 
			
		||||
        + whitespace_optional
 | 
			
		||||
        + newline_only
 | 
			
		||||
    )
 | 
			
		||||
    re_xref_entry = re.compile(br"([0-9]{10}) ([0-9]{5}) ([fn])( \r| \n|\r\n)")
 | 
			
		||||
 | 
			
		||||
    def read_xref_table(self, xref_section_offset):
 | 
			
		||||
        subsection_found = False
 | 
			
		||||
        m = self.re_xref_section_start.match(
 | 
			
		||||
            self.buf, xref_section_offset + self.start_offset)
 | 
			
		||||
            self.buf, xref_section_offset + self.start_offset
 | 
			
		||||
        )
 | 
			
		||||
        check_format_condition(m, "xref section start not found")
 | 
			
		||||
        offset = m.end()
 | 
			
		||||
        while True:
 | 
			
		||||
            m = self.re_xref_subsection_start.match(self.buf, offset)
 | 
			
		||||
            if not m:
 | 
			
		||||
                check_format_condition(
 | 
			
		||||
                    subsection_found, "xref subsection start not found")
 | 
			
		||||
                    subsection_found, "xref subsection start not found"
 | 
			
		||||
                )
 | 
			
		||||
                break
 | 
			
		||||
            subsection_found = True
 | 
			
		||||
            offset = m.end()
 | 
			
		||||
            first_object = int(m.group(1))
 | 
			
		||||
            num_objects = int(m.group(2))
 | 
			
		||||
            for i in range(first_object, first_object+num_objects):
 | 
			
		||||
            for i in range(first_object, first_object + num_objects):
 | 
			
		||||
                m = self.re_xref_entry.match(self.buf, offset)
 | 
			
		||||
                check_format_condition(m, "xref entry not found")
 | 
			
		||||
                offset = m.end()
 | 
			
		||||
| 
						 | 
				
			
			@ -934,9 +1003,9 @@ class PdfParser:
 | 
			
		|||
                if not is_free:
 | 
			
		||||
                    new_entry = (int(m.group(1)), generation)
 | 
			
		||||
                    check_format_condition(
 | 
			
		||||
                        i not in self.xref_table or
 | 
			
		||||
                        self.xref_table[i] == new_entry,
 | 
			
		||||
                        "xref entry duplicated (and not identical)")
 | 
			
		||||
                        i not in self.xref_table or self.xref_table[i] == new_entry,
 | 
			
		||||
                        "xref entry duplicated (and not identical)",
 | 
			
		||||
                    )
 | 
			
		||||
                    self.xref_table[i] = new_entry
 | 
			
		||||
        return offset
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -946,10 +1015,14 @@ class PdfParser:
 | 
			
		|||
            generation == ref[1],
 | 
			
		||||
            "expected to find generation %s for object ID %s in xref table, "
 | 
			
		||||
            "instead found generation %s at offset %s"
 | 
			
		||||
            % (ref[1], ref[0], generation, offset))
 | 
			
		||||
        value = self.get_value(self.buf, offset + self.start_offset,
 | 
			
		||||
            % (ref[1], ref[0], generation, offset),
 | 
			
		||||
        )
 | 
			
		||||
        value = self.get_value(
 | 
			
		||||
            self.buf,
 | 
			
		||||
            offset + self.start_offset,
 | 
			
		||||
            expect_indirect=IndirectReference(*ref),
 | 
			
		||||
                               max_nesting=max_nesting)[0]
 | 
			
		||||
            max_nesting=max_nesting,
 | 
			
		||||
        )[0]
 | 
			
		||||
        self.cached_objects[ref] = value
 | 
			
		||||
        return value
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -957,7 +1030,8 @@ class PdfParser:
 | 
			
		|||
        if node is None:
 | 
			
		||||
            node = self.page_tree_root
 | 
			
		||||
        check_format_condition(
 | 
			
		||||
            node[b"Type"] == b"Pages", "/Type of page tree node is not /Pages")
 | 
			
		||||
            node[b"Type"] == b"Pages", "/Type of page tree node is not /Pages"
 | 
			
		||||
        )
 | 
			
		||||
        pages = []
 | 
			
		||||
        for kid in node[b"Kids"]:
 | 
			
		||||
            kid_object = self.read_indirect(kid)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,6 +30,7 @@ __version__ = "0.1"
 | 
			
		|||
#
 | 
			
		||||
# helpers
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _accept(prefix):
 | 
			
		||||
    return prefix[:4] == b"\200\350\000\000"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -37,6 +38,7 @@ def _accept(prefix):
 | 
			
		|||
##
 | 
			
		||||
# Image plugin for PIXAR raster images.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PixarImageFile(ImageFile.ImageFile):
 | 
			
		||||
 | 
			
		||||
    format = "PIXAR"
 | 
			
		||||
| 
						 | 
				
			
			@ -62,7 +64,7 @@ class PixarImageFile(ImageFile.ImageFile):
 | 
			
		|||
        # FIXME: to be continued...
 | 
			
		||||
 | 
			
		||||
        # create tile descriptor (assuming "dumped")
 | 
			
		||||
        self.tile = [("raw", (0, 0)+self.size, 1024, (self.mode, 0, 1))]
 | 
			
		||||
        self.tile = [("raw", (0, 0) + self.size, 1024, (self.mode, 0, 1))]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -77,7 +77,7 @@ _MODES = {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_simple_palette = re.compile(b'^\xff*\x00\xff*$')
 | 
			
		||||
_simple_palette = re.compile(b"^\xff*\x00\xff*$")
 | 
			
		||||
 | 
			
		||||
# Maximum decompressed size for a iTXt or zTXt chunk.
 | 
			
		||||
# Eliminates decompression bombs where compressed chunks can expand 1000x
 | 
			
		||||
| 
						 | 
				
			
			@ -95,14 +95,14 @@ def _safe_zlib_decompress(s):
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
def _crc32(data, seed=0):
 | 
			
		||||
    return zlib.crc32(data, seed) & 0xffffffff
 | 
			
		||||
    return zlib.crc32(data, seed) & 0xFFFFFFFF
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# --------------------------------------------------------------------
 | 
			
		||||
# Support classes.  Suitable for PNG and related formats like MNG etc.
 | 
			
		||||
 | 
			
		||||
class ChunkStream(object):
 | 
			
		||||
 | 
			
		||||
class ChunkStream(object):
 | 
			
		||||
    def __init__(self, fp):
 | 
			
		||||
 | 
			
		||||
        self.fp = fp
 | 
			
		||||
| 
						 | 
				
			
			@ -144,7 +144,7 @@ class ChunkStream(object):
 | 
			
		|||
        """Call the appropriate chunk handler"""
 | 
			
		||||
 | 
			
		||||
        logger.debug("STREAM %r %s %s", cid, pos, length)
 | 
			
		||||
        return getattr(self, "chunk_" + cid.decode('ascii'))(pos, length)
 | 
			
		||||
        return getattr(self, "chunk_" + cid.decode("ascii"))(pos, length)
 | 
			
		||||
 | 
			
		||||
    def crc(self, cid, data):
 | 
			
		||||
        """Read and verify checksum"""
 | 
			
		||||
| 
						 | 
				
			
			@ -160,11 +160,9 @@ class ChunkStream(object):
 | 
			
		|||
            crc1 = _crc32(data, _crc32(cid))
 | 
			
		||||
            crc2 = i32(self.fp.read(4))
 | 
			
		||||
            if crc1 != crc2:
 | 
			
		||||
                raise SyntaxError("broken PNG file (bad header checksum in %r)"
 | 
			
		||||
                                  % cid)
 | 
			
		||||
                raise SyntaxError("broken PNG file (bad header checksum in %r)" % cid)
 | 
			
		||||
        except struct.error:
 | 
			
		||||
            raise SyntaxError("broken PNG file (incomplete checksum in %r)"
 | 
			
		||||
                              % cid)
 | 
			
		||||
            raise SyntaxError("broken PNG file (incomplete checksum in %r)" % cid)
 | 
			
		||||
 | 
			
		||||
    def crc_skip(self, cid, data):
 | 
			
		||||
        """Read checksum.  Used if the C module is not present"""
 | 
			
		||||
| 
						 | 
				
			
			@ -198,6 +196,7 @@ class iTXt(str):
 | 
			
		|||
    keeping their extra information
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def __new__(cls, text, lang=None, tkey=None):
 | 
			
		||||
        """
 | 
			
		||||
| 
						 | 
				
			
			@ -253,11 +252,12 @@ class PngInfo(object):
 | 
			
		|||
            tkey = tkey.encode("utf-8", "strict")
 | 
			
		||||
 | 
			
		||||
        if zip:
 | 
			
		||||
            self.add(b"iTXt", key + b"\0\x01\0" + lang + b"\0" + tkey + b"\0" +
 | 
			
		||||
                     zlib.compress(value))
 | 
			
		||||
            self.add(
 | 
			
		||||
                b"iTXt",
 | 
			
		||||
                key + b"\0\x01\0" + lang + b"\0" + tkey + b"\0" + zlib.compress(value),
 | 
			
		||||
            )
 | 
			
		||||
        else:
 | 
			
		||||
            self.add(b"iTXt", key + b"\0\0\0" + lang + b"\0" + tkey + b"\0" +
 | 
			
		||||
                     value)
 | 
			
		||||
            self.add(b"iTXt", key + b"\0\0\0" + lang + b"\0" + tkey + b"\0" + value)
 | 
			
		||||
 | 
			
		||||
    def add_text(self, key, value, zip=False):
 | 
			
		||||
        """Appends a text chunk.
 | 
			
		||||
| 
						 | 
				
			
			@ -274,12 +274,12 @@ class PngInfo(object):
 | 
			
		|||
        # The tEXt chunk stores latin-1 text
 | 
			
		||||
        if not isinstance(value, bytes):
 | 
			
		||||
            try:
 | 
			
		||||
                value = value.encode('latin-1', 'strict')
 | 
			
		||||
                value = value.encode("latin-1", "strict")
 | 
			
		||||
            except UnicodeError:
 | 
			
		||||
                return self.add_itxt(key, value, zip=zip)
 | 
			
		||||
 | 
			
		||||
        if not isinstance(key, bytes):
 | 
			
		||||
            key = key.encode('latin-1', 'strict')
 | 
			
		||||
            key = key.encode("latin-1", "strict")
 | 
			
		||||
 | 
			
		||||
        if zip:
 | 
			
		||||
            self.add(b"zTXt", key + b"\0\0" + zlib.compress(value))
 | 
			
		||||
| 
						 | 
				
			
			@ -290,8 +290,8 @@ class PngInfo(object):
 | 
			
		|||
# --------------------------------------------------------------------
 | 
			
		||||
# PNG image stream (IHDR/IEND)
 | 
			
		||||
 | 
			
		||||
class PngStream(ChunkStream):
 | 
			
		||||
 | 
			
		||||
class PngStream(ChunkStream):
 | 
			
		||||
    def __init__(self, fp):
 | 
			
		||||
 | 
			
		||||
        ChunkStream.__init__(self, fp)
 | 
			
		||||
| 
						 | 
				
			
			@ -310,8 +310,10 @@ class PngStream(ChunkStream):
 | 
			
		|||
    def check_text_memory(self, chunklen):
 | 
			
		||||
        self.text_memory += chunklen
 | 
			
		||||
        if self.text_memory > MAX_TEXT_MEMORY:
 | 
			
		||||
            raise ValueError("Too much memory used in text chunks: "
 | 
			
		||||
                             "%s>MAX_TEXT_MEMORY" % self.text_memory)
 | 
			
		||||
            raise ValueError(
 | 
			
		||||
                "Too much memory used in text chunks: %s>MAX_TEXT_MEMORY"
 | 
			
		||||
                % self.text_memory
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    def chunk_iCCP(self, pos, length):
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -327,10 +329,11 @@ class PngStream(ChunkStream):
 | 
			
		|||
        logger.debug("Compression method %s", i8(s[i]))
 | 
			
		||||
        comp_method = i8(s[i])
 | 
			
		||||
        if comp_method != 0:
 | 
			
		||||
            raise SyntaxError("Unknown compression method %s in iCCP chunk" %
 | 
			
		||||
                              comp_method)
 | 
			
		||||
            raise SyntaxError(
 | 
			
		||||
                "Unknown compression method %s in iCCP chunk" % comp_method
 | 
			
		||||
            )
 | 
			
		||||
        try:
 | 
			
		||||
            icc_profile = _safe_zlib_decompress(s[i+2:])
 | 
			
		||||
            icc_profile = _safe_zlib_decompress(s[i + 2 :])
 | 
			
		||||
        except ValueError:
 | 
			
		||||
            if ImageFile.LOAD_TRUNCATED_IMAGES:
 | 
			
		||||
                icc_profile = None
 | 
			
		||||
| 
						 | 
				
			
			@ -359,7 +362,7 @@ class PngStream(ChunkStream):
 | 
			
		|||
    def chunk_IDAT(self, pos, length):
 | 
			
		||||
 | 
			
		||||
        # image data
 | 
			
		||||
        self.im_tile = [("zip", (0, 0)+self.im_size, pos, self.im_rawmode)]
 | 
			
		||||
        self.im_tile = [("zip", (0, 0) + self.im_size, pos, self.im_rawmode)]
 | 
			
		||||
        self.im_idat = length
 | 
			
		||||
        raise EOFError
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -408,8 +411,8 @@ class PngStream(ChunkStream):
 | 
			
		|||
        # WP x,y, Red x,y, Green x,y Blue x,y
 | 
			
		||||
 | 
			
		||||
        s = ImageFile._safe_read(self.fp, length)
 | 
			
		||||
        raw_vals = struct.unpack('>%dI' % (len(s) // 4), s)
 | 
			
		||||
        self.im_info['chromaticity'] = tuple(elt/100000.0 for elt in raw_vals)
 | 
			
		||||
        raw_vals = struct.unpack(">%dI" % (len(s) // 4), s)
 | 
			
		||||
        self.im_info["chromaticity"] = tuple(elt / 100000.0 for elt in raw_vals)
 | 
			
		||||
        return s
 | 
			
		||||
 | 
			
		||||
    def chunk_sRGB(self, pos, length):
 | 
			
		||||
| 
						 | 
				
			
			@ -420,7 +423,7 @@ class PngStream(ChunkStream):
 | 
			
		|||
        # 3 absolute colorimetric
 | 
			
		||||
 | 
			
		||||
        s = ImageFile._safe_read(self.fp, length)
 | 
			
		||||
        self.im_info['srgb'] = i8(s)
 | 
			
		||||
        self.im_info["srgb"] = i8(s)
 | 
			
		||||
        return s
 | 
			
		||||
 | 
			
		||||
    def chunk_pHYs(self, pos, length):
 | 
			
		||||
| 
						 | 
				
			
			@ -448,8 +451,8 @@ class PngStream(ChunkStream):
 | 
			
		|||
            v = b""
 | 
			
		||||
        if k:
 | 
			
		||||
            if py3:
 | 
			
		||||
                k = k.decode('latin-1', 'strict')
 | 
			
		||||
                v = v.decode('latin-1', 'replace')
 | 
			
		||||
                k = k.decode("latin-1", "strict")
 | 
			
		||||
                v = v.decode("latin-1", "replace")
 | 
			
		||||
 | 
			
		||||
            self.im_info[k] = self.im_text[k] = v
 | 
			
		||||
            self.check_text_memory(len(v))
 | 
			
		||||
| 
						 | 
				
			
			@ -470,8 +473,9 @@ class PngStream(ChunkStream):
 | 
			
		|||
        else:
 | 
			
		||||
            comp_method = 0
 | 
			
		||||
        if comp_method != 0:
 | 
			
		||||
            raise SyntaxError("Unknown compression method %s in zTXt chunk" %
 | 
			
		||||
                              comp_method)
 | 
			
		||||
            raise SyntaxError(
 | 
			
		||||
                "Unknown compression method %s in zTXt chunk" % comp_method
 | 
			
		||||
            )
 | 
			
		||||
        try:
 | 
			
		||||
            v = _safe_zlib_decompress(v[1:])
 | 
			
		||||
        except ValueError:
 | 
			
		||||
| 
						 | 
				
			
			@ -484,8 +488,8 @@ class PngStream(ChunkStream):
 | 
			
		|||
 | 
			
		||||
        if k:
 | 
			
		||||
            if py3:
 | 
			
		||||
                k = k.decode('latin-1', 'strict')
 | 
			
		||||
                v = v.decode('latin-1', 'replace')
 | 
			
		||||
                k = k.decode("latin-1", "strict")
 | 
			
		||||
                v = v.decode("latin-1", "replace")
 | 
			
		||||
 | 
			
		||||
            self.im_info[k] = self.im_text[k] = v
 | 
			
		||||
            self.check_text_memory(len(v))
 | 
			
		||||
| 
						 | 
				
			
			@ -536,19 +540,20 @@ class PngStream(ChunkStream):
 | 
			
		|||
 | 
			
		||||
    def chunk_eXIf(self, pos, length):
 | 
			
		||||
        s = ImageFile._safe_read(self.fp, length)
 | 
			
		||||
        self.im_info["exif"] = b"Exif\x00\x00"+s
 | 
			
		||||
        self.im_info["exif"] = b"Exif\x00\x00" + s
 | 
			
		||||
        return s
 | 
			
		||||
 | 
			
		||||
    # APNG chunks
 | 
			
		||||
    def chunk_acTL(self, pos, length):
 | 
			
		||||
        s = ImageFile._safe_read(self.fp, length)
 | 
			
		||||
        self.im_custom_mimetype = 'image/apng'
 | 
			
		||||
        self.im_custom_mimetype = "image/apng"
 | 
			
		||||
        return s
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# --------------------------------------------------------------------
 | 
			
		||||
# PNG reader
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _accept(prefix):
 | 
			
		||||
    return prefix[:8] == _MAGIC
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -556,6 +561,7 @@ def _accept(prefix):
 | 
			
		|||
##
 | 
			
		||||
# Image plugin for PNG images.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PngImageFile(ImageFile.ImageFile):
 | 
			
		||||
 | 
			
		||||
    format = "PNG"
 | 
			
		||||
| 
						 | 
				
			
			@ -711,20 +717,20 @@ class PngImageFile(ImageFile.ImageFile):
 | 
			
		|||
 | 
			
		||||
_OUTMODES = {
 | 
			
		||||
    # supported PIL modes, and corresponding rawmodes/bits/color combinations
 | 
			
		||||
    "1":    ("1",       b'\x01\x00'),
 | 
			
		||||
    "L;1":  ("L;1",     b'\x01\x00'),
 | 
			
		||||
    "L;2":  ("L;2",     b'\x02\x00'),
 | 
			
		||||
    "L;4":  ("L;4",     b'\x04\x00'),
 | 
			
		||||
    "L":    ("L",       b'\x08\x00'),
 | 
			
		||||
    "LA":   ("LA",      b'\x08\x04'),
 | 
			
		||||
    "I":    ("I;16B",   b'\x10\x00'),
 | 
			
		||||
    "I;16": ("I;16B",   b'\x10\x00'),
 | 
			
		||||
    "P;1":  ("P;1",     b'\x01\x03'),
 | 
			
		||||
    "P;2":  ("P;2",     b'\x02\x03'),
 | 
			
		||||
    "P;4":  ("P;4",     b'\x04\x03'),
 | 
			
		||||
    "P":    ("P",       b'\x08\x03'),
 | 
			
		||||
    "RGB":  ("RGB",     b'\x08\x02'),
 | 
			
		||||
    "RGBA": ("RGBA",    b'\x08\x06'),
 | 
			
		||||
    "1": ("1", b"\x01\x00"),
 | 
			
		||||
    "L;1": ("L;1", b"\x01\x00"),
 | 
			
		||||
    "L;2": ("L;2", b"\x02\x00"),
 | 
			
		||||
    "L;4": ("L;4", b"\x04\x00"),
 | 
			
		||||
    "L": ("L", b"\x08\x00"),
 | 
			
		||||
    "LA": ("LA", b"\x08\x04"),
 | 
			
		||||
    "I": ("I;16B", b"\x10\x00"),
 | 
			
		||||
    "I;16": ("I;16B", b"\x10\x00"),
 | 
			
		||||
    "P;1": ("P;1", b"\x01\x03"),
 | 
			
		||||
    "P;2": ("P;2", b"\x02\x03"),
 | 
			
		||||
    "P;4": ("P;4", b"\x04\x03"),
 | 
			
		||||
    "P": ("P", b"\x08\x03"),
 | 
			
		||||
    "RGB": ("RGB", b"\x08\x02"),
 | 
			
		||||
    "RGBA": ("RGBA", b"\x08\x06"),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -765,7 +771,7 @@ def _save(im, fp, filename, chunk=putchunk):
 | 
			
		|||
        else:
 | 
			
		||||
            # check palette contents
 | 
			
		||||
            if im.palette:
 | 
			
		||||
                colors = max(min(len(im.palette.getdata()[1])//3, 256), 2)
 | 
			
		||||
                colors = max(min(len(im.palette.getdata()[1]) // 3, 256), 2)
 | 
			
		||||
            else:
 | 
			
		||||
                colors = 256
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -781,10 +787,12 @@ def _save(im, fp, filename, chunk=putchunk):
 | 
			
		|||
            mode = "%s;%d" % (mode, bits)
 | 
			
		||||
 | 
			
		||||
    # encoder options
 | 
			
		||||
    im.encoderconfig = (im.encoderinfo.get("optimize", False),
 | 
			
		||||
    im.encoderconfig = (
 | 
			
		||||
        im.encoderinfo.get("optimize", False),
 | 
			
		||||
        im.encoderinfo.get("compress_level", -1),
 | 
			
		||||
        im.encoderinfo.get("compress_type", -1),
 | 
			
		||||
                        im.encoderinfo.get("dictionary", b""))
 | 
			
		||||
        im.encoderinfo.get("dictionary", b""),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # get the corresponding PNG mode
 | 
			
		||||
    try:
 | 
			
		||||
| 
						 | 
				
			
			@ -797,12 +805,16 @@ def _save(im, fp, filename, chunk=putchunk):
 | 
			
		|||
 | 
			
		||||
    fp.write(_MAGIC)
 | 
			
		||||
 | 
			
		||||
    chunk(fp, b"IHDR",
 | 
			
		||||
          o32(im.size[0]), o32(im.size[1]),     # 0: size
 | 
			
		||||
    chunk(
 | 
			
		||||
        fp,
 | 
			
		||||
        b"IHDR",
 | 
			
		||||
        o32(im.size[0]),  # 0: size
 | 
			
		||||
        o32(im.size[1]),
 | 
			
		||||
        mode,  # 8: depth/type
 | 
			
		||||
          b'\0',                                # 10: compression
 | 
			
		||||
          b'\0',                                # 11: filter category
 | 
			
		||||
          b'\0')                                # 12: interlace flag
 | 
			
		||||
        b"\0",  # 10: compression
 | 
			
		||||
        b"\0",  # 11: filter category
 | 
			
		||||
        b"\0",  # 12: interlace flag
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    chunks = [b"cHRM", b"gAMA", b"sBIT", b"sRGB", b"tIME"]
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -836,21 +848,20 @@ def _save(im, fp, filename, chunk=putchunk):
 | 
			
		|||
        palette_byte_number = (2 ** bits) * 3
 | 
			
		||||
        palette_bytes = im.im.getpalette("RGB")[:palette_byte_number]
 | 
			
		||||
        while len(palette_bytes) < palette_byte_number:
 | 
			
		||||
            palette_bytes += b'\0'
 | 
			
		||||
            palette_bytes += b"\0"
 | 
			
		||||
        chunk(fp, b"PLTE", palette_bytes)
 | 
			
		||||
 | 
			
		||||
    transparency = im.encoderinfo.get('transparency',
 | 
			
		||||
                                      im.info.get('transparency', None))
 | 
			
		||||
    transparency = im.encoderinfo.get("transparency", im.info.get("transparency", None))
 | 
			
		||||
 | 
			
		||||
    if transparency or transparency == 0:
 | 
			
		||||
        if im.mode == "P":
 | 
			
		||||
            # limit to actual palette size
 | 
			
		||||
            alpha_bytes = 2**bits
 | 
			
		||||
            alpha_bytes = 2 ** bits
 | 
			
		||||
            if isinstance(transparency, bytes):
 | 
			
		||||
                chunk(fp, b"tRNS", transparency[:alpha_bytes])
 | 
			
		||||
            else:
 | 
			
		||||
                transparency = max(0, min(255, transparency))
 | 
			
		||||
                alpha = b'\xFF' * transparency + b'\0'
 | 
			
		||||
                alpha = b"\xFF" * transparency + b"\0"
 | 
			
		||||
                chunk(fp, b"tRNS", alpha[:alpha_bytes])
 | 
			
		||||
        elif im.mode in ("1", "L", "I"):
 | 
			
		||||
            transparency = max(0, min(65535, transparency))
 | 
			
		||||
| 
						 | 
				
			
			@ -866,15 +877,18 @@ def _save(im, fp, filename, chunk=putchunk):
 | 
			
		|||
    else:
 | 
			
		||||
        if im.mode == "P" and im.im.getpalettemode() == "RGBA":
 | 
			
		||||
            alpha = im.im.getpalette("RGBA", "A")
 | 
			
		||||
            alpha_bytes = 2**bits
 | 
			
		||||
            alpha_bytes = 2 ** bits
 | 
			
		||||
            chunk(fp, b"tRNS", alpha[:alpha_bytes])
 | 
			
		||||
 | 
			
		||||
    dpi = im.encoderinfo.get("dpi")
 | 
			
		||||
    if dpi:
 | 
			
		||||
        chunk(fp, b"pHYs",
 | 
			
		||||
        chunk(
 | 
			
		||||
            fp,
 | 
			
		||||
            b"pHYs",
 | 
			
		||||
            o32(int(dpi[0] / 0.0254 + 0.5)),
 | 
			
		||||
            o32(int(dpi[1] / 0.0254 + 0.5)),
 | 
			
		||||
              b'\x01')
 | 
			
		||||
            b"\x01",
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    info = im.encoderinfo.get("pnginfo")
 | 
			
		||||
    if info:
 | 
			
		||||
| 
						 | 
				
			
			@ -892,8 +906,7 @@ def _save(im, fp, filename, chunk=putchunk):
 | 
			
		|||
            exif = exif[6:]
 | 
			
		||||
        chunk(fp, b"eXIf", exif)
 | 
			
		||||
 | 
			
		||||
    ImageFile._save(im, _idat(fp, chunk),
 | 
			
		||||
                    [("zip", (0, 0)+im.size, 0, rawmode)])
 | 
			
		||||
    ImageFile._save(im, _idat(fp, chunk), [("zip", (0, 0) + im.size, 0, rawmode)])
 | 
			
		||||
 | 
			
		||||
    chunk(fp, b"IEND", b"")
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -904,6 +917,7 @@ def _save(im, fp, filename, chunk=putchunk):
 | 
			
		|||
# --------------------------------------------------------------------
 | 
			
		||||
# PNG chunk converter
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def getchunks(im, **params):
 | 
			
		||||
    """Return a list of PNG chunks representing this image."""
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,7 +24,7 @@ __version__ = "0.2"
 | 
			
		|||
#
 | 
			
		||||
# --------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
b_whitespace = b'\x20\x09\x0a\x0b\x0c\x0d'
 | 
			
		||||
b_whitespace = b"\x20\x09\x0a\x0b\x0c\x0d"
 | 
			
		||||
 | 
			
		||||
MODES = {
 | 
			
		||||
    # standard
 | 
			
		||||
| 
						 | 
				
			
			@ -36,7 +36,7 @@ MODES = {
 | 
			
		|||
    # PIL extensions (for test purposes only)
 | 
			
		||||
    b"PyP": "P",
 | 
			
		||||
    b"PyRGBA": "RGBA",
 | 
			
		||||
    b"PyCMYK": "CMYK"
 | 
			
		||||
    b"PyCMYK": "CMYK",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -47,6 +47,7 @@ def _accept(prefix):
 | 
			
		|||
##
 | 
			
		||||
# Image plugin for PBM, PGM, and PPM images.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PpmImageFile(ImageFile.ImageFile):
 | 
			
		||||
 | 
			
		||||
    format = "PPM"
 | 
			
		||||
| 
						 | 
				
			
			@ -57,10 +58,10 @@ class PpmImageFile(ImageFile.ImageFile):
 | 
			
		|||
            c = self.fp.read(1)
 | 
			
		||||
            if not c or c in b_whitespace:
 | 
			
		||||
                break
 | 
			
		||||
            if c > b'\x79':
 | 
			
		||||
            if c > b"\x79":
 | 
			
		||||
                raise ValueError("Expected ASCII value, found binary")
 | 
			
		||||
            s = s + c
 | 
			
		||||
            if (len(s) > 9):
 | 
			
		||||
            if len(s) > 9:
 | 
			
		||||
                raise ValueError("Expected int, got > 9 digits")
 | 
			
		||||
        return s
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -92,8 +93,7 @@ class PpmImageFile(ImageFile.ImageFile):
 | 
			
		|||
                    if s not in b_whitespace:
 | 
			
		||||
                        break
 | 
			
		||||
                    if s == b"":
 | 
			
		||||
                        raise ValueError(
 | 
			
		||||
                            "File does not extend beyond magic number")
 | 
			
		||||
                        raise ValueError("File does not extend beyond magic number")
 | 
			
		||||
                if s != b"#":
 | 
			
		||||
                    break
 | 
			
		||||
                s = self.fp.readline()
 | 
			
		||||
| 
						 | 
				
			
			@ -107,32 +107,30 @@ class PpmImageFile(ImageFile.ImageFile):
 | 
			
		|||
            elif ix == 2:
 | 
			
		||||
                # maxgrey
 | 
			
		||||
                if s > 255:
 | 
			
		||||
                    if not mode == 'L':
 | 
			
		||||
                    if not mode == "L":
 | 
			
		||||
                        raise ValueError("Too many colors for band: %s" % s)
 | 
			
		||||
                    if s < 2**16:
 | 
			
		||||
                        self.mode = 'I'
 | 
			
		||||
                        rawmode = 'I;16B'
 | 
			
		||||
                    if s < 2 ** 16:
 | 
			
		||||
                        self.mode = "I"
 | 
			
		||||
                        rawmode = "I;16B"
 | 
			
		||||
                    else:
 | 
			
		||||
                        self.mode = 'I'
 | 
			
		||||
                        rawmode = 'I;32B'
 | 
			
		||||
                        self.mode = "I"
 | 
			
		||||
                        rawmode = "I;32B"
 | 
			
		||||
 | 
			
		||||
        self._size = xsize, ysize
 | 
			
		||||
        self.tile = [("raw",
 | 
			
		||||
                     (0, 0, xsize, ysize),
 | 
			
		||||
                     self.fp.tell(),
 | 
			
		||||
                     (rawmode, 0, 1))]
 | 
			
		||||
        self.tile = [("raw", (0, 0, xsize, ysize), self.fp.tell(), (rawmode, 0, 1))]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# --------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _save(im, fp, filename):
 | 
			
		||||
    if im.mode == "1":
 | 
			
		||||
        rawmode, head = "1;I", b"P4"
 | 
			
		||||
    elif im.mode == "L":
 | 
			
		||||
        rawmode, head = "L", b"P5"
 | 
			
		||||
    elif im.mode == "I":
 | 
			
		||||
        if im.getextrema()[1] < 2**16:
 | 
			
		||||
        if im.getextrema()[1] < 2 ** 16:
 | 
			
		||||
            rawmode, head = "I;16B", b"P5"
 | 
			
		||||
        else:
 | 
			
		||||
            rawmode, head = "I;32B", b"P5"
 | 
			
		||||
| 
						 | 
				
			
			@ -142,7 +140,7 @@ def _save(im, fp, filename):
 | 
			
		|||
        rawmode, head = "RGB", b"P6"
 | 
			
		||||
    else:
 | 
			
		||||
        raise IOError("cannot write mode %s as PPM" % im.mode)
 | 
			
		||||
    fp.write(head + ("\n%d %d\n" % im.size).encode('ascii'))
 | 
			
		||||
    fp.write(head + ("\n%d %d\n" % im.size).encode("ascii"))
 | 
			
		||||
    if head == b"P6":
 | 
			
		||||
        fp.write(b"255\n")
 | 
			
		||||
    if head == b"P5":
 | 
			
		||||
| 
						 | 
				
			
			@ -152,11 +150,12 @@ def _save(im, fp, filename):
 | 
			
		|||
            fp.write(b"65535\n")
 | 
			
		||||
        elif rawmode == "I;32B":
 | 
			
		||||
            fp.write(b"2147483648\n")
 | 
			
		||||
    ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0, (rawmode, 0, 1))])
 | 
			
		||||
    ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, 0, 1))])
 | 
			
		||||
 | 
			
		||||
    # ALTERNATIVE: save via builtin debug function
 | 
			
		||||
    # im._dump(filename)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# --------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,13 +34,14 @@ MODES = {
 | 
			
		|||
    (4, 8): ("CMYK", 4),
 | 
			
		||||
    (7, 8): ("L", 1),  # FIXME: multilayer
 | 
			
		||||
    (8, 8): ("L", 1),  # duotone
 | 
			
		||||
    (9, 8): ("LAB", 3)
 | 
			
		||||
    (9, 8): ("LAB", 3),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# --------------------------------------------------------------------.
 | 
			
		||||
# read PSD images
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _accept(prefix):
 | 
			
		||||
    return prefix[:4] == b"8BPS"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -48,6 +49,7 @@ def _accept(prefix):
 | 
			
		|||
##
 | 
			
		||||
# Image plugin for Photoshop images.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PsdImageFile(ImageFile.ImageFile):
 | 
			
		||||
 | 
			
		||||
    format = "PSD"
 | 
			
		||||
| 
						 | 
				
			
			@ -101,7 +103,7 @@ class PsdImageFile(ImageFile.ImageFile):
 | 
			
		|||
                if not (len(name) & 1):
 | 
			
		||||
                    read(1)  # padding
 | 
			
		||||
                data = read(i32(read(4)))
 | 
			
		||||
                if (len(data) & 1):
 | 
			
		||||
                if len(data) & 1:
 | 
			
		||||
                    read(1)  # padding
 | 
			
		||||
                self.resources.append((id, name, data))
 | 
			
		||||
                if id == 1039:  # ICC profile
 | 
			
		||||
| 
						 | 
				
			
			@ -144,7 +146,7 @@ class PsdImageFile(ImageFile.ImageFile):
 | 
			
		|||
 | 
			
		||||
        # seek to given layer (1..max)
 | 
			
		||||
        try:
 | 
			
		||||
            name, mode, bbox, tile = self.layers[layer-1]
 | 
			
		||||
            name, mode, bbox, tile = self.layers[layer - 1]
 | 
			
		||||
            self.mode = mode
 | 
			
		||||
            self.tile = tile
 | 
			
		||||
            self.frame = layer
 | 
			
		||||
| 
						 | 
				
			
			@ -159,8 +161,7 @@ class PsdImageFile(ImageFile.ImageFile):
 | 
			
		|||
 | 
			
		||||
    def load_prepare(self):
 | 
			
		||||
        # create image memory if necessary
 | 
			
		||||
        if not self.im or\
 | 
			
		||||
           self.im.mode != self.mode or self.im.size != self.size:
 | 
			
		||||
        if not self.im or self.im.mode != self.mode or self.im.size != self.size:
 | 
			
		||||
            self.im = Image.core.fill(self.mode, self.size, 0)
 | 
			
		||||
        # create palette (optional)
 | 
			
		||||
        if self.mode == "P":
 | 
			
		||||
| 
						 | 
				
			
			@ -229,7 +230,7 @@ def _layerinfo(file):
 | 
			
		|||
            if length:
 | 
			
		||||
                # Don't know the proper encoding,
 | 
			
		||||
                # Latin-1 should be a good guess
 | 
			
		||||
                name = read(length).decode('latin-1', 'replace')
 | 
			
		||||
                name = read(length).decode("latin-1", "replace")
 | 
			
		||||
            combined += length + 1
 | 
			
		||||
 | 
			
		||||
        file.seek(size - combined, io.SEEK_CUR)
 | 
			
		||||
| 
						 | 
				
			
			@ -270,7 +271,7 @@ def _maketile(file, mode, bbox, channels):
 | 
			
		|||
            if mode == "CMYK":
 | 
			
		||||
                layer += ";I"
 | 
			
		||||
            tile.append(("raw", bbox, offset, layer))
 | 
			
		||||
            offset = offset + xsize*ysize
 | 
			
		||||
            offset = offset + xsize * ysize
 | 
			
		||||
 | 
			
		||||
    elif compression == 1:
 | 
			
		||||
        #
 | 
			
		||||
| 
						 | 
				
			
			@ -283,11 +284,9 @@ def _maketile(file, mode, bbox, channels):
 | 
			
		|||
            layer = mode[channel]
 | 
			
		||||
            if mode == "CMYK":
 | 
			
		||||
                layer += ";I"
 | 
			
		||||
            tile.append(
 | 
			
		||||
                ("packbits", bbox, offset, layer)
 | 
			
		||||
                )
 | 
			
		||||
            tile.append(("packbits", bbox, offset, layer))
 | 
			
		||||
            for y in range(ysize):
 | 
			
		||||
                offset = offset + i16(bytecount[i:i+2])
 | 
			
		||||
                offset = offset + i16(bytecount[i : i + 2])
 | 
			
		||||
                i += 2
 | 
			
		||||
 | 
			
		||||
    file.seek(offset)
 | 
			
		||||
| 
						 | 
				
			
			@ -297,6 +296,7 @@ def _maketile(file, mode, bbox, channels):
 | 
			
		|||
 | 
			
		||||
    return tile
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# --------------------------------------------------------------------
 | 
			
		||||
# registry
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,13 +42,12 @@ ffi.cdef(defs)
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
class PyAccess(object):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, img, readonly=False):
 | 
			
		||||
        vals = dict(img.im.unsafe_ptrs)
 | 
			
		||||
        self.readonly = readonly
 | 
			
		||||
        self.image8 = ffi.cast('unsigned char **', vals['image8'])
 | 
			
		||||
        self.image32 = ffi.cast('int **', vals['image32'])
 | 
			
		||||
        self.image = ffi.cast('unsigned char **', vals['image'])
 | 
			
		||||
        self.image8 = ffi.cast("unsigned char **", vals["image8"])
 | 
			
		||||
        self.image32 = ffi.cast("int **", vals["image32"])
 | 
			
		||||
        self.image = ffi.cast("unsigned char **", vals["image"])
 | 
			
		||||
        self.xsize, self.ysize = img.im.size
 | 
			
		||||
 | 
			
		||||
        # Keep pointer to im object to prevent dereferencing.
 | 
			
		||||
| 
						 | 
				
			
			@ -75,7 +74,7 @@ class PyAccess(object):
 | 
			
		|||
        :param color: The pixel value.
 | 
			
		||||
        """
 | 
			
		||||
        if self.readonly:
 | 
			
		||||
            raise ValueError('Attempt to putpixel a read only image')
 | 
			
		||||
            raise ValueError("Attempt to putpixel a read only image")
 | 
			
		||||
        (x, y) = xy
 | 
			
		||||
        if x < 0:
 | 
			
		||||
            x = self.xsize + x
 | 
			
		||||
| 
						 | 
				
			
			@ -83,8 +82,11 @@ class PyAccess(object):
 | 
			
		|||
            y = self.ysize + y
 | 
			
		||||
        (x, y) = self.check_xy((x, y))
 | 
			
		||||
 | 
			
		||||
        if self._im.mode == "P" and \
 | 
			
		||||
           isinstance(color, (list, tuple)) and len(color) in [3, 4]:
 | 
			
		||||
        if (
 | 
			
		||||
            self._im.mode == "P"
 | 
			
		||||
            and isinstance(color, (list, tuple))
 | 
			
		||||
            and len(color) in [3, 4]
 | 
			
		||||
        ):
 | 
			
		||||
            # RGB or RGBA value for a P image
 | 
			
		||||
            color = self._palette.getcolor(color)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -115,12 +117,13 @@ class PyAccess(object):
 | 
			
		|||
    def check_xy(self, xy):
 | 
			
		||||
        (x, y) = xy
 | 
			
		||||
        if not (0 <= x < self.xsize and 0 <= y < self.ysize):
 | 
			
		||||
            raise ValueError('pixel location out of range')
 | 
			
		||||
            raise ValueError("pixel location out of range")
 | 
			
		||||
        return xy
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class _PyAccess32_2(PyAccess):
 | 
			
		||||
    """ PA, LA, stored in first and last bytes of a 32 bit word """
 | 
			
		||||
 | 
			
		||||
    def _post_init(self, *args, **kwargs):
 | 
			
		||||
        self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -156,6 +159,7 @@ class _PyAccess32_3(PyAccess):
 | 
			
		|||
 | 
			
		||||
class _PyAccess32_4(PyAccess):
 | 
			
		||||
    """ RGBA etc, all 4 bytes of a 32 bit word """
 | 
			
		||||
 | 
			
		||||
    def _post_init(self, *args, **kwargs):
 | 
			
		||||
        self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -174,6 +178,7 @@ class _PyAccess32_4(PyAccess):
 | 
			
		|||
 | 
			
		||||
class _PyAccess8(PyAccess):
 | 
			
		||||
    """ 1, L, P, 8 bit images stored as uint8 """
 | 
			
		||||
 | 
			
		||||
    def _post_init(self, *args, **kwargs):
 | 
			
		||||
        self.pixels = self.image8
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -191,8 +196,9 @@ class _PyAccess8(PyAccess):
 | 
			
		|||
 | 
			
		||||
class _PyAccessI16_N(PyAccess):
 | 
			
		||||
    """ I;16 access, native bitendian without conversion """
 | 
			
		||||
 | 
			
		||||
    def _post_init(self, *args, **kwargs):
 | 
			
		||||
        self.pixels = ffi.cast('unsigned short **', self.image)
 | 
			
		||||
        self.pixels = ffi.cast("unsigned short **", self.image)
 | 
			
		||||
 | 
			
		||||
    def get_pixel(self, x, y):
 | 
			
		||||
        return self.pixels[y][x]
 | 
			
		||||
| 
						 | 
				
			
			@ -208,8 +214,9 @@ class _PyAccessI16_N(PyAccess):
 | 
			
		|||
 | 
			
		||||
class _PyAccessI16_L(PyAccess):
 | 
			
		||||
    """ I;16L access, with conversion """
 | 
			
		||||
 | 
			
		||||
    def _post_init(self, *args, **kwargs):
 | 
			
		||||
        self.pixels = ffi.cast('struct Pixel_I16 **', self.image)
 | 
			
		||||
        self.pixels = ffi.cast("struct Pixel_I16 **", self.image)
 | 
			
		||||
 | 
			
		||||
    def get_pixel(self, x, y):
 | 
			
		||||
        pixel = self.pixels[y][x]
 | 
			
		||||
| 
						 | 
				
			
			@ -228,8 +235,9 @@ class _PyAccessI16_L(PyAccess):
 | 
			
		|||
 | 
			
		||||
class _PyAccessI16_B(PyAccess):
 | 
			
		||||
    """ I;16B access, with conversion """
 | 
			
		||||
 | 
			
		||||
    def _post_init(self, *args, **kwargs):
 | 
			
		||||
        self.pixels = ffi.cast('struct Pixel_I16 **', self.image)
 | 
			
		||||
        self.pixels = ffi.cast("struct Pixel_I16 **", self.image)
 | 
			
		||||
 | 
			
		||||
    def get_pixel(self, x, y):
 | 
			
		||||
        pixel = self.pixels[y][x]
 | 
			
		||||
| 
						 | 
				
			
			@ -248,6 +256,7 @@ class _PyAccessI16_B(PyAccess):
 | 
			
		|||
 | 
			
		||||
class _PyAccessI32_N(PyAccess):
 | 
			
		||||
    """ Signed Int32 access, native endian """
 | 
			
		||||
 | 
			
		||||
    def _post_init(self, *args, **kwargs):
 | 
			
		||||
        self.pixels = self.image32
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -260,15 +269,15 @@ class _PyAccessI32_N(PyAccess):
 | 
			
		|||
 | 
			
		||||
class _PyAccessI32_Swap(PyAccess):
 | 
			
		||||
    """ I;32L/B access, with byteswapping conversion """
 | 
			
		||||
 | 
			
		||||
    def _post_init(self, *args, **kwargs):
 | 
			
		||||
        self.pixels = self.image32
 | 
			
		||||
 | 
			
		||||
    def reverse(self, i):
 | 
			
		||||
        orig = ffi.new('int *', i)
 | 
			
		||||
        chars = ffi.cast('unsigned char *', orig)
 | 
			
		||||
        chars[0], chars[1], chars[2], chars[3] = chars[3], chars[2], \
 | 
			
		||||
            chars[1], chars[0]
 | 
			
		||||
        return ffi.cast('int *', chars)[0]
 | 
			
		||||
        orig = ffi.new("int *", i)
 | 
			
		||||
        chars = ffi.cast("unsigned char *", orig)
 | 
			
		||||
        chars[0], chars[1], chars[2], chars[3] = chars[3], chars[2], chars[1], chars[0]
 | 
			
		||||
        return ffi.cast("int *", chars)[0]
 | 
			
		||||
 | 
			
		||||
    def get_pixel(self, x, y):
 | 
			
		||||
        return self.reverse(self.pixels[y][x])
 | 
			
		||||
| 
						 | 
				
			
			@ -279,8 +288,9 @@ class _PyAccessI32_Swap(PyAccess):
 | 
			
		|||
 | 
			
		||||
class _PyAccessF(PyAccess):
 | 
			
		||||
    """ 32 bit float access """
 | 
			
		||||
 | 
			
		||||
    def _post_init(self, *args, **kwargs):
 | 
			
		||||
        self.pixels = ffi.cast('float **', self.image32)
 | 
			
		||||
        self.pixels = ffi.cast("float **", self.image32)
 | 
			
		||||
 | 
			
		||||
    def get_pixel(self, x, y):
 | 
			
		||||
        return self.pixels[y][x]
 | 
			
		||||
| 
						 | 
				
			
			@ -294,38 +304,39 @@ class _PyAccessF(PyAccess):
 | 
			
		|||
            self.pixels[y][x] = color[0]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
mode_map = {'1': _PyAccess8,
 | 
			
		||||
            'L': _PyAccess8,
 | 
			
		||||
            'P': _PyAccess8,
 | 
			
		||||
            'LA': _PyAccess32_2,
 | 
			
		||||
            'La': _PyAccess32_2,
 | 
			
		||||
            'PA': _PyAccess32_2,
 | 
			
		||||
            'RGB': _PyAccess32_3,
 | 
			
		||||
            'LAB': _PyAccess32_3,
 | 
			
		||||
            'HSV': _PyAccess32_3,
 | 
			
		||||
            'YCbCr': _PyAccess32_3,
 | 
			
		||||
            'RGBA': _PyAccess32_4,
 | 
			
		||||
            'RGBa': _PyAccess32_4,
 | 
			
		||||
            'RGBX': _PyAccess32_4,
 | 
			
		||||
            'CMYK': _PyAccess32_4,
 | 
			
		||||
            'F': _PyAccessF,
 | 
			
		||||
            'I': _PyAccessI32_N,
 | 
			
		||||
            }
 | 
			
		||||
mode_map = {
 | 
			
		||||
    "1": _PyAccess8,
 | 
			
		||||
    "L": _PyAccess8,
 | 
			
		||||
    "P": _PyAccess8,
 | 
			
		||||
    "LA": _PyAccess32_2,
 | 
			
		||||
    "La": _PyAccess32_2,
 | 
			
		||||
    "PA": _PyAccess32_2,
 | 
			
		||||
    "RGB": _PyAccess32_3,
 | 
			
		||||
    "LAB": _PyAccess32_3,
 | 
			
		||||
    "HSV": _PyAccess32_3,
 | 
			
		||||
    "YCbCr": _PyAccess32_3,
 | 
			
		||||
    "RGBA": _PyAccess32_4,
 | 
			
		||||
    "RGBa": _PyAccess32_4,
 | 
			
		||||
    "RGBX": _PyAccess32_4,
 | 
			
		||||
    "CMYK": _PyAccess32_4,
 | 
			
		||||
    "F": _PyAccessF,
 | 
			
		||||
    "I": _PyAccessI32_N,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if sys.byteorder == 'little':
 | 
			
		||||
    mode_map['I;16'] = _PyAccessI16_N
 | 
			
		||||
    mode_map['I;16L'] = _PyAccessI16_N
 | 
			
		||||
    mode_map['I;16B'] = _PyAccessI16_B
 | 
			
		||||
if sys.byteorder == "little":
 | 
			
		||||
    mode_map["I;16"] = _PyAccessI16_N
 | 
			
		||||
    mode_map["I;16L"] = _PyAccessI16_N
 | 
			
		||||
    mode_map["I;16B"] = _PyAccessI16_B
 | 
			
		||||
 | 
			
		||||
    mode_map['I;32L'] = _PyAccessI32_N
 | 
			
		||||
    mode_map['I;32B'] = _PyAccessI32_Swap
 | 
			
		||||
    mode_map["I;32L"] = _PyAccessI32_N
 | 
			
		||||
    mode_map["I;32B"] = _PyAccessI32_Swap
 | 
			
		||||
else:
 | 
			
		||||
    mode_map['I;16'] = _PyAccessI16_L
 | 
			
		||||
    mode_map['I;16L'] = _PyAccessI16_L
 | 
			
		||||
    mode_map['I;16B'] = _PyAccessI16_N
 | 
			
		||||
    mode_map["I;16"] = _PyAccessI16_L
 | 
			
		||||
    mode_map["I;16L"] = _PyAccessI16_L
 | 
			
		||||
    mode_map["I;16B"] = _PyAccessI16_N
 | 
			
		||||
 | 
			
		||||
    mode_map['I;32L'] = _PyAccessI32_Swap
 | 
			
		||||
    mode_map['I;32B'] = _PyAccessI32_N
 | 
			
		||||
    mode_map["I;32L"] = _PyAccessI32_Swap
 | 
			
		||||
    mode_map["I;32B"] = _PyAccessI32_N
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def new(img, readonly=False):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,7 +46,7 @@ MODES = {
 | 
			
		|||
    (1, 3, 3): "RGB",
 | 
			
		||||
    (2, 3, 3): "RGB;16B",
 | 
			
		||||
    (1, 3, 4): "RGBA",
 | 
			
		||||
    (2, 3, 4): "RGBA;16B"
 | 
			
		||||
    (2, 3, 4): "RGBA;16B",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -100,8 +100,8 @@ class SgiImageFile(ImageFile.ImageFile):
 | 
			
		|||
 | 
			
		||||
        self._size = xsize, ysize
 | 
			
		||||
        self.mode = rawmode.split(";")[0]
 | 
			
		||||
        if self.mode == 'RGB':
 | 
			
		||||
            self.custom_mimetype = 'image/rgb'
 | 
			
		||||
        if self.mode == "RGB":
 | 
			
		||||
            self.custom_mimetype = "image/rgb"
 | 
			
		||||
 | 
			
		||||
        # orientation -1 : scanlines begins at the bottom-left corner
 | 
			
		||||
        orientation = -1
 | 
			
		||||
| 
						 | 
				
			
			@ -110,19 +110,21 @@ class SgiImageFile(ImageFile.ImageFile):
 | 
			
		|||
        if compression == 0:
 | 
			
		||||
            pagesize = xsize * ysize * bpc
 | 
			
		||||
            if bpc == 2:
 | 
			
		||||
                self.tile = [("SGI16", (0, 0) + self.size,
 | 
			
		||||
                              headlen, (self.mode, 0, orientation))]
 | 
			
		||||
                self.tile = [
 | 
			
		||||
                    ("SGI16", (0, 0) + self.size, headlen, (self.mode, 0, orientation))
 | 
			
		||||
                ]
 | 
			
		||||
            else:
 | 
			
		||||
                self.tile = []
 | 
			
		||||
                offset = headlen
 | 
			
		||||
                for layer in self.mode:
 | 
			
		||||
                    self.tile.append(
 | 
			
		||||
                        ("raw", (0, 0) + self.size,
 | 
			
		||||
                            offset, (layer, 0, orientation)))
 | 
			
		||||
                        ("raw", (0, 0) + self.size, offset, (layer, 0, orientation))
 | 
			
		||||
                    )
 | 
			
		||||
                    offset += pagesize
 | 
			
		||||
        elif compression == 1:
 | 
			
		||||
            self.tile = [("sgi_rle", (0, 0) + self.size,
 | 
			
		||||
                          headlen, (rawmode, orientation, bpc))]
 | 
			
		||||
            self.tile = [
 | 
			
		||||
                ("sgi_rle", (0, 0) + self.size, headlen, (rawmode, orientation, bpc))
 | 
			
		||||
            ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _save(im, fp, filename):
 | 
			
		||||
| 
						 | 
				
			
			@ -161,8 +163,9 @@ def _save(im, fp, filename):
 | 
			
		|||
 | 
			
		||||
    # assert we've got the right number of bands.
 | 
			
		||||
    if len(im.getbands()) != z:
 | 
			
		||||
        raise ValueError("incorrect number of bands in SGI write: %s vs %s" %
 | 
			
		||||
                         (z, len(im.getbands())))
 | 
			
		||||
        raise ValueError(
 | 
			
		||||
            "incorrect number of bands in SGI write: %s vs %s" % (z, len(im.getbands()))
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    # Minimum Byte value
 | 
			
		||||
    pinmin = 0
 | 
			
		||||
| 
						 | 
				
			
			@ -171,30 +174,30 @@ def _save(im, fp, filename):
 | 
			
		|||
    # Image name (79 characters max, truncated below in write)
 | 
			
		||||
    imgName = os.path.splitext(os.path.basename(filename))[0]
 | 
			
		||||
    if py3:
 | 
			
		||||
        imgName = imgName.encode('ascii', 'ignore')
 | 
			
		||||
        imgName = imgName.encode("ascii", "ignore")
 | 
			
		||||
    # Standard representation of pixel in the file
 | 
			
		||||
    colormap = 0
 | 
			
		||||
    fp.write(struct.pack('>h', magicNumber))
 | 
			
		||||
    fp.write(struct.pack(">h", magicNumber))
 | 
			
		||||
    fp.write(o8(rle))
 | 
			
		||||
    fp.write(o8(bpc))
 | 
			
		||||
    fp.write(struct.pack('>H', dim))
 | 
			
		||||
    fp.write(struct.pack('>H', x))
 | 
			
		||||
    fp.write(struct.pack('>H', y))
 | 
			
		||||
    fp.write(struct.pack('>H', z))
 | 
			
		||||
    fp.write(struct.pack('>l', pinmin))
 | 
			
		||||
    fp.write(struct.pack('>l', pinmax))
 | 
			
		||||
    fp.write(struct.pack('4s', b''))  # dummy
 | 
			
		||||
    fp.write(struct.pack('79s', imgName))  # truncates to 79 chars
 | 
			
		||||
    fp.write(struct.pack('s', b''))  # force null byte after imgname
 | 
			
		||||
    fp.write(struct.pack('>l', colormap))
 | 
			
		||||
    fp.write(struct.pack('404s', b''))  # dummy
 | 
			
		||||
    fp.write(struct.pack(">H", dim))
 | 
			
		||||
    fp.write(struct.pack(">H", x))
 | 
			
		||||
    fp.write(struct.pack(">H", y))
 | 
			
		||||
    fp.write(struct.pack(">H", z))
 | 
			
		||||
    fp.write(struct.pack(">l", pinmin))
 | 
			
		||||
    fp.write(struct.pack(">l", pinmax))
 | 
			
		||||
    fp.write(struct.pack("4s", b""))  # dummy
 | 
			
		||||
    fp.write(struct.pack("79s", imgName))  # truncates to 79 chars
 | 
			
		||||
    fp.write(struct.pack("s", b""))  # force null byte after imgname
 | 
			
		||||
    fp.write(struct.pack(">l", colormap))
 | 
			
		||||
    fp.write(struct.pack("404s", b""))  # dummy
 | 
			
		||||
 | 
			
		||||
    rawmode = 'L'
 | 
			
		||||
    rawmode = "L"
 | 
			
		||||
    if bpc == 2:
 | 
			
		||||
        rawmode = 'L;16B'
 | 
			
		||||
        rawmode = "L;16B"
 | 
			
		||||
 | 
			
		||||
    for channel in im.split():
 | 
			
		||||
        fp.write(channel.tobytes('raw', rawmode, 0, orientation))
 | 
			
		||||
        fp.write(channel.tobytes("raw", rawmode, 0, orientation))
 | 
			
		||||
 | 
			
		||||
    fp.close()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -209,13 +212,15 @@ class SGI16Decoder(ImageFile.PyDecoder):
 | 
			
		|||
        self.fd.seek(512)
 | 
			
		||||
 | 
			
		||||
        for band in range(zsize):
 | 
			
		||||
            channel = Image.new('L', (self.state.xsize, self.state.ysize))
 | 
			
		||||
            channel.frombytes(self.fd.read(2 * pagesize), 'raw',
 | 
			
		||||
                              'L;16B', stride, orientation)
 | 
			
		||||
            channel = Image.new("L", (self.state.xsize, self.state.ysize))
 | 
			
		||||
            channel.frombytes(
 | 
			
		||||
                self.fd.read(2 * pagesize), "raw", "L;16B", stride, orientation
 | 
			
		||||
            )
 | 
			
		||||
            self.im.putband(channel.im, band)
 | 
			
		||||
 | 
			
		||||
        return -1, 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# registry
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -225,7 +230,6 @@ Image.register_open(SgiImageFile.format, SgiImageFile, _accept)
 | 
			
		|||
Image.register_save(SgiImageFile.format, _save)
 | 
			
		||||
Image.register_mime(SgiImageFile.format, "image/sgi")
 | 
			
		||||
 | 
			
		||||
Image.register_extensions(SgiImageFile.format,
 | 
			
		||||
                          [".bw", ".rgb", ".rgba", ".sgi"])
 | 
			
		||||
Image.register_extensions(SgiImageFile.format, [".bw", ".rgb", ".rgba", ".sgi"])
 | 
			
		||||
 | 
			
		||||
# End of file
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -44,7 +44,7 @@ import sys
 | 
			
		|||
def isInt(f):
 | 
			
		||||
    try:
 | 
			
		||||
        i = int(f)
 | 
			
		||||
        if f-i == 0:
 | 
			
		||||
        if f - i == 0:
 | 
			
		||||
            return 1
 | 
			
		||||
        else:
 | 
			
		||||
            return 0
 | 
			
		||||
| 
						 | 
				
			
			@ -60,6 +60,7 @@ iforms = [1, 3, -11, -12, -21, -22]
 | 
			
		|||
# Returns no. of bytes in the header, if it is a valid Spider header,
 | 
			
		||||
# otherwise returns 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def isSpiderHeader(t):
 | 
			
		||||
    h = (99,) + t  # add 1 value so can use spider header index start=1
 | 
			
		||||
    # header values 1,2,5,12,13,22,23 should be integers
 | 
			
		||||
| 
						 | 
				
			
			@ -81,12 +82,12 @@ def isSpiderHeader(t):
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
def isSpiderImage(filename):
 | 
			
		||||
    with open(filename, 'rb') as fp:
 | 
			
		||||
    with open(filename, "rb") as fp:
 | 
			
		||||
        f = fp.read(92)  # read 23 * 4 bytes
 | 
			
		||||
    t = struct.unpack('>23f', f)  # try big-endian first
 | 
			
		||||
    t = struct.unpack(">23f", f)  # try big-endian first
 | 
			
		||||
    hdrlen = isSpiderHeader(t)
 | 
			
		||||
    if hdrlen == 0:
 | 
			
		||||
        t = struct.unpack('<23f', f)  # little-endian
 | 
			
		||||
        t = struct.unpack("<23f", f)  # little-endian
 | 
			
		||||
        hdrlen = isSpiderHeader(t)
 | 
			
		||||
    return hdrlen
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -104,11 +105,11 @@ class SpiderImageFile(ImageFile.ImageFile):
 | 
			
		|||
 | 
			
		||||
        try:
 | 
			
		||||
            self.bigendian = 1
 | 
			
		||||
            t = struct.unpack('>27f', f)  # try big-endian first
 | 
			
		||||
            t = struct.unpack(">27f", f)  # try big-endian first
 | 
			
		||||
            hdrlen = isSpiderHeader(t)
 | 
			
		||||
            if hdrlen == 0:
 | 
			
		||||
                self.bigendian = 0
 | 
			
		||||
                t = struct.unpack('<27f', f)  # little-endian
 | 
			
		||||
                t = struct.unpack("<27f", f)  # little-endian
 | 
			
		||||
                hdrlen = isSpiderHeader(t)
 | 
			
		||||
            if hdrlen == 0:
 | 
			
		||||
                raise SyntaxError("not a valid Spider file")
 | 
			
		||||
| 
						 | 
				
			
			@ -149,9 +150,7 @@ class SpiderImageFile(ImageFile.ImageFile):
 | 
			
		|||
            self.rawmode = "F;32F"
 | 
			
		||||
        self.mode = "F"
 | 
			
		||||
 | 
			
		||||
        self.tile = [
 | 
			
		||||
            ("raw", (0, 0) + self.size, offset,
 | 
			
		||||
                (self.rawmode, 0, 1))]
 | 
			
		||||
        self.tile = [("raw", (0, 0) + self.size, offset, (self.rawmode, 0, 1))]
 | 
			
		||||
        self.__fp = self.fp  # FIXME: hack
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
| 
						 | 
				
			
			@ -184,13 +183,14 @@ class SpiderImageFile(ImageFile.ImageFile):
 | 
			
		|||
        (minimum, maximum) = self.getextrema()
 | 
			
		||||
        m = 1
 | 
			
		||||
        if maximum != minimum:
 | 
			
		||||
            m = depth / (maximum-minimum)
 | 
			
		||||
            m = depth / (maximum - minimum)
 | 
			
		||||
        b = -m * minimum
 | 
			
		||||
        return self.point(lambda i, m=m, b=b: i * m + b).convert("L")
 | 
			
		||||
 | 
			
		||||
    # returns a ImageTk.PhotoImage object, after rescaling to 0..255
 | 
			
		||||
    def tkPhotoImage(self):
 | 
			
		||||
        from PIL import ImageTk
 | 
			
		||||
 | 
			
		||||
        return ImageTk.PhotoImage(self.convert2byte(), palette=256)
 | 
			
		||||
 | 
			
		||||
    def _close__fp(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -223,7 +223,7 @@ def loadImageSeries(filelist=None):
 | 
			
		|||
            if not isSpiderImage(img):
 | 
			
		||||
                print(img + " is not a Spider image file")
 | 
			
		||||
            continue
 | 
			
		||||
        im.info['filename'] = img
 | 
			
		||||
        im.info["filename"] = img
 | 
			
		||||
        imglist.append(im)
 | 
			
		||||
    return imglist
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -231,6 +231,7 @@ def loadImageSeries(filelist=None):
 | 
			
		|||
# --------------------------------------------------------------------
 | 
			
		||||
# For saving images in Spider format
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def makeSpiderHeader(im):
 | 
			
		||||
    nsam, nrow = im.size
 | 
			
		||||
    lenbyt = nsam * 4  # There are labrec records in the header
 | 
			
		||||
| 
						 | 
				
			
			@ -261,13 +262,13 @@ def makeSpiderHeader(im):
 | 
			
		|||
    # pack binary data into a string
 | 
			
		||||
    hdrstr = []
 | 
			
		||||
    for v in hdr:
 | 
			
		||||
        hdrstr.append(struct.pack('f', v))
 | 
			
		||||
        hdrstr.append(struct.pack("f", v))
 | 
			
		||||
    return hdrstr
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _save(im, fp, filename):
 | 
			
		||||
    if im.mode[0] != "F":
 | 
			
		||||
        im = im.convert('F')
 | 
			
		||||
        im = im.convert("F")
 | 
			
		||||
 | 
			
		||||
    hdr = makeSpiderHeader(im)
 | 
			
		||||
    if len(hdr) < 256:
 | 
			
		||||
| 
						 | 
				
			
			@ -277,7 +278,7 @@ def _save(im, fp, filename):
 | 
			
		|||
    fp.writelines(hdr)
 | 
			
		||||
 | 
			
		||||
    rawmode = "F;32NF"  # 32-bit native floating point
 | 
			
		||||
    ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0, (rawmode, 0, 1))])
 | 
			
		||||
    ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, 0, 1))])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _save_spider(im, fp, filename):
 | 
			
		||||
| 
						 | 
				
			
			@ -286,6 +287,7 @@ def _save_spider(im, fp, filename):
 | 
			
		|||
    Image.register_extension(SpiderImageFile.format, ext)
 | 
			
		||||
    _save(im, fp, filename)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# --------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -308,7 +310,7 @@ if __name__ == "__main__":
 | 
			
		|||
    print("format: " + str(im.format))
 | 
			
		||||
    print("size: " + str(im.size))
 | 
			
		||||
    print("mode: " + str(im.mode))
 | 
			
		||||
    print("max, min: ", end=' ')
 | 
			
		||||
    print("max, min: ", end=" ")
 | 
			
		||||
    print(im.getextrema())
 | 
			
		||||
 | 
			
		||||
    if len(sys.argv) > 2:
 | 
			
		||||
| 
						 | 
				
			
			@ -317,6 +319,7 @@ if __name__ == "__main__":
 | 
			
		|||
        # perform some image operation
 | 
			
		||||
        im = im.transpose(Image.FLIP_LEFT_RIGHT)
 | 
			
		||||
        print(
 | 
			
		||||
            "saving a flipped version of %s as %s " %
 | 
			
		||||
            (os.path.basename(filename), outfile))
 | 
			
		||||
            "saving a flipped version of %s as %s "
 | 
			
		||||
            % (os.path.basename(filename), outfile)
 | 
			
		||||
        )
 | 
			
		||||
        im.save(outfile, SpiderImageFile.format)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,12 +26,13 @@ __version__ = "0.3"
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
def _accept(prefix):
 | 
			
		||||
    return len(prefix) >= 4 and i32(prefix) == 0x59a66a95
 | 
			
		||||
    return len(prefix) >= 4 and i32(prefix) == 0x59A66A95
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
# Image plugin for Sun raster files.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SunImageFile(ImageFile.ImageFile):
 | 
			
		||||
 | 
			
		||||
    format = "SUN"
 | 
			
		||||
| 
						 | 
				
			
			@ -56,7 +57,7 @@ class SunImageFile(ImageFile.ImageFile):
 | 
			
		|||
 | 
			
		||||
        # HEAD
 | 
			
		||||
        s = self.fp.read(32)
 | 
			
		||||
        if i32(s) != 0x59a66a95:
 | 
			
		||||
        if i32(s) != 0x59A66A95:
 | 
			
		||||
            raise SyntaxError("not an SUN raster file")
 | 
			
		||||
 | 
			
		||||
        offset = 32
 | 
			
		||||
| 
						 | 
				
			
			@ -82,9 +83,9 @@ class SunImageFile(ImageFile.ImageFile):
 | 
			
		|||
                self.mode, rawmode = "RGB", "BGR"
 | 
			
		||||
        elif depth == 32:
 | 
			
		||||
            if file_type == 3:
 | 
			
		||||
                self.mode, rawmode = 'RGB', 'RGBX'
 | 
			
		||||
                self.mode, rawmode = "RGB", "RGBX"
 | 
			
		||||
            else:
 | 
			
		||||
                self.mode, rawmode = 'RGB', 'BGRX'
 | 
			
		||||
                self.mode, rawmode = "RGB", "BGRX"
 | 
			
		||||
        else:
 | 
			
		||||
            raise SyntaxError("Unsupported Mode/Bit Depth")
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -96,11 +97,10 @@ class SunImageFile(ImageFile.ImageFile):
 | 
			
		|||
                raise SyntaxError("Unsupported Palette Type")
 | 
			
		||||
 | 
			
		||||
            offset = offset + palette_length
 | 
			
		||||
            self.palette = ImagePalette.raw("RGB;L",
 | 
			
		||||
                                            self.fp.read(palette_length))
 | 
			
		||||
            self.palette = ImagePalette.raw("RGB;L", self.fp.read(palette_length))
 | 
			
		||||
            if self.mode == "L":
 | 
			
		||||
                self.mode = "P"
 | 
			
		||||
                rawmode = rawmode.replace('L', 'P')
 | 
			
		||||
                rawmode = rawmode.replace("L", "P")
 | 
			
		||||
 | 
			
		||||
        # 16 bit boundaries on stride
 | 
			
		||||
        stride = ((self.size[0] * depth + 15) // 16) * 2
 | 
			
		||||
| 
						 | 
				
			
			@ -124,11 +124,12 @@ class SunImageFile(ImageFile.ImageFile):
 | 
			
		|||
        # (https://www.fileformat.info/format/sunraster/egff.htm)
 | 
			
		||||
 | 
			
		||||
        if file_type in (0, 1, 3, 4, 5):
 | 
			
		||||
            self.tile = [("raw", (0, 0)+self.size, offset, (rawmode, stride))]
 | 
			
		||||
            self.tile = [("raw", (0, 0) + self.size, offset, (rawmode, stride))]
 | 
			
		||||
        elif file_type == 2:
 | 
			
		||||
            self.tile = [("sun_rle", (0, 0)+self.size, offset, rawmode)]
 | 
			
		||||
            self.tile = [("sun_rle", (0, 0) + self.size, offset, rawmode)]
 | 
			
		||||
        else:
 | 
			
		||||
            raise SyntaxError('Unsupported Sun Raster file type')
 | 
			
		||||
            raise SyntaxError("Unsupported Sun Raster file type")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# registry
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,8 +23,8 @@ from . import ContainerIO
 | 
			
		|||
# A file object that provides read access to a given member of a TAR
 | 
			
		||||
# file.
 | 
			
		||||
 | 
			
		||||
class TarIO(ContainerIO.ContainerIO):
 | 
			
		||||
 | 
			
		||||
class TarIO(ContainerIO.ContainerIO):
 | 
			
		||||
    def __init__(self, tarfile, file):
 | 
			
		||||
        """
 | 
			
		||||
        Create file object.
 | 
			
		||||
| 
						 | 
				
			
			@ -40,8 +40,8 @@ class TarIO(ContainerIO.ContainerIO):
 | 
			
		|||
            if len(s) != 512:
 | 
			
		||||
                raise IOError("unexpected end of tar file")
 | 
			
		||||
 | 
			
		||||
            name = s[:100].decode('utf-8')
 | 
			
		||||
            i = name.find('\0')
 | 
			
		||||
            name = s[:100].decode("utf-8")
 | 
			
		||||
            i = name.find("\0")
 | 
			
		||||
            if i == 0:
 | 
			
		||||
                raise IOError("cannot find subfile")
 | 
			
		||||
            if i > 0:
 | 
			
		||||
| 
						 | 
				
			
			@ -65,6 +65,7 @@ class TarIO(ContainerIO.ContainerIO):
 | 
			
		|||
        self.close()
 | 
			
		||||
 | 
			
		||||
    if sys.version_info.major >= 3:
 | 
			
		||||
 | 
			
		||||
        def __del__(self):
 | 
			
		||||
            self.close()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -47,6 +47,7 @@ MODES = {
 | 
			
		|||
##
 | 
			
		||||
# Image plugin for Targa files.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TgaImageFile(ImageFile.ImageFile):
 | 
			
		||||
 | 
			
		||||
    format = "TGA"
 | 
			
		||||
| 
						 | 
				
			
			@ -69,9 +70,12 @@ class TgaImageFile(ImageFile.ImageFile):
 | 
			
		|||
        self._size = i16(s[12:]), i16(s[14:])
 | 
			
		||||
 | 
			
		||||
        # validate header fields
 | 
			
		||||
        if colormaptype not in (0, 1) or\
 | 
			
		||||
           self.size[0] <= 0 or self.size[1] <= 0 or\
 | 
			
		||||
           depth not in (1, 8, 16, 24, 32):
 | 
			
		||||
        if (
 | 
			
		||||
            colormaptype not in (0, 1)
 | 
			
		||||
            or self.size[0] <= 0
 | 
			
		||||
            or self.size[1] <= 0
 | 
			
		||||
            or depth not in (1, 8, 16, 24, 32)
 | 
			
		||||
        ):
 | 
			
		||||
            raise SyntaxError("not a TGA file")
 | 
			
		||||
 | 
			
		||||
        # image mode
 | 
			
		||||
| 
						 | 
				
			
			@ -112,27 +116,43 @@ class TgaImageFile(ImageFile.ImageFile):
 | 
			
		|||
            start, size, mapdepth = i16(s[3:]), i16(s[5:]), i16(s[7:])
 | 
			
		||||
            if mapdepth == 16:
 | 
			
		||||
                self.palette = ImagePalette.raw(
 | 
			
		||||
                    "BGR;16", b"\0"*2*start + self.fp.read(2*size))
 | 
			
		||||
                    "BGR;16", b"\0" * 2 * start + self.fp.read(2 * size)
 | 
			
		||||
                )
 | 
			
		||||
            elif mapdepth == 24:
 | 
			
		||||
                self.palette = ImagePalette.raw(
 | 
			
		||||
                    "BGR", b"\0"*3*start + self.fp.read(3*size))
 | 
			
		||||
                    "BGR", b"\0" * 3 * start + self.fp.read(3 * size)
 | 
			
		||||
                )
 | 
			
		||||
            elif mapdepth == 32:
 | 
			
		||||
                self.palette = ImagePalette.raw(
 | 
			
		||||
                    "BGRA", b"\0"*4*start + self.fp.read(4*size))
 | 
			
		||||
                    "BGRA", b"\0" * 4 * start + self.fp.read(4 * size)
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
        # setup tile descriptor
 | 
			
		||||
        try:
 | 
			
		||||
            rawmode = MODES[(imagetype & 7, depth)]
 | 
			
		||||
            if imagetype & 8:
 | 
			
		||||
                # compressed
 | 
			
		||||
                self.tile = [("tga_rle", (0, 0)+self.size,
 | 
			
		||||
                              self.fp.tell(), (rawmode, orientation, depth))]
 | 
			
		||||
                self.tile = [
 | 
			
		||||
                    (
 | 
			
		||||
                        "tga_rle",
 | 
			
		||||
                        (0, 0) + self.size,
 | 
			
		||||
                        self.fp.tell(),
 | 
			
		||||
                        (rawmode, orientation, depth),
 | 
			
		||||
                    )
 | 
			
		||||
                ]
 | 
			
		||||
            else:
 | 
			
		||||
                self.tile = [("raw", (0, 0)+self.size,
 | 
			
		||||
                              self.fp.tell(), (rawmode, 0, orientation))]
 | 
			
		||||
                self.tile = [
 | 
			
		||||
                    (
 | 
			
		||||
                        "raw",
 | 
			
		||||
                        (0, 0) + self.size,
 | 
			
		||||
                        self.fp.tell(),
 | 
			
		||||
                        (rawmode, 0, orientation),
 | 
			
		||||
                    )
 | 
			
		||||
                ]
 | 
			
		||||
        except KeyError:
 | 
			
		||||
            pass  # cannot decode
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# --------------------------------------------------------------------
 | 
			
		||||
# Write TGA file
 | 
			
		||||
| 
						 | 
				
			
			@ -158,14 +178,12 @@ def _save(im, fp, filename):
 | 
			
		|||
    if "rle" in im.encoderinfo:
 | 
			
		||||
        rle = im.encoderinfo["rle"]
 | 
			
		||||
    else:
 | 
			
		||||
        compression = im.encoderinfo.get("compression",
 | 
			
		||||
                                         im.info.get("compression"))
 | 
			
		||||
        compression = im.encoderinfo.get("compression", im.info.get("compression"))
 | 
			
		||||
        rle = compression == "tga_rle"
 | 
			
		||||
    if rle:
 | 
			
		||||
        imagetype += 8
 | 
			
		||||
 | 
			
		||||
    id_section = im.encoderinfo.get("id_section",
 | 
			
		||||
                                    im.info.get("id_section", ""))
 | 
			
		||||
    id_section = im.encoderinfo.get("id_section", im.info.get("id_section", ""))
 | 
			
		||||
    id_len = len(id_section)
 | 
			
		||||
    if id_len > 255:
 | 
			
		||||
        id_len = 255
 | 
			
		||||
| 
						 | 
				
			
			@ -182,23 +200,24 @@ def _save(im, fp, filename):
 | 
			
		|||
    else:
 | 
			
		||||
        flags = 0
 | 
			
		||||
 | 
			
		||||
    orientation = im.encoderinfo.get("orientation",
 | 
			
		||||
                                     im.info.get("orientation", -1))
 | 
			
		||||
    orientation = im.encoderinfo.get("orientation", im.info.get("orientation", -1))
 | 
			
		||||
    if orientation > 0:
 | 
			
		||||
        flags = flags | 0x20
 | 
			
		||||
 | 
			
		||||
    fp.write(o8(id_len) +
 | 
			
		||||
             o8(colormaptype) +
 | 
			
		||||
             o8(imagetype) +
 | 
			
		||||
             o16(colormapfirst) +
 | 
			
		||||
             o16(colormaplength) +
 | 
			
		||||
             o8(colormapentry) +
 | 
			
		||||
             o16(0) +
 | 
			
		||||
             o16(0) +
 | 
			
		||||
             o16(im.size[0]) +
 | 
			
		||||
             o16(im.size[1]) +
 | 
			
		||||
             o8(bits) +
 | 
			
		||||
             o8(flags))
 | 
			
		||||
    fp.write(
 | 
			
		||||
        o8(id_len)
 | 
			
		||||
        + o8(colormaptype)
 | 
			
		||||
        + o8(imagetype)
 | 
			
		||||
        + o16(colormapfirst)
 | 
			
		||||
        + o16(colormaplength)
 | 
			
		||||
        + o8(colormapentry)
 | 
			
		||||
        + o16(0)
 | 
			
		||||
        + o16(0)
 | 
			
		||||
        + o16(im.size[0])
 | 
			
		||||
        + o16(im.size[1])
 | 
			
		||||
        + o8(bits)
 | 
			
		||||
        + o8(flags)
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    if id_section:
 | 
			
		||||
        fp.write(id_section)
 | 
			
		||||
| 
						 | 
				
			
			@ -208,16 +227,17 @@ def _save(im, fp, filename):
 | 
			
		|||
 | 
			
		||||
    if rle:
 | 
			
		||||
        ImageFile._save(
 | 
			
		||||
            im,
 | 
			
		||||
            fp,
 | 
			
		||||
            [("tga_rle", (0, 0) + im.size, 0, (rawmode, orientation))])
 | 
			
		||||
            im, fp, [("tga_rle", (0, 0) + im.size, 0, (rawmode, orientation))]
 | 
			
		||||
        )
 | 
			
		||||
    else:
 | 
			
		||||
        ImageFile._save(
 | 
			
		||||
            im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, 0, orientation))])
 | 
			
		||||
            im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, 0, orientation))]
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    # write targa version 2 footer
 | 
			
		||||
    fp.write(b"\000" * 8 + b"TRUEVISION-XFILE." + b"\000")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# --------------------------------------------------------------------
 | 
			
		||||
# Registry
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -153,7 +153,6 @@ OPEN_INFO = {
 | 
			
		|||
    (MM, 1, (1,), 1, (1,), ()): ("1", "1"),
 | 
			
		||||
    (II, 1, (1,), 2, (1,), ()): ("1", "1;R"),
 | 
			
		||||
    (MM, 1, (1,), 2, (1,), ()): ("1", "1;R"),
 | 
			
		||||
 | 
			
		||||
    (II, 0, (1,), 1, (2,), ()): ("L", "L;2I"),
 | 
			
		||||
    (MM, 0, (1,), 1, (2,), ()): ("L", "L;2I"),
 | 
			
		||||
    (II, 0, (1,), 2, (2,), ()): ("L", "L;2IR"),
 | 
			
		||||
| 
						 | 
				
			
			@ -162,7 +161,6 @@ OPEN_INFO = {
 | 
			
		|||
    (MM, 1, (1,), 1, (2,), ()): ("L", "L;2"),
 | 
			
		||||
    (II, 1, (1,), 2, (2,), ()): ("L", "L;2R"),
 | 
			
		||||
    (MM, 1, (1,), 2, (2,), ()): ("L", "L;2R"),
 | 
			
		||||
 | 
			
		||||
    (II, 0, (1,), 1, (4,), ()): ("L", "L;4I"),
 | 
			
		||||
    (MM, 0, (1,), 1, (4,), ()): ("L", "L;4I"),
 | 
			
		||||
    (II, 0, (1,), 2, (4,), ()): ("L", "L;4IR"),
 | 
			
		||||
| 
						 | 
				
			
			@ -171,7 +169,6 @@ OPEN_INFO = {
 | 
			
		|||
    (MM, 1, (1,), 1, (4,), ()): ("L", "L;4"),
 | 
			
		||||
    (II, 1, (1,), 2, (4,), ()): ("L", "L;4R"),
 | 
			
		||||
    (MM, 1, (1,), 2, (4,), ()): ("L", "L;4R"),
 | 
			
		||||
 | 
			
		||||
    (II, 0, (1,), 1, (8,), ()): ("L", "L;I"),
 | 
			
		||||
    (MM, 0, (1,), 1, (8,), ()): ("L", "L;I"),
 | 
			
		||||
    (II, 0, (1,), 2, (8,), ()): ("L", "L;IR"),
 | 
			
		||||
| 
						 | 
				
			
			@ -180,14 +177,11 @@ OPEN_INFO = {
 | 
			
		|||
    (MM, 1, (1,), 1, (8,), ()): ("L", "L"),
 | 
			
		||||
    (II, 1, (1,), 2, (8,), ()): ("L", "L;R"),
 | 
			
		||||
    (MM, 1, (1,), 2, (8,), ()): ("L", "L;R"),
 | 
			
		||||
 | 
			
		||||
    (II, 1, (1,), 1, (12,), ()): ("I;16", "I;12"),
 | 
			
		||||
 | 
			
		||||
    (II, 1, (1,), 1, (16,), ()): ("I;16", "I;16"),
 | 
			
		||||
    (MM, 1, (1,), 1, (16,), ()): ("I;16B", "I;16B"),
 | 
			
		||||
    (II, 1, (2,), 1, (16,), ()): ("I", "I;16S"),
 | 
			
		||||
    (MM, 1, (2,), 1, (16,), ()): ("I", "I;16BS"),
 | 
			
		||||
 | 
			
		||||
    (II, 0, (3,), 1, (32,), ()): ("F", "F;32F"),
 | 
			
		||||
    (MM, 0, (3,), 1, (32,), ()): ("F", "F;32BF"),
 | 
			
		||||
    (II, 1, (1,), 1, (32,), ()): ("I", "I;32N"),
 | 
			
		||||
| 
						 | 
				
			
			@ -195,10 +189,8 @@ OPEN_INFO = {
 | 
			
		|||
    (MM, 1, (2,), 1, (32,), ()): ("I", "I;32BS"),
 | 
			
		||||
    (II, 1, (3,), 1, (32,), ()): ("F", "F;32F"),
 | 
			
		||||
    (MM, 1, (3,), 1, (32,), ()): ("F", "F;32BF"),
 | 
			
		||||
 | 
			
		||||
    (II, 1, (1,), 1, (8, 8), (2,)): ("LA", "LA"),
 | 
			
		||||
    (MM, 1, (1,), 1, (8, 8), (2,)): ("LA", "LA"),
 | 
			
		||||
 | 
			
		||||
    (II, 2, (1,), 1, (8, 8, 8), ()): ("RGB", "RGB"),
 | 
			
		||||
    (MM, 2, (1,), 1, (8, 8, 8), ()): ("RGB", "RGB"),
 | 
			
		||||
    (II, 2, (1,), 2, (8, 8, 8), ()): ("RGB", "RGB;R"),
 | 
			
		||||
| 
						 | 
				
			
			@ -225,7 +217,6 @@ OPEN_INFO = {
 | 
			
		|||
    (MM, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (2, 0, 0)): ("RGBA", "RGBAXX"),
 | 
			
		||||
    (II, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"),  # Corel Draw 10
 | 
			
		||||
    (MM, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"),  # Corel Draw 10
 | 
			
		||||
 | 
			
		||||
    (II, 2, (1,), 1, (16, 16, 16), ()): ("RGB", "RGB;16L"),
 | 
			
		||||
    (MM, 2, (1,), 1, (16, 16, 16), ()): ("RGB", "RGB;16B"),
 | 
			
		||||
    (II, 2, (1,), 1, (16, 16, 16, 16), ()): ("RGBA", "RGBA;16L"),
 | 
			
		||||
| 
						 | 
				
			
			@ -236,7 +227,6 @@ OPEN_INFO = {
 | 
			
		|||
    (MM, 2, (1,), 1, (16, 16, 16, 16), (1,)): ("RGBA", "RGBa;16B"),
 | 
			
		||||
    (II, 2, (1,), 1, (16, 16, 16, 16), (2,)): ("RGBA", "RGBA;16L"),
 | 
			
		||||
    (MM, 2, (1,), 1, (16, 16, 16, 16), (2,)): ("RGBA", "RGBA;16B"),
 | 
			
		||||
 | 
			
		||||
    (II, 3, (1,), 1, (1,), ()): ("P", "P;1"),
 | 
			
		||||
    (MM, 3, (1,), 1, (1,), ()): ("P", "P;1"),
 | 
			
		||||
    (II, 3, (1,), 2, (1,), ()): ("P", "P;1R"),
 | 
			
		||||
| 
						 | 
				
			
			@ -255,21 +245,17 @@ OPEN_INFO = {
 | 
			
		|||
    (MM, 3, (1,), 1, (8, 8), (2,)): ("PA", "PA"),
 | 
			
		||||
    (II, 3, (1,), 2, (8,), ()): ("P", "P;R"),
 | 
			
		||||
    (MM, 3, (1,), 2, (8,), ()): ("P", "P;R"),
 | 
			
		||||
 | 
			
		||||
    (II, 5, (1,), 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"),
 | 
			
		||||
    (MM, 5, (1,), 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"),
 | 
			
		||||
    (II, 5, (1,), 1, (8, 8, 8, 8, 8), (0,)): ("CMYK", "CMYKX"),
 | 
			
		||||
    (MM, 5, (1,), 1, (8, 8, 8, 8, 8), (0,)): ("CMYK", "CMYKX"),
 | 
			
		||||
    (II, 5, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0)): ("CMYK", "CMYKXX"),
 | 
			
		||||
    (MM, 5, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0)): ("CMYK", "CMYKXX"),
 | 
			
		||||
 | 
			
		||||
    (II, 5, (1,), 1, (16, 16, 16, 16), ()): ("CMYK", "CMYK;16L"),
 | 
			
		||||
 | 
			
		||||
    # JPEG compressed images handled by LibTiff and auto-converted to RGBX
 | 
			
		||||
    # Minimal Baseline TIFF requires YCbCr images to have 3 SamplesPerPixel
 | 
			
		||||
    (II, 6, (1,), 1, (8, 8, 8), ()): ("RGB", "RGBX"),
 | 
			
		||||
    (MM, 6, (1,), 1, (8, 8, 8), ()): ("RGB", "RGBX"),
 | 
			
		||||
 | 
			
		||||
    (II, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"),
 | 
			
		||||
    (MM, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"),
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -315,7 +301,7 @@ class IFDRational(Rational):
 | 
			
		|||
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    __slots__ = ('_numerator', '_denominator', '_val')
 | 
			
		||||
    __slots__ = ("_numerator", "_denominator", "_val")
 | 
			
		||||
 | 
			
		||||
    def __init__(self, value, denominator=1):
 | 
			
		||||
        """
 | 
			
		||||
| 
						 | 
				
			
			@ -339,7 +325,7 @@ class IFDRational(Rational):
 | 
			
		|||
            return
 | 
			
		||||
 | 
			
		||||
        if denominator == 0:
 | 
			
		||||
            self._val = float('nan')
 | 
			
		||||
            self._val = float("nan")
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        elif denominator == 1:
 | 
			
		||||
| 
						 | 
				
			
			@ -380,6 +366,7 @@ class IFDRational(Rational):
 | 
			
		|||
    def _delegate(op):
 | 
			
		||||
        def delegate(self, *args):
 | 
			
		||||
            return getattr(self._val, op)(*args)
 | 
			
		||||
 | 
			
		||||
        return delegate
 | 
			
		||||
 | 
			
		||||
    """ a = ['add','radd', 'sub', 'rsub','div', 'rdiv', 'mul', 'rmul',
 | 
			
		||||
| 
						 | 
				
			
			@ -390,34 +377,34 @@ class IFDRational(Rational):
 | 
			
		|||
        print("\n".join("__%s__ = _delegate('__%s__')" % (s,s) for s in a))
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
    __add__ = _delegate('__add__')
 | 
			
		||||
    __radd__ = _delegate('__radd__')
 | 
			
		||||
    __sub__ = _delegate('__sub__')
 | 
			
		||||
    __rsub__ = _delegate('__rsub__')
 | 
			
		||||
    __div__ = _delegate('__div__')
 | 
			
		||||
    __rdiv__ = _delegate('__rdiv__')
 | 
			
		||||
    __mul__ = _delegate('__mul__')
 | 
			
		||||
    __rmul__ = _delegate('__rmul__')
 | 
			
		||||
    __truediv__ = _delegate('__truediv__')
 | 
			
		||||
    __rtruediv__ = _delegate('__rtruediv__')
 | 
			
		||||
    __floordiv__ = _delegate('__floordiv__')
 | 
			
		||||
    __rfloordiv__ = _delegate('__rfloordiv__')
 | 
			
		||||
    __mod__ = _delegate('__mod__')
 | 
			
		||||
    __rmod__ = _delegate('__rmod__')
 | 
			
		||||
    __pow__ = _delegate('__pow__')
 | 
			
		||||
    __rpow__ = _delegate('__rpow__')
 | 
			
		||||
    __pos__ = _delegate('__pos__')
 | 
			
		||||
    __neg__ = _delegate('__neg__')
 | 
			
		||||
    __abs__ = _delegate('__abs__')
 | 
			
		||||
    __trunc__ = _delegate('__trunc__')
 | 
			
		||||
    __lt__ = _delegate('__lt__')
 | 
			
		||||
    __gt__ = _delegate('__gt__')
 | 
			
		||||
    __le__ = _delegate('__le__')
 | 
			
		||||
    __ge__ = _delegate('__ge__')
 | 
			
		||||
    __nonzero__ = _delegate('__nonzero__')
 | 
			
		||||
    __ceil__ = _delegate('__ceil__')
 | 
			
		||||
    __floor__ = _delegate('__floor__')
 | 
			
		||||
    __round__ = _delegate('__round__')
 | 
			
		||||
    __add__ = _delegate("__add__")
 | 
			
		||||
    __radd__ = _delegate("__radd__")
 | 
			
		||||
    __sub__ = _delegate("__sub__")
 | 
			
		||||
    __rsub__ = _delegate("__rsub__")
 | 
			
		||||
    __div__ = _delegate("__div__")
 | 
			
		||||
    __rdiv__ = _delegate("__rdiv__")
 | 
			
		||||
    __mul__ = _delegate("__mul__")
 | 
			
		||||
    __rmul__ = _delegate("__rmul__")
 | 
			
		||||
    __truediv__ = _delegate("__truediv__")
 | 
			
		||||
    __rtruediv__ = _delegate("__rtruediv__")
 | 
			
		||||
    __floordiv__ = _delegate("__floordiv__")
 | 
			
		||||
    __rfloordiv__ = _delegate("__rfloordiv__")
 | 
			
		||||
    __mod__ = _delegate("__mod__")
 | 
			
		||||
    __rmod__ = _delegate("__rmod__")
 | 
			
		||||
    __pow__ = _delegate("__pow__")
 | 
			
		||||
    __rpow__ = _delegate("__rpow__")
 | 
			
		||||
    __pos__ = _delegate("__pos__")
 | 
			
		||||
    __neg__ = _delegate("__neg__")
 | 
			
		||||
    __abs__ = _delegate("__abs__")
 | 
			
		||||
    __trunc__ = _delegate("__trunc__")
 | 
			
		||||
    __lt__ = _delegate("__lt__")
 | 
			
		||||
    __gt__ = _delegate("__gt__")
 | 
			
		||||
    __le__ = _delegate("__le__")
 | 
			
		||||
    __ge__ = _delegate("__ge__")
 | 
			
		||||
    __nonzero__ = _delegate("__nonzero__")
 | 
			
		||||
    __ceil__ = _delegate("__ceil__")
 | 
			
		||||
    __floor__ = _delegate("__floor__")
 | 
			
		||||
    __round__ = _delegate("__round__")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ImageFileDirectory_v2(MutableMapping):
 | 
			
		||||
| 
						 | 
				
			
			@ -451,6 +438,7 @@ class ImageFileDirectory_v2(MutableMapping):
 | 
			
		|||
 | 
			
		||||
    .. versionadded:: 3.0.0
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    Documentation:
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -536,13 +524,14 @@ class ImageFileDirectory_v2(MutableMapping):
 | 
			
		|||
            self[tag] = handler(self, data, self.legacy_api)  # check type
 | 
			
		||||
        val = self._tags_v2[tag]
 | 
			
		||||
        if self.legacy_api and not isinstance(val, (tuple, bytes)):
 | 
			
		||||
            val = val,
 | 
			
		||||
            val = (val,)
 | 
			
		||||
        return val
 | 
			
		||||
 | 
			
		||||
    def __contains__(self, tag):
 | 
			
		||||
        return tag in self._tags_v2 or tag in self._tagdata
 | 
			
		||||
 | 
			
		||||
    if not py3:
 | 
			
		||||
 | 
			
		||||
        def has_key(self, tag):
 | 
			
		||||
            return tag in self
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -552,7 +541,7 @@ class ImageFileDirectory_v2(MutableMapping):
 | 
			
		|||
    def _setitem(self, tag, value, legacy_api):
 | 
			
		||||
        basetypes = (Number, bytes, str)
 | 
			
		||||
        if not py3:
 | 
			
		||||
            basetypes += unicode,  # noqa: F821
 | 
			
		||||
            basetypes += (unicode,)  # noqa: F821
 | 
			
		||||
 | 
			
		||||
        info = TiffTags.lookup(tag)
 | 
			
		||||
        values = [value] if isinstance(value, basetypes) else value
 | 
			
		||||
| 
						 | 
				
			
			@ -580,11 +569,11 @@ class ImageFileDirectory_v2(MutableMapping):
 | 
			
		|||
                        self.tagtype[tag] = TiffTags.ASCII
 | 
			
		||||
 | 
			
		||||
        if self.tagtype[tag] == TiffTags.UNDEFINED and py3:
 | 
			
		||||
            values = [value.encode("ascii", 'replace') if isinstance(
 | 
			
		||||
                      value, str) else value]
 | 
			
		||||
            values = [
 | 
			
		||||
                value.encode("ascii", "replace") if isinstance(value, str) else value
 | 
			
		||||
            ]
 | 
			
		||||
        elif self.tagtype[tag] == TiffTags.RATIONAL:
 | 
			
		||||
            values = [float(v) if isinstance(v, int) else v
 | 
			
		||||
                      for v in values]
 | 
			
		||||
            values = [float(v) if isinstance(v, int) else v for v in values]
 | 
			
		||||
 | 
			
		||||
        values = tuple(info.cvt_enum(value) for value in values)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -595,22 +584,23 @@ class ImageFileDirectory_v2(MutableMapping):
 | 
			
		|||
        # Spec'd length == 1, Actual > 1, Warn and truncate. Formerly barfed.
 | 
			
		||||
        # No Spec, Actual length 1, Formerly (<4.2) returned a 1 element tuple.
 | 
			
		||||
        # Don't mess with the legacy api, since it's frozen.
 | 
			
		||||
        if (info.length == 1) or \
 | 
			
		||||
           (info.length is None and len(values) == 1 and not legacy_api):
 | 
			
		||||
        if (info.length == 1) or (
 | 
			
		||||
            info.length is None and len(values) == 1 and not legacy_api
 | 
			
		||||
        ):
 | 
			
		||||
            # Don't mess with the legacy api, since it's frozen.
 | 
			
		||||
            if legacy_api and self.tagtype[tag] in [
 | 
			
		||||
                TiffTags.RATIONAL,
 | 
			
		||||
                TiffTags.SIGNED_RATIONAL
 | 
			
		||||
                TiffTags.SIGNED_RATIONAL,
 | 
			
		||||
            ]:  # rationals
 | 
			
		||||
                values = values,
 | 
			
		||||
                values = (values,)
 | 
			
		||||
            try:
 | 
			
		||||
                dest[tag], = values
 | 
			
		||||
            except ValueError:
 | 
			
		||||
                # We've got a builtin tag with 1 expected entry
 | 
			
		||||
                warnings.warn(
 | 
			
		||||
                    "Metadata Warning, tag %s had too many entries: "
 | 
			
		||||
                    "%s, expected 1" % (
 | 
			
		||||
                        tag, len(values)))
 | 
			
		||||
                    "Metadata Warning, tag %s had too many entries: %s, expected 1"
 | 
			
		||||
                    % (tag, len(values))
 | 
			
		||||
                )
 | 
			
		||||
                dest[tag] = values[0]
 | 
			
		||||
 | 
			
		||||
        else:
 | 
			
		||||
| 
						 | 
				
			
			@ -635,36 +625,51 @@ class ImageFileDirectory_v2(MutableMapping):
 | 
			
		|||
    def _register_loader(idx, size):
 | 
			
		||||
        def decorator(func):
 | 
			
		||||
            from .TiffTags import TYPES
 | 
			
		||||
 | 
			
		||||
            if func.__name__.startswith("load_"):
 | 
			
		||||
                TYPES[idx] = func.__name__[5:].replace("_", " ")
 | 
			
		||||
            _load_dispatch[idx] = size, func  # noqa: F821
 | 
			
		||||
            return func
 | 
			
		||||
 | 
			
		||||
        return decorator
 | 
			
		||||
 | 
			
		||||
    def _register_writer(idx):
 | 
			
		||||
        def decorator(func):
 | 
			
		||||
            _write_dispatch[idx] = func  # noqa: F821
 | 
			
		||||
            return func
 | 
			
		||||
 | 
			
		||||
        return decorator
 | 
			
		||||
 | 
			
		||||
    def _register_basic(idx_fmt_name):
 | 
			
		||||
        from .TiffTags import TYPES
 | 
			
		||||
 | 
			
		||||
        idx, fmt, name = idx_fmt_name
 | 
			
		||||
        TYPES[idx] = name
 | 
			
		||||
        size = struct.calcsize("=" + fmt)
 | 
			
		||||
        _load_dispatch[idx] = size, lambda self, data, legacy_api=True: (  # noqa: F821
 | 
			
		||||
            self._unpack("{}{}".format(len(data) // size, fmt), data))
 | 
			
		||||
        _load_dispatch[idx] = (  # noqa: F821
 | 
			
		||||
            size,
 | 
			
		||||
            lambda self, data, legacy_api=True: (
 | 
			
		||||
                self._unpack("{}{}".format(len(data) // size, fmt), data)
 | 
			
		||||
            ),
 | 
			
		||||
        )
 | 
			
		||||
        _write_dispatch[idx] = lambda self, *values: (  # noqa: F821
 | 
			
		||||
            b"".join(self._pack(fmt, value) for value in values))
 | 
			
		||||
            b"".join(self._pack(fmt, value) for value in values)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    list(map(_register_basic,
 | 
			
		||||
             [(TiffTags.SHORT, "H", "short"),
 | 
			
		||||
    list(
 | 
			
		||||
        map(
 | 
			
		||||
            _register_basic,
 | 
			
		||||
            [
 | 
			
		||||
                (TiffTags.SHORT, "H", "short"),
 | 
			
		||||
                (TiffTags.LONG, "L", "long"),
 | 
			
		||||
                (TiffTags.SIGNED_BYTE, "b", "signed byte"),
 | 
			
		||||
                (TiffTags.SIGNED_SHORT, "h", "signed short"),
 | 
			
		||||
                (TiffTags.SIGNED_LONG, "l", "signed long"),
 | 
			
		||||
                (TiffTags.FLOAT, "f", "float"),
 | 
			
		||||
              (TiffTags.DOUBLE, "d", "double")]))
 | 
			
		||||
                (TiffTags.DOUBLE, "d", "double"),
 | 
			
		||||
            ],
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    @_register_loader(1, 1)  # Basic type, except for the legacy API.
 | 
			
		||||
    def load_byte(self, data, legacy_api=True):
 | 
			
		||||
| 
						 | 
				
			
			@ -684,21 +689,23 @@ class ImageFileDirectory_v2(MutableMapping):
 | 
			
		|||
    def write_string(self, value):
 | 
			
		||||
        # remerge of https://github.com/python-pillow/Pillow/pull/1416
 | 
			
		||||
        if sys.version_info.major == 2:
 | 
			
		||||
            value = value.decode('ascii', 'replace')
 | 
			
		||||
        return b"" + value.encode('ascii', 'replace') + b"\0"
 | 
			
		||||
            value = value.decode("ascii", "replace")
 | 
			
		||||
        return b"" + value.encode("ascii", "replace") + b"\0"
 | 
			
		||||
 | 
			
		||||
    @_register_loader(5, 8)
 | 
			
		||||
    def load_rational(self, data, legacy_api=True):
 | 
			
		||||
        vals = self._unpack("{}L".format(len(data) // 4), data)
 | 
			
		||||
 | 
			
		||||
        def combine(a, b): return (a, b) if legacy_api else IFDRational(a, b)
 | 
			
		||||
        return tuple(combine(num, denom)
 | 
			
		||||
                     for num, denom in zip(vals[::2], vals[1::2]))
 | 
			
		||||
        def combine(a, b):
 | 
			
		||||
            return (a, b) if legacy_api else IFDRational(a, b)
 | 
			
		||||
 | 
			
		||||
        return tuple(combine(num, denom) for num, denom in zip(vals[::2], vals[1::2]))
 | 
			
		||||
 | 
			
		||||
    @_register_writer(5)
 | 
			
		||||
    def write_rational(self, *values):
 | 
			
		||||
        return b"".join(self._pack("2L", *_limit_rational(frac, 2 ** 31))
 | 
			
		||||
                        for frac in values)
 | 
			
		||||
        return b"".join(
 | 
			
		||||
            self._pack("2L", *_limit_rational(frac, 2 ** 31)) for frac in values
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    @_register_loader(7, 1)
 | 
			
		||||
    def load_undefined(self, data, legacy_api=True):
 | 
			
		||||
| 
						 | 
				
			
			@ -712,21 +719,24 @@ class ImageFileDirectory_v2(MutableMapping):
 | 
			
		|||
    def load_signed_rational(self, data, legacy_api=True):
 | 
			
		||||
        vals = self._unpack("{}l".format(len(data) // 4), data)
 | 
			
		||||
 | 
			
		||||
        def combine(a, b): return (a, b) if legacy_api else IFDRational(a, b)
 | 
			
		||||
        return tuple(combine(num, denom)
 | 
			
		||||
                     for num, denom in zip(vals[::2], vals[1::2]))
 | 
			
		||||
        def combine(a, b):
 | 
			
		||||
            return (a, b) if legacy_api else IFDRational(a, b)
 | 
			
		||||
 | 
			
		||||
        return tuple(combine(num, denom) for num, denom in zip(vals[::2], vals[1::2]))
 | 
			
		||||
 | 
			
		||||
    @_register_writer(10)
 | 
			
		||||
    def write_signed_rational(self, *values):
 | 
			
		||||
        return b"".join(self._pack("2L", *_limit_rational(frac, 2 ** 30))
 | 
			
		||||
                        for frac in values)
 | 
			
		||||
        return b"".join(
 | 
			
		||||
            self._pack("2L", *_limit_rational(frac, 2 ** 30)) for frac in values
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def _ensure_read(self, fp, size):
 | 
			
		||||
        ret = fp.read(size)
 | 
			
		||||
        if len(ret) != size:
 | 
			
		||||
            raise IOError("Corrupt EXIF data.  " +
 | 
			
		||||
                          "Expecting to read %d bytes but only got %d. " %
 | 
			
		||||
                          (size, len(ret)))
 | 
			
		||||
            raise IOError(
 | 
			
		||||
                "Corrupt EXIF data.  "
 | 
			
		||||
                + "Expecting to read %d bytes but only got %d. " % (size, len(ret))
 | 
			
		||||
            )
 | 
			
		||||
        return ret
 | 
			
		||||
 | 
			
		||||
    def load(self, fp):
 | 
			
		||||
| 
						 | 
				
			
			@ -736,13 +746,14 @@ class ImageFileDirectory_v2(MutableMapping):
 | 
			
		|||
 | 
			
		||||
        try:
 | 
			
		||||
            for i in range(self._unpack("H", self._ensure_read(fp, 2))[0]):
 | 
			
		||||
                tag, typ, count, data = self._unpack("HHL4s",
 | 
			
		||||
                                                     self._ensure_read(fp, 12))
 | 
			
		||||
                tag, typ, count, data = self._unpack("HHL4s", self._ensure_read(fp, 12))
 | 
			
		||||
                if DEBUG:
 | 
			
		||||
                    tagname = TiffTags.lookup(tag).name
 | 
			
		||||
                    typname = TYPES.get(typ, "unknown")
 | 
			
		||||
                    print("tag: %s (%d) - type: %s (%d)" %
 | 
			
		||||
                          (tagname, tag, typname, typ), end=" ")
 | 
			
		||||
                    print(
 | 
			
		||||
                        "tag: %s (%d) - type: %s (%d)" % (tagname, tag, typname, typ),
 | 
			
		||||
                        end=" ",
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
                try:
 | 
			
		||||
                    unit_size, handler = self._load_dispatch[typ]
 | 
			
		||||
| 
						 | 
				
			
			@ -755,8 +766,10 @@ class ImageFileDirectory_v2(MutableMapping):
 | 
			
		|||
                    here = fp.tell()
 | 
			
		||||
                    offset, = self._unpack("L", data)
 | 
			
		||||
                    if DEBUG:
 | 
			
		||||
                        print("Tag Location: %s - Data Location: %s" %
 | 
			
		||||
                              (here, offset), end=" ")
 | 
			
		||||
                        print(
 | 
			
		||||
                            "Tag Location: %s - Data Location: %s" % (here, offset),
 | 
			
		||||
                            end=" ",
 | 
			
		||||
                        )
 | 
			
		||||
                    fp.seek(offset)
 | 
			
		||||
                    data = ImageFile._safe_read(fp, size)
 | 
			
		||||
                    fp.seek(here)
 | 
			
		||||
| 
						 | 
				
			
			@ -764,9 +777,11 @@ class ImageFileDirectory_v2(MutableMapping):
 | 
			
		|||
                    data = data[:size]
 | 
			
		||||
 | 
			
		||||
                if len(data) != size:
 | 
			
		||||
                    warnings.warn("Possibly corrupt EXIF data.  "
 | 
			
		||||
                    warnings.warn(
 | 
			
		||||
                        "Possibly corrupt EXIF data.  "
 | 
			
		||||
                        "Expecting to read %d bytes but only got %d."
 | 
			
		||||
                                  " Skipping tag %s" % (size, len(data), tag))
 | 
			
		||||
                        " Skipping tag %s" % (size, len(data), tag)
 | 
			
		||||
                    )
 | 
			
		||||
                    continue
 | 
			
		||||
 | 
			
		||||
                if not data:
 | 
			
		||||
| 
						 | 
				
			
			@ -807,8 +822,10 @@ class ImageFileDirectory_v2(MutableMapping):
 | 
			
		|||
            if DEBUG:
 | 
			
		||||
                tagname = TiffTags.lookup(tag).name
 | 
			
		||||
                typname = TYPES.get(typ, "unknown")
 | 
			
		||||
                print("save: %s (%d) - type: %s (%d)" %
 | 
			
		||||
                      (tagname, tag, typname, typ), end=" ")
 | 
			
		||||
                print(
 | 
			
		||||
                    "save: %s (%d) - type: %s (%d)" % (tagname, tag, typname, typ),
 | 
			
		||||
                    end=" ",
 | 
			
		||||
                )
 | 
			
		||||
                if len(data) >= 16:
 | 
			
		||||
                    print("- value: <table: %d bytes>" % len(data))
 | 
			
		||||
                else:
 | 
			
		||||
| 
						 | 
				
			
			@ -823,16 +840,14 @@ class ImageFileDirectory_v2(MutableMapping):
 | 
			
		|||
            if len(data) <= 4:
 | 
			
		||||
                entries.append((tag, typ, count, data.ljust(4, b"\0"), b""))
 | 
			
		||||
            else:
 | 
			
		||||
                entries.append((tag, typ, count, self._pack("L", offset),
 | 
			
		||||
                                data))
 | 
			
		||||
                entries.append((tag, typ, count, self._pack("L", offset), data))
 | 
			
		||||
                offset += (len(data) + 1) // 2 * 2  # pad to word
 | 
			
		||||
 | 
			
		||||
        # update strip offset data to point beyond auxiliary data
 | 
			
		||||
        if stripoffsets is not None:
 | 
			
		||||
            tag, typ, count, value, data = entries[stripoffsets]
 | 
			
		||||
            if data:
 | 
			
		||||
                raise NotImplementedError(
 | 
			
		||||
                    "multistrip support not yet implemented")
 | 
			
		||||
                raise NotImplementedError("multistrip support not yet implemented")
 | 
			
		||||
            value = self._pack("L", self._unpack("L", value)[0] + offset)
 | 
			
		||||
            entries[stripoffsets] = tag, typ, count, value, data
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -893,6 +908,7 @@ class ImageFileDirectory_v1(ImageFileDirectory_v2):
 | 
			
		|||
 | 
			
		||||
    ..  deprecated:: 3.0.0
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        ImageFileDirectory_v2.__init__(self, *args, **kwargs)
 | 
			
		||||
        self._legacy_api = True
 | 
			
		||||
| 
						 | 
				
			
			@ -957,7 +973,7 @@ class ImageFileDirectory_v1(ImageFileDirectory_v2):
 | 
			
		|||
                self._setitem(tag, handler(self, data, legacy), legacy)
 | 
			
		||||
        val = self._tags_v1[tag]
 | 
			
		||||
        if not isinstance(val, (tuple, bytes)):
 | 
			
		||||
            val = val,
 | 
			
		||||
            val = (val,)
 | 
			
		||||
        return val
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -968,6 +984,7 @@ ImageFileDirectory = ImageFileDirectory_v1
 | 
			
		|||
##
 | 
			
		||||
# Image plugin for TIFF files.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TiffImageFile(ImageFile.ImageFile):
 | 
			
		||||
 | 
			
		||||
    format = "TIFF"
 | 
			
		||||
| 
						 | 
				
			
			@ -1032,9 +1049,10 @@ class TiffImageFile(ImageFile.ImageFile):
 | 
			
		|||
            if not self.__next:
 | 
			
		||||
                raise EOFError("no more images in TIFF file")
 | 
			
		||||
            if DEBUG:
 | 
			
		||||
                print("Seeking to frame %s, on frame %s, "
 | 
			
		||||
                      "__next %s, location: %s" %
 | 
			
		||||
                      (frame, self.__frame, self.__next, self.fp.tell()))
 | 
			
		||||
                print(
 | 
			
		||||
                    "Seeking to frame %s, on frame %s, __next %s, location: %s"
 | 
			
		||||
                    % (frame, self.__frame, self.__next, self.fp.tell())
 | 
			
		||||
                )
 | 
			
		||||
            # reset python3 buffered io handle in case fp
 | 
			
		||||
            # was passed to libtiff, invalidating the buffer
 | 
			
		||||
            self.fp.tell()
 | 
			
		||||
| 
						 | 
				
			
			@ -1067,9 +1085,9 @@ class TiffImageFile(ImageFile.ImageFile):
 | 
			
		|||
    @size.setter
 | 
			
		||||
    def size(self, value):
 | 
			
		||||
        warnings.warn(
 | 
			
		||||
            'Setting the size of a TIFF image directly is deprecated, and will'
 | 
			
		||||
            ' be removed in a future version. Use the resize method instead.',
 | 
			
		||||
            DeprecationWarning
 | 
			
		||||
            "Setting the size of a TIFF image directly is deprecated, and will"
 | 
			
		||||
            " be removed in a future version. Use the resize method instead.",
 | 
			
		||||
            DeprecationWarning,
 | 
			
		||||
        )
 | 
			
		||||
        self._size = value
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1124,8 +1142,9 @@ class TiffImageFile(ImageFile.ImageFile):
 | 
			
		|||
        if fp:
 | 
			
		||||
            args[2] = fp
 | 
			
		||||
 | 
			
		||||
        decoder = Image._getdecoder(self.mode, 'libtiff', tuple(args),
 | 
			
		||||
                                    self.decoderconfig)
 | 
			
		||||
        decoder = Image._getdecoder(
 | 
			
		||||
            self.mode, "libtiff", tuple(args), self.decoderconfig
 | 
			
		||||
        )
 | 
			
		||||
        try:
 | 
			
		||||
            decoder.setimage(self.im, extents)
 | 
			
		||||
        except ValueError:
 | 
			
		||||
| 
						 | 
				
			
			@ -1207,8 +1226,7 @@ class TiffImageFile(ImageFile.ImageFile):
 | 
			
		|||
            print("- size:", self.size)
 | 
			
		||||
 | 
			
		||||
        sampleFormat = self.tag_v2.get(SAMPLEFORMAT, (1,))
 | 
			
		||||
        if (len(sampleFormat) > 1
 | 
			
		||||
           and max(sampleFormat) == min(sampleFormat) == 1):
 | 
			
		||||
        if len(sampleFormat) > 1 and max(sampleFormat) == min(sampleFormat) == 1:
 | 
			
		||||
            # SAMPLEFORMAT is properly per band, so an RGB image will
 | 
			
		||||
            # be (1,1,1).  But, we don't support per band pixel types,
 | 
			
		||||
            # and anything more than one band is a uint8. So, just
 | 
			
		||||
| 
						 | 
				
			
			@ -1231,8 +1249,14 @@ class TiffImageFile(ImageFile.ImageFile):
 | 
			
		|||
            bps_tuple = bps_tuple * bps_count
 | 
			
		||||
 | 
			
		||||
        # mode: check photometric interpretation and bits per pixel
 | 
			
		||||
        key = (self.tag_v2.prefix, photo, sampleFormat, fillorder,
 | 
			
		||||
               bps_tuple, extra_tuple)
 | 
			
		||||
        key = (
 | 
			
		||||
            self.tag_v2.prefix,
 | 
			
		||||
            photo,
 | 
			
		||||
            sampleFormat,
 | 
			
		||||
            fillorder,
 | 
			
		||||
            bps_tuple,
 | 
			
		||||
            extra_tuple,
 | 
			
		||||
        )
 | 
			
		||||
        if DEBUG:
 | 
			
		||||
            print("format key:", key)
 | 
			
		||||
        try:
 | 
			
		||||
| 
						 | 
				
			
			@ -1268,7 +1292,7 @@ class TiffImageFile(ImageFile.ImageFile):
 | 
			
		|||
        # build tile descriptors
 | 
			
		||||
        x = y = layer = 0
 | 
			
		||||
        self.tile = []
 | 
			
		||||
        self.use_load_libtiff = READ_LIBTIFF or self._compression != 'raw'
 | 
			
		||||
        self.use_load_libtiff = READ_LIBTIFF or self._compression != "raw"
 | 
			
		||||
        if self.use_load_libtiff:
 | 
			
		||||
            # Decoder expects entire file as one tile.
 | 
			
		||||
            # There's a buffer size limit in load (64k)
 | 
			
		||||
| 
						 | 
				
			
			@ -1295,20 +1319,17 @@ class TiffImageFile(ImageFile.ImageFile):
 | 
			
		|||
            # we're expecting image byte order. So, if the rawmode
 | 
			
		||||
            # contains I;16, we need to convert from native to image
 | 
			
		||||
            # byte order.
 | 
			
		||||
            if rawmode == 'I;16':
 | 
			
		||||
                rawmode = 'I;16N'
 | 
			
		||||
            if ';16B' in rawmode:
 | 
			
		||||
                rawmode = rawmode.replace(';16B', ';16N')
 | 
			
		||||
            if ';16L' in rawmode:
 | 
			
		||||
                rawmode = rawmode.replace(';16L', ';16N')
 | 
			
		||||
            if rawmode == "I;16":
 | 
			
		||||
                rawmode = "I;16N"
 | 
			
		||||
            if ";16B" in rawmode:
 | 
			
		||||
                rawmode = rawmode.replace(";16B", ";16N")
 | 
			
		||||
            if ";16L" in rawmode:
 | 
			
		||||
                rawmode = rawmode.replace(";16L", ";16N")
 | 
			
		||||
 | 
			
		||||
            # Offset in the tile tuple is 0, we go from 0,0 to
 | 
			
		||||
            # w,h, and we only do this once -- eds
 | 
			
		||||
            a = (rawmode, self._compression, False)
 | 
			
		||||
            self.tile.append(
 | 
			
		||||
                (self._compression,
 | 
			
		||||
                 (0, 0, xsize, ysize),
 | 
			
		||||
                 0, a))
 | 
			
		||||
            self.tile.append((self._compression, (0, 0, xsize, ysize), 0, a))
 | 
			
		||||
 | 
			
		||||
        elif STRIPOFFSETS in self.tag_v2 or TILEOFFSETS in self.tag_v2:
 | 
			
		||||
            # striped image
 | 
			
		||||
| 
						 | 
				
			
			@ -1337,9 +1358,13 @@ class TiffImageFile(ImageFile.ImageFile):
 | 
			
		|||
 | 
			
		||||
                a = (tile_rawmode, int(stride), 1)
 | 
			
		||||
                self.tile.append(
 | 
			
		||||
                    (self._compression,
 | 
			
		||||
                     (x, y, min(x+w, xsize), min(y+h, ysize)),
 | 
			
		||||
                     offset, a))
 | 
			
		||||
                    (
 | 
			
		||||
                        self._compression,
 | 
			
		||||
                        (x, y, min(x + w, xsize), min(y + h, ysize)),
 | 
			
		||||
                        offset,
 | 
			
		||||
                        a,
 | 
			
		||||
                    )
 | 
			
		||||
                )
 | 
			
		||||
                x = x + w
 | 
			
		||||
                if x >= self.size[0]:
 | 
			
		||||
                    x, y = 0, y + h
 | 
			
		||||
| 
						 | 
				
			
			@ -1353,7 +1378,7 @@ class TiffImageFile(ImageFile.ImageFile):
 | 
			
		|||
 | 
			
		||||
        # Fix up info.
 | 
			
		||||
        if ICCPROFILE in self.tag_v2:
 | 
			
		||||
            self.info['icc_profile'] = self.tag_v2[ICCPROFILE]
 | 
			
		||||
            self.info["icc_profile"] = self.tag_v2[ICCPROFILE]
 | 
			
		||||
 | 
			
		||||
        # fixup palette descriptor
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1396,7 +1421,6 @@ SAVE_INFO = {
 | 
			
		|||
    "CMYK": ("CMYK", II, 5, 1, (8, 8, 8, 8), None),
 | 
			
		||||
    "YCbCr": ("YCbCr", II, 6, 1, (8, 8, 8), None),
 | 
			
		||||
    "LAB": ("LAB", II, 8, 1, (8, 8, 8), None),
 | 
			
		||||
 | 
			
		||||
    "I;32BS": ("I;32BS", MM, 1, 2, (32,), None),
 | 
			
		||||
    "I;16B": ("I;16B", MM, 1, 1, (16,), None),
 | 
			
		||||
    "I;16BS": ("I;16BS", MM, 1, 2, (16,), None),
 | 
			
		||||
| 
						 | 
				
			
			@ -1413,14 +1437,14 @@ def _save(im, fp, filename):
 | 
			
		|||
 | 
			
		||||
    ifd = ImageFileDirectory_v2(prefix=prefix)
 | 
			
		||||
 | 
			
		||||
    compression = im.encoderinfo.get('compression', im.info.get('compression'))
 | 
			
		||||
    compression = im.encoderinfo.get("compression", im.info.get("compression"))
 | 
			
		||||
    if compression is None:
 | 
			
		||||
        compression = 'raw'
 | 
			
		||||
        compression = "raw"
 | 
			
		||||
 | 
			
		||||
    libtiff = WRITE_LIBTIFF or compression != 'raw'
 | 
			
		||||
    libtiff = WRITE_LIBTIFF or compression != "raw"
 | 
			
		||||
 | 
			
		||||
    # required for color libtiff images
 | 
			
		||||
    ifd[PLANAR_CONFIGURATION] = getattr(im, '_planar_configuration', 1)
 | 
			
		||||
    ifd[PLANAR_CONFIGURATION] = getattr(im, "_planar_configuration", 1)
 | 
			
		||||
 | 
			
		||||
    ifd[IMAGEWIDTH] = im.size[0]
 | 
			
		||||
    ifd[IMAGELENGTH] = im.size[1]
 | 
			
		||||
| 
						 | 
				
			
			@ -1440,10 +1464,16 @@ def _save(im, fp, filename):
 | 
			
		|||
 | 
			
		||||
    # additions written by Greg Couch, gregc@cgl.ucsf.edu
 | 
			
		||||
    # inspired by image-sig posting from Kevin Cazabon, kcazabon@home.com
 | 
			
		||||
    if hasattr(im, 'tag_v2'):
 | 
			
		||||
    if hasattr(im, "tag_v2"):
 | 
			
		||||
        # preserve tags from original TIFF image file
 | 
			
		||||
        for key in (RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION,
 | 
			
		||||
                    IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, XMP):
 | 
			
		||||
        for key in (
 | 
			
		||||
            RESOLUTION_UNIT,
 | 
			
		||||
            X_RESOLUTION,
 | 
			
		||||
            Y_RESOLUTION,
 | 
			
		||||
            IPTC_NAA_CHUNK,
 | 
			
		||||
            PHOTOSHOP_CHUNK,
 | 
			
		||||
            XMP,
 | 
			
		||||
        ):
 | 
			
		||||
            if key in im.tag_v2:
 | 
			
		||||
                ifd[key] = im.tag_v2[key]
 | 
			
		||||
                ifd.tagtype[key] = im.tag_v2.tagtype[key]
 | 
			
		||||
| 
						 | 
				
			
			@ -1453,7 +1483,8 @@ def _save(im, fp, filename):
 | 
			
		|||
    if "icc_profile" in im.info:
 | 
			
		||||
        ifd[ICCPROFILE] = im.info["icc_profile"]
 | 
			
		||||
 | 
			
		||||
    for key, name in [(IMAGEDESCRIPTION, "description"),
 | 
			
		||||
    for key, name in [
 | 
			
		||||
        (IMAGEDESCRIPTION, "description"),
 | 
			
		||||
        (X_RESOLUTION, "resolution"),
 | 
			
		||||
        (Y_RESOLUTION, "resolution"),
 | 
			
		||||
        (X_RESOLUTION, "x_resolution"),
 | 
			
		||||
| 
						 | 
				
			
			@ -1462,7 +1493,8 @@ def _save(im, fp, filename):
 | 
			
		|||
        (SOFTWARE, "software"),
 | 
			
		||||
        (DATE_TIME, "date_time"),
 | 
			
		||||
        (ARTIST, "artist"),
 | 
			
		||||
                      (COPYRIGHT, "copyright")]:
 | 
			
		||||
        (COPYRIGHT, "copyright"),
 | 
			
		||||
    ]:
 | 
			
		||||
        if name in im.encoderinfo:
 | 
			
		||||
            ifd[key] = im.encoderinfo[name]
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1487,7 +1519,7 @@ def _save(im, fp, filename):
 | 
			
		|||
        lut = im.im.getpalette("RGB", "RGB;L")
 | 
			
		||||
        ifd[COLORMAP] = tuple(i8(v) * 256 for v in lut)
 | 
			
		||||
    # data orientation
 | 
			
		||||
    stride = len(bits) * ((im.size[0]*bits[0]+7)//8)
 | 
			
		||||
    stride = len(bits) * ((im.size[0] * bits[0] + 7) // 8)
 | 
			
		||||
    ifd[ROWSPERSTRIP] = im.size[1]
 | 
			
		||||
    ifd[STRIPBYTECOUNTS] = stride * im.size[1]
 | 
			
		||||
    ifd[STRIPOFFSETS] = 0  # this is adjusted by IFD writer
 | 
			
		||||
| 
						 | 
				
			
			@ -1516,11 +1548,11 @@ def _save(im, fp, filename):
 | 
			
		|||
        # the original file, e.g x,y resolution so that we can
 | 
			
		||||
        # save(load('')) == original file.
 | 
			
		||||
        legacy_ifd = {}
 | 
			
		||||
        if hasattr(im, 'tag'):
 | 
			
		||||
        if hasattr(im, "tag"):
 | 
			
		||||
            legacy_ifd = im.tag.to_v2()
 | 
			
		||||
        for tag, value in itertools.chain(ifd.items(),
 | 
			
		||||
                                          getattr(im, 'tag_v2', {}).items(),
 | 
			
		||||
                                          legacy_ifd.items()):
 | 
			
		||||
        for tag, value in itertools.chain(
 | 
			
		||||
            ifd.items(), getattr(im, "tag_v2", {}).items(), legacy_ifd.items()
 | 
			
		||||
        ):
 | 
			
		||||
            # Libtiff can only process certain core items without adding
 | 
			
		||||
            # them to the custom dictionary.
 | 
			
		||||
            # Support for custom items has only been been added
 | 
			
		||||
| 
						 | 
				
			
			@ -1528,14 +1560,17 @@ def _save(im, fp, filename):
 | 
			
		|||
            if tag not in TiffTags.LIBTIFF_CORE:
 | 
			
		||||
                if TiffTags.lookup(tag).type == TiffTags.UNDEFINED:
 | 
			
		||||
                    continue
 | 
			
		||||
                if (distutils.version.StrictVersion(_libtiff_version()) <
 | 
			
		||||
                    distutils.version.StrictVersion("4.0")) \
 | 
			
		||||
                   or not (isinstance(value, (int, float, str, bytes)) or
 | 
			
		||||
                           (not py3 and isinstance(value, unicode))):  # noqa: F821
 | 
			
		||||
                if (
 | 
			
		||||
                    distutils.version.StrictVersion(_libtiff_version())
 | 
			
		||||
                    < distutils.version.StrictVersion("4.0")
 | 
			
		||||
                ) or not (
 | 
			
		||||
                    isinstance(value, (int, float, str, bytes))
 | 
			
		||||
                    or (not py3 and isinstance(value, unicode))  # noqa: F821
 | 
			
		||||
                ):
 | 
			
		||||
                    continue
 | 
			
		||||
            if tag not in atts and tag not in blocklist:
 | 
			
		||||
                if isinstance(value, str if py3 else unicode):  # noqa: F821
 | 
			
		||||
                    atts[tag] = value.encode('ascii', 'replace') + b"\0"
 | 
			
		||||
                    atts[tag] = value.encode("ascii", "replace") + b"\0"
 | 
			
		||||
                elif isinstance(value, IFDRational):
 | 
			
		||||
                    atts[tag] = float(value)
 | 
			
		||||
                else:
 | 
			
		||||
| 
						 | 
				
			
			@ -1548,15 +1583,15 @@ def _save(im, fp, filename):
 | 
			
		|||
        # we're storing image byte order. So, if the rawmode
 | 
			
		||||
        # contains I;16, we need to convert from native to image
 | 
			
		||||
        # byte order.
 | 
			
		||||
        if im.mode in ('I;16B', 'I;16'):
 | 
			
		||||
            rawmode = 'I;16N'
 | 
			
		||||
        if im.mode in ("I;16B", "I;16"):
 | 
			
		||||
            rawmode = "I;16N"
 | 
			
		||||
 | 
			
		||||
        a = (rawmode, compression, _fp, filename, atts)
 | 
			
		||||
        e = Image._getencoder(im.mode, 'libtiff', a, im.encoderconfig)
 | 
			
		||||
        e.setimage(im.im, (0, 0)+im.size)
 | 
			
		||||
        e = Image._getencoder(im.mode, "libtiff", a, im.encoderconfig)
 | 
			
		||||
        e.setimage(im.im, (0, 0) + im.size)
 | 
			
		||||
        while True:
 | 
			
		||||
            # undone, change to self.decodermaxblock:
 | 
			
		||||
            l, s, d = e.encode(16*1024)
 | 
			
		||||
            l, s, d = e.encode(16 * 1024)
 | 
			
		||||
            if not _fp:
 | 
			
		||||
                fp.write(d)
 | 
			
		||||
            if s:
 | 
			
		||||
| 
						 | 
				
			
			@ -1567,9 +1602,9 @@ def _save(im, fp, filename):
 | 
			
		|||
    else:
 | 
			
		||||
        offset = ifd.save(fp)
 | 
			
		||||
 | 
			
		||||
        ImageFile._save(im, fp, [
 | 
			
		||||
            ("raw", (0, 0)+im.size, offset, (rawmode, stride, 1))
 | 
			
		||||
            ])
 | 
			
		||||
        ImageFile._save(
 | 
			
		||||
            im, fp, [("raw", (0, 0) + im.size, offset, (rawmode, stride, 1))]
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    # -- helper for multi-page save --
 | 
			
		||||
    if "_debug_multipage" in im.encoderinfo:
 | 
			
		||||
| 
						 | 
				
			
			@ -1603,7 +1638,7 @@ class AppendingTiffWriter:
 | 
			
		|||
    Tags = {273, 288, 324, 519, 520, 521}
 | 
			
		||||
 | 
			
		||||
    def __init__(self, fn, new=False):
 | 
			
		||||
        if hasattr(fn, 'read'):
 | 
			
		||||
        if hasattr(fn, "read"):
 | 
			
		||||
            self.f = fn
 | 
			
		||||
            self.close_fp = False
 | 
			
		||||
        else:
 | 
			
		||||
| 
						 | 
				
			
			@ -1654,8 +1689,7 @@ class AppendingTiffWriter:
 | 
			
		|||
            return
 | 
			
		||||
 | 
			
		||||
        if IIMM != self.IIMM:
 | 
			
		||||
            raise RuntimeError("IIMM of new page doesn't match IIMM of "
 | 
			
		||||
                               "first page")
 | 
			
		||||
            raise RuntimeError("IIMM of new page doesn't match IIMM of first page")
 | 
			
		||||
 | 
			
		||||
        IFDoffset = self.readLong()
 | 
			
		||||
        IFDoffset += self.offsetOfNewPage
 | 
			
		||||
| 
						 | 
				
			
			@ -1729,34 +1763,29 @@ class AppendingTiffWriter:
 | 
			
		|||
        self.f.seek(-2, os.SEEK_CUR)
 | 
			
		||||
        bytesWritten = self.f.write(struct.pack(self.longFmt, value))
 | 
			
		||||
        if bytesWritten is not None and bytesWritten != 4:
 | 
			
		||||
            raise RuntimeError("wrote only %u bytes but wanted 4" %
 | 
			
		||||
                               bytesWritten)
 | 
			
		||||
            raise RuntimeError("wrote only %u bytes but wanted 4" % bytesWritten)
 | 
			
		||||
 | 
			
		||||
    def rewriteLastShort(self, value):
 | 
			
		||||
        self.f.seek(-2, os.SEEK_CUR)
 | 
			
		||||
        bytesWritten = self.f.write(struct.pack(self.shortFmt, value))
 | 
			
		||||
        if bytesWritten is not None and bytesWritten != 2:
 | 
			
		||||
            raise RuntimeError("wrote only %u bytes but wanted 2" %
 | 
			
		||||
                               bytesWritten)
 | 
			
		||||
            raise RuntimeError("wrote only %u bytes but wanted 2" % bytesWritten)
 | 
			
		||||
 | 
			
		||||
    def rewriteLastLong(self, value):
 | 
			
		||||
        self.f.seek(-4, os.SEEK_CUR)
 | 
			
		||||
        bytesWritten = self.f.write(struct.pack(self.longFmt, value))
 | 
			
		||||
        if bytesWritten is not None and bytesWritten != 4:
 | 
			
		||||
            raise RuntimeError("wrote only %u bytes but wanted 4" %
 | 
			
		||||
                               bytesWritten)
 | 
			
		||||
            raise RuntimeError("wrote only %u bytes but wanted 4" % bytesWritten)
 | 
			
		||||
 | 
			
		||||
    def writeShort(self, value):
 | 
			
		||||
        bytesWritten = self.f.write(struct.pack(self.shortFmt, value))
 | 
			
		||||
        if bytesWritten is not None and bytesWritten != 2:
 | 
			
		||||
            raise RuntimeError("wrote only %u bytes but wanted 2" %
 | 
			
		||||
                               bytesWritten)
 | 
			
		||||
            raise RuntimeError("wrote only %u bytes but wanted 2" % bytesWritten)
 | 
			
		||||
 | 
			
		||||
    def writeLong(self, value):
 | 
			
		||||
        bytesWritten = self.f.write(struct.pack(self.longFmt, value))
 | 
			
		||||
        if bytesWritten is not None and bytesWritten != 4:
 | 
			
		||||
            raise RuntimeError("wrote only %u bytes but wanted 4" %
 | 
			
		||||
                               bytesWritten)
 | 
			
		||||
            raise RuntimeError("wrote only %u bytes but wanted 4" % bytesWritten)
 | 
			
		||||
 | 
			
		||||
    def close(self):
 | 
			
		||||
        self.finalize()
 | 
			
		||||
| 
						 | 
				
			
			@ -1766,12 +1795,11 @@ class AppendingTiffWriter:
 | 
			
		|||
        numTags = self.readShort()
 | 
			
		||||
 | 
			
		||||
        for i in range(numTags):
 | 
			
		||||
            tag, fieldType, count = struct.unpack(self.tagFormat,
 | 
			
		||||
                                                  self.f.read(8))
 | 
			
		||||
            tag, fieldType, count = struct.unpack(self.tagFormat, self.f.read(8))
 | 
			
		||||
 | 
			
		||||
            fieldSize = self.fieldSizes[fieldType]
 | 
			
		||||
            totalSize = fieldSize * count
 | 
			
		||||
            isLocal = (totalSize <= 4)
 | 
			
		||||
            isLocal = totalSize <= 4
 | 
			
		||||
            if not isLocal:
 | 
			
		||||
                offset = self.readLong()
 | 
			
		||||
                offset += self.offsetOfNewPage
 | 
			
		||||
| 
						 | 
				
			
			@ -1781,13 +1809,15 @@ class AppendingTiffWriter:
 | 
			
		|||
                curPos = self.f.tell()
 | 
			
		||||
 | 
			
		||||
                if isLocal:
 | 
			
		||||
                    self.fixOffsets(count, isShort=(fieldSize == 2),
 | 
			
		||||
                                    isLong=(fieldSize == 4))
 | 
			
		||||
                    self.fixOffsets(
 | 
			
		||||
                        count, isShort=(fieldSize == 2), isLong=(fieldSize == 4)
 | 
			
		||||
                    )
 | 
			
		||||
                    self.f.seek(curPos + 4)
 | 
			
		||||
                else:
 | 
			
		||||
                    self.f.seek(offset)
 | 
			
		||||
                    self.fixOffsets(count, isShort=(fieldSize == 2),
 | 
			
		||||
                                    isLong=(fieldSize == 4))
 | 
			
		||||
                    self.fixOffsets(
 | 
			
		||||
                        count, isShort=(fieldSize == 2), isLong=(fieldSize == 4)
 | 
			
		||||
                    )
 | 
			
		||||
                    self.f.seek(curPos)
 | 
			
		||||
 | 
			
		||||
                offset = curPos = None
 | 
			
		||||
| 
						 | 
				
			
			@ -1830,7 +1860,7 @@ def _save_all(im, fp, filename):
 | 
			
		|||
    cur_idx = im.tell()
 | 
			
		||||
    try:
 | 
			
		||||
        with AppendingTiffWriter(fp) as tf:
 | 
			
		||||
            for ims in [im]+append_images:
 | 
			
		||||
            for ims in [im] + append_images:
 | 
			
		||||
                ims.encoderinfo = encoderinfo
 | 
			
		||||
                ims.encoderconfig = encoderconfig
 | 
			
		||||
                if not hasattr(ims, "n_frames"):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,10 +23,8 @@ from collections import namedtuple
 | 
			
		|||
class TagInfo(namedtuple("_TagInfo", "value name type length enum")):
 | 
			
		||||
    __slots__ = []
 | 
			
		||||
 | 
			
		||||
    def __new__(cls, value=None, name="unknown",
 | 
			
		||||
                type=None, length=None, enum=None):
 | 
			
		||||
        return super(TagInfo, cls).__new__(
 | 
			
		||||
            cls, value, name, type, length, enum or {})
 | 
			
		||||
    def __new__(cls, value=None, name="unknown", type=None, length=None, enum=None):
 | 
			
		||||
        return super(TagInfo, cls).__new__(cls, value, name, type, length, enum or {})
 | 
			
		||||
 | 
			
		||||
    def cvt_enum(self, value):
 | 
			
		||||
        # Using get will call hash(value), which can be expensive
 | 
			
		||||
| 
						 | 
				
			
			@ -44,7 +42,7 @@ def lookup(tag):
 | 
			
		|||
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    return TAGS_V2.get(tag, TagInfo(tag, TAGS.get(tag, 'unknown')))
 | 
			
		||||
    return TAGS_V2.get(tag, TagInfo(tag, TAGS.get(tag, "unknown")))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
| 
						 | 
				
			
			@ -73,27 +71,47 @@ FLOAT = 11
 | 
			
		|||
DOUBLE = 12
 | 
			
		||||
 | 
			
		||||
TAGS_V2 = {
 | 
			
		||||
 | 
			
		||||
    254: ("NewSubfileType", LONG, 1),
 | 
			
		||||
    255: ("SubfileType", SHORT, 1),
 | 
			
		||||
    256: ("ImageWidth", LONG, 1),
 | 
			
		||||
    257: ("ImageLength", LONG, 1),
 | 
			
		||||
    258: ("BitsPerSample", SHORT, 0),
 | 
			
		||||
    259: ("Compression", SHORT, 1,
 | 
			
		||||
          {"Uncompressed": 1, "CCITT 1d": 2, "Group 3 Fax": 3,
 | 
			
		||||
           "Group 4 Fax": 4, "LZW": 5, "JPEG": 6, "PackBits": 32773}),
 | 
			
		||||
 | 
			
		||||
    262: ("PhotometricInterpretation", SHORT, 1,
 | 
			
		||||
          {"WhiteIsZero": 0, "BlackIsZero": 1, "RGB": 2, "RGB Palette": 3,
 | 
			
		||||
           "Transparency Mask": 4, "CMYK": 5, "YCbCr": 6, "CieLAB": 8,
 | 
			
		||||
    259: (
 | 
			
		||||
        "Compression",
 | 
			
		||||
        SHORT,
 | 
			
		||||
        1,
 | 
			
		||||
        {
 | 
			
		||||
            "Uncompressed": 1,
 | 
			
		||||
            "CCITT 1d": 2,
 | 
			
		||||
            "Group 3 Fax": 3,
 | 
			
		||||
            "Group 4 Fax": 4,
 | 
			
		||||
            "LZW": 5,
 | 
			
		||||
            "JPEG": 6,
 | 
			
		||||
            "PackBits": 32773,
 | 
			
		||||
        },
 | 
			
		||||
    ),
 | 
			
		||||
    262: (
 | 
			
		||||
        "PhotometricInterpretation",
 | 
			
		||||
        SHORT,
 | 
			
		||||
        1,
 | 
			
		||||
        {
 | 
			
		||||
            "WhiteIsZero": 0,
 | 
			
		||||
            "BlackIsZero": 1,
 | 
			
		||||
            "RGB": 2,
 | 
			
		||||
            "RGB Palette": 3,
 | 
			
		||||
            "Transparency Mask": 4,
 | 
			
		||||
            "CMYK": 5,
 | 
			
		||||
            "YCbCr": 6,
 | 
			
		||||
            "CieLAB": 8,
 | 
			
		||||
            "CFA": 32803,  # TIFF/EP, Adobe DNG
 | 
			
		||||
           "LinearRaw": 32892}),  # Adobe DNG
 | 
			
		||||
            "LinearRaw": 32892,  # Adobe DNG
 | 
			
		||||
        },
 | 
			
		||||
    ),
 | 
			
		||||
    263: ("Threshholding", SHORT, 1),
 | 
			
		||||
    264: ("CellWidth", SHORT, 1),
 | 
			
		||||
    265: ("CellLength", SHORT, 1),
 | 
			
		||||
    266: ("FillOrder", SHORT, 1),
 | 
			
		||||
    269: ("DocumentName", ASCII, 1),
 | 
			
		||||
 | 
			
		||||
    270: ("ImageDescription", ASCII, 1),
 | 
			
		||||
    271: ("Make", ASCII, 1),
 | 
			
		||||
    272: ("Model", ASCII, 1),
 | 
			
		||||
| 
						 | 
				
			
			@ -102,7 +120,6 @@ TAGS_V2 = {
 | 
			
		|||
    277: ("SamplesPerPixel", SHORT, 1),
 | 
			
		||||
    278: ("RowsPerStrip", LONG, 1),
 | 
			
		||||
    279: ("StripByteCounts", LONG, 0),
 | 
			
		||||
 | 
			
		||||
    280: ("MinSampleValue", LONG, 0),
 | 
			
		||||
    281: ("MaxSampleValue", SHORT, 0),
 | 
			
		||||
    282: ("XResolution", RATIONAL, 1),
 | 
			
		||||
| 
						 | 
				
			
			@ -113,31 +130,26 @@ TAGS_V2 = {
 | 
			
		|||
    287: ("YPosition", RATIONAL, 1),
 | 
			
		||||
    288: ("FreeOffsets", LONG, 1),
 | 
			
		||||
    289: ("FreeByteCounts", LONG, 1),
 | 
			
		||||
 | 
			
		||||
    290: ("GrayResponseUnit", SHORT, 1),
 | 
			
		||||
    291: ("GrayResponseCurve", SHORT, 0),
 | 
			
		||||
    292: ("T4Options", LONG, 1),
 | 
			
		||||
    293: ("T6Options", LONG, 1),
 | 
			
		||||
    296: ("ResolutionUnit", SHORT, 1, {"none": 1, "inch": 2, "cm": 3}),
 | 
			
		||||
    297: ("PageNumber", SHORT, 2),
 | 
			
		||||
 | 
			
		||||
    301: ("TransferFunction", SHORT, 0),
 | 
			
		||||
    305: ("Software", ASCII, 1),
 | 
			
		||||
    306: ("DateTime", ASCII, 1),
 | 
			
		||||
 | 
			
		||||
    315: ("Artist", ASCII, 1),
 | 
			
		||||
    316: ("HostComputer", ASCII, 1),
 | 
			
		||||
    317: ("Predictor", SHORT, 1, {"none": 1, "Horizontal Differencing": 2}),
 | 
			
		||||
    318: ("WhitePoint", RATIONAL, 2),
 | 
			
		||||
    319: ("PrimaryChromaticities", RATIONAL, 6),
 | 
			
		||||
 | 
			
		||||
    320: ("ColorMap", SHORT, 0),
 | 
			
		||||
    321: ("HalftoneHints", SHORT, 2),
 | 
			
		||||
    322: ("TileWidth", LONG, 1),
 | 
			
		||||
    323: ("TileLength", LONG, 1),
 | 
			
		||||
    324: ("TileOffsets", LONG, 0),
 | 
			
		||||
    325: ("TileByteCounts", LONG, 0),
 | 
			
		||||
 | 
			
		||||
    332: ("InkSet", SHORT, 1),
 | 
			
		||||
    333: ("InkNames", ASCII, 1),
 | 
			
		||||
    334: ("NumberOfInks", SHORT, 1),
 | 
			
		||||
| 
						 | 
				
			
			@ -145,13 +157,10 @@ TAGS_V2 = {
 | 
			
		|||
    337: ("TargetPrinter", ASCII, 1),
 | 
			
		||||
    338: ("ExtraSamples", SHORT, 0),
 | 
			
		||||
    339: ("SampleFormat", SHORT, 0),
 | 
			
		||||
 | 
			
		||||
    340: ("SMinSampleValue", DOUBLE, 0),
 | 
			
		||||
    341: ("SMaxSampleValue", DOUBLE, 0),
 | 
			
		||||
    342: ("TransferRange", SHORT, 6),
 | 
			
		||||
 | 
			
		||||
    347: ("JPEGTables", UNDEFINED, 1),
 | 
			
		||||
 | 
			
		||||
    # obsolete JPEG tags
 | 
			
		||||
    512: ("JPEGProc", SHORT, 1),
 | 
			
		||||
    513: ("JPEGInterchangeFormat", LONG, 1),
 | 
			
		||||
| 
						 | 
				
			
			@ -162,22 +171,17 @@ TAGS_V2 = {
 | 
			
		|||
    519: ("JPEGQTables", LONG, 0),
 | 
			
		||||
    520: ("JPEGDCTables", LONG, 0),
 | 
			
		||||
    521: ("JPEGACTables", LONG, 0),
 | 
			
		||||
 | 
			
		||||
    529: ("YCbCrCoefficients", RATIONAL, 3),
 | 
			
		||||
    530: ("YCbCrSubSampling", SHORT, 2),
 | 
			
		||||
    531: ("YCbCrPositioning", SHORT, 1),
 | 
			
		||||
    532: ("ReferenceBlackWhite", RATIONAL, 6),
 | 
			
		||||
 | 
			
		||||
    700: ('XMP', BYTE, 1),
 | 
			
		||||
 | 
			
		||||
    700: ("XMP", BYTE, 1),
 | 
			
		||||
    33432: ("Copyright", ASCII, 1),
 | 
			
		||||
    34377: ('PhotoshopInfo', BYTE, 1),
 | 
			
		||||
 | 
			
		||||
    34377: ("PhotoshopInfo", BYTE, 1),
 | 
			
		||||
    # FIXME add more tags here
 | 
			
		||||
    34665: ("ExifIFD", SHORT, 1),
 | 
			
		||||
    34675: ('ICCProfile', UNDEFINED, 1),
 | 
			
		||||
    34853: ('GPSInfoIFD', BYTE, 1),
 | 
			
		||||
 | 
			
		||||
    34675: ("ICCProfile", UNDEFINED, 1),
 | 
			
		||||
    34853: ("GPSInfoIFD", BYTE, 1),
 | 
			
		||||
    # MPInfo
 | 
			
		||||
    45056: ("MPFVersion", UNDEFINED, 1),
 | 
			
		||||
    45057: ("NumberOfImages", LONG, 1),
 | 
			
		||||
| 
						 | 
				
			
			@ -198,159 +202,157 @@ TAGS_V2 = {
 | 
			
		|||
    45579: ("YawAngle", SIGNED_RATIONAL, 1),
 | 
			
		||||
    45580: ("PitchAngle", SIGNED_RATIONAL, 1),
 | 
			
		||||
    45581: ("RollAngle", SIGNED_RATIONAL, 1),
 | 
			
		||||
 | 
			
		||||
    50741: ("MakerNoteSafety", SHORT, 1, {"Unsafe": 0, "Safe": 1}),
 | 
			
		||||
    50780: ("BestQualityScale", RATIONAL, 1),
 | 
			
		||||
    50838: ("ImageJMetaDataByteCounts", LONG, 0),  # Can be more than one
 | 
			
		||||
    50839: ("ImageJMetaData", UNDEFINED, 1)        # see Issue #2006
 | 
			
		||||
    50839: ("ImageJMetaData", UNDEFINED, 1),  # see Issue #2006
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Legacy Tags structure
 | 
			
		||||
# these tags aren't included above, but were in the previous versions
 | 
			
		||||
TAGS = {347: 'JPEGTables',
 | 
			
		||||
        700: 'XMP',
 | 
			
		||||
 | 
			
		||||
TAGS = {
 | 
			
		||||
    347: "JPEGTables",
 | 
			
		||||
    700: "XMP",
 | 
			
		||||
    # Additional Exif Info
 | 
			
		||||
        32932: 'Wang Annotation',
 | 
			
		||||
        33434: 'ExposureTime',
 | 
			
		||||
        33437: 'FNumber',
 | 
			
		||||
        33445: 'MD FileTag',
 | 
			
		||||
        33446: 'MD ScalePixel',
 | 
			
		||||
        33447: 'MD ColorTable',
 | 
			
		||||
        33448: 'MD LabName',
 | 
			
		||||
        33449: 'MD SampleInfo',
 | 
			
		||||
        33450: 'MD PrepDate',
 | 
			
		||||
        33451: 'MD PrepTime',
 | 
			
		||||
        33452: 'MD FileUnits',
 | 
			
		||||
        33550: 'ModelPixelScaleTag',
 | 
			
		||||
        33723: 'IptcNaaInfo',
 | 
			
		||||
        33918: 'INGR Packet Data Tag',
 | 
			
		||||
        33919: 'INGR Flag Registers',
 | 
			
		||||
        33920: 'IrasB Transformation Matrix',
 | 
			
		||||
        33922: 'ModelTiepointTag',
 | 
			
		||||
        34264: 'ModelTransformationTag',
 | 
			
		||||
        34377: 'PhotoshopInfo',
 | 
			
		||||
        34735: 'GeoKeyDirectoryTag',
 | 
			
		||||
        34736: 'GeoDoubleParamsTag',
 | 
			
		||||
        34737: 'GeoAsciiParamsTag',
 | 
			
		||||
        34850: 'ExposureProgram',
 | 
			
		||||
        34852: 'SpectralSensitivity',
 | 
			
		||||
        34855: 'ISOSpeedRatings',
 | 
			
		||||
        34856: 'OECF',
 | 
			
		||||
        34864: 'SensitivityType',
 | 
			
		||||
        34865: 'StandardOutputSensitivity',
 | 
			
		||||
        34866: 'RecommendedExposureIndex',
 | 
			
		||||
        34867: 'ISOSpeed',
 | 
			
		||||
        34868: 'ISOSpeedLatitudeyyy',
 | 
			
		||||
        34869: 'ISOSpeedLatitudezzz',
 | 
			
		||||
        34908: 'HylaFAX FaxRecvParams',
 | 
			
		||||
        34909: 'HylaFAX FaxSubAddress',
 | 
			
		||||
        34910: 'HylaFAX FaxRecvTime',
 | 
			
		||||
        36864: 'ExifVersion',
 | 
			
		||||
        36867: 'DateTimeOriginal',
 | 
			
		||||
        36868: 'DateTImeDigitized',
 | 
			
		||||
        37121: 'ComponentsConfiguration',
 | 
			
		||||
        37122: 'CompressedBitsPerPixel',
 | 
			
		||||
        37724: 'ImageSourceData',
 | 
			
		||||
        37377: 'ShutterSpeedValue',
 | 
			
		||||
        37378: 'ApertureValue',
 | 
			
		||||
        37379: 'BrightnessValue',
 | 
			
		||||
        37380: 'ExposureBiasValue',
 | 
			
		||||
        37381: 'MaxApertureValue',
 | 
			
		||||
        37382: 'SubjectDistance',
 | 
			
		||||
        37383: 'MeteringMode',
 | 
			
		||||
        37384: 'LightSource',
 | 
			
		||||
        37385: 'Flash',
 | 
			
		||||
        37386: 'FocalLength',
 | 
			
		||||
        37396: 'SubjectArea',
 | 
			
		||||
        37500: 'MakerNote',
 | 
			
		||||
        37510: 'UserComment',
 | 
			
		||||
        37520: 'SubSec',
 | 
			
		||||
        37521: 'SubSecTimeOriginal',
 | 
			
		||||
        37522: 'SubsecTimeDigitized',
 | 
			
		||||
        40960: 'FlashPixVersion',
 | 
			
		||||
        40961: 'ColorSpace',
 | 
			
		||||
        40962: 'PixelXDimension',
 | 
			
		||||
        40963: 'PixelYDimension',
 | 
			
		||||
        40964: 'RelatedSoundFile',
 | 
			
		||||
        40965: 'InteroperabilityIFD',
 | 
			
		||||
        41483: 'FlashEnergy',
 | 
			
		||||
        41484: 'SpatialFrequencyResponse',
 | 
			
		||||
        41486: 'FocalPlaneXResolution',
 | 
			
		||||
        41487: 'FocalPlaneYResolution',
 | 
			
		||||
        41488: 'FocalPlaneResolutionUnit',
 | 
			
		||||
        41492: 'SubjectLocation',
 | 
			
		||||
        41493: 'ExposureIndex',
 | 
			
		||||
        41495: 'SensingMethod',
 | 
			
		||||
        41728: 'FileSource',
 | 
			
		||||
        41729: 'SceneType',
 | 
			
		||||
        41730: 'CFAPattern',
 | 
			
		||||
        41985: 'CustomRendered',
 | 
			
		||||
        41986: 'ExposureMode',
 | 
			
		||||
        41987: 'WhiteBalance',
 | 
			
		||||
        41988: 'DigitalZoomRatio',
 | 
			
		||||
        41989: 'FocalLengthIn35mmFilm',
 | 
			
		||||
        41990: 'SceneCaptureType',
 | 
			
		||||
        41991: 'GainControl',
 | 
			
		||||
        41992: 'Contrast',
 | 
			
		||||
        41993: 'Saturation',
 | 
			
		||||
        41994: 'Sharpness',
 | 
			
		||||
        41995: 'DeviceSettingDescription',
 | 
			
		||||
        41996: 'SubjectDistanceRange',
 | 
			
		||||
        42016: 'ImageUniqueID',
 | 
			
		||||
        42032: 'CameraOwnerName',
 | 
			
		||||
        42033: 'BodySerialNumber',
 | 
			
		||||
        42034: 'LensSpecification',
 | 
			
		||||
        42035: 'LensMake',
 | 
			
		||||
        42036: 'LensModel',
 | 
			
		||||
        42037: 'LensSerialNumber',
 | 
			
		||||
        42112: 'GDAL_METADATA',
 | 
			
		||||
        42113: 'GDAL_NODATA',
 | 
			
		||||
        42240: 'Gamma',
 | 
			
		||||
        50215: 'Oce Scanjob Description',
 | 
			
		||||
        50216: 'Oce Application Selector',
 | 
			
		||||
        50217: 'Oce Identification Number',
 | 
			
		||||
        50218: 'Oce ImageLogic Characteristics',
 | 
			
		||||
 | 
			
		||||
    32932: "Wang Annotation",
 | 
			
		||||
    33434: "ExposureTime",
 | 
			
		||||
    33437: "FNumber",
 | 
			
		||||
    33445: "MD FileTag",
 | 
			
		||||
    33446: "MD ScalePixel",
 | 
			
		||||
    33447: "MD ColorTable",
 | 
			
		||||
    33448: "MD LabName",
 | 
			
		||||
    33449: "MD SampleInfo",
 | 
			
		||||
    33450: "MD PrepDate",
 | 
			
		||||
    33451: "MD PrepTime",
 | 
			
		||||
    33452: "MD FileUnits",
 | 
			
		||||
    33550: "ModelPixelScaleTag",
 | 
			
		||||
    33723: "IptcNaaInfo",
 | 
			
		||||
    33918: "INGR Packet Data Tag",
 | 
			
		||||
    33919: "INGR Flag Registers",
 | 
			
		||||
    33920: "IrasB Transformation Matrix",
 | 
			
		||||
    33922: "ModelTiepointTag",
 | 
			
		||||
    34264: "ModelTransformationTag",
 | 
			
		||||
    34377: "PhotoshopInfo",
 | 
			
		||||
    34735: "GeoKeyDirectoryTag",
 | 
			
		||||
    34736: "GeoDoubleParamsTag",
 | 
			
		||||
    34737: "GeoAsciiParamsTag",
 | 
			
		||||
    34850: "ExposureProgram",
 | 
			
		||||
    34852: "SpectralSensitivity",
 | 
			
		||||
    34855: "ISOSpeedRatings",
 | 
			
		||||
    34856: "OECF",
 | 
			
		||||
    34864: "SensitivityType",
 | 
			
		||||
    34865: "StandardOutputSensitivity",
 | 
			
		||||
    34866: "RecommendedExposureIndex",
 | 
			
		||||
    34867: "ISOSpeed",
 | 
			
		||||
    34868: "ISOSpeedLatitudeyyy",
 | 
			
		||||
    34869: "ISOSpeedLatitudezzz",
 | 
			
		||||
    34908: "HylaFAX FaxRecvParams",
 | 
			
		||||
    34909: "HylaFAX FaxSubAddress",
 | 
			
		||||
    34910: "HylaFAX FaxRecvTime",
 | 
			
		||||
    36864: "ExifVersion",
 | 
			
		||||
    36867: "DateTimeOriginal",
 | 
			
		||||
    36868: "DateTImeDigitized",
 | 
			
		||||
    37121: "ComponentsConfiguration",
 | 
			
		||||
    37122: "CompressedBitsPerPixel",
 | 
			
		||||
    37724: "ImageSourceData",
 | 
			
		||||
    37377: "ShutterSpeedValue",
 | 
			
		||||
    37378: "ApertureValue",
 | 
			
		||||
    37379: "BrightnessValue",
 | 
			
		||||
    37380: "ExposureBiasValue",
 | 
			
		||||
    37381: "MaxApertureValue",
 | 
			
		||||
    37382: "SubjectDistance",
 | 
			
		||||
    37383: "MeteringMode",
 | 
			
		||||
    37384: "LightSource",
 | 
			
		||||
    37385: "Flash",
 | 
			
		||||
    37386: "FocalLength",
 | 
			
		||||
    37396: "SubjectArea",
 | 
			
		||||
    37500: "MakerNote",
 | 
			
		||||
    37510: "UserComment",
 | 
			
		||||
    37520: "SubSec",
 | 
			
		||||
    37521: "SubSecTimeOriginal",
 | 
			
		||||
    37522: "SubsecTimeDigitized",
 | 
			
		||||
    40960: "FlashPixVersion",
 | 
			
		||||
    40961: "ColorSpace",
 | 
			
		||||
    40962: "PixelXDimension",
 | 
			
		||||
    40963: "PixelYDimension",
 | 
			
		||||
    40964: "RelatedSoundFile",
 | 
			
		||||
    40965: "InteroperabilityIFD",
 | 
			
		||||
    41483: "FlashEnergy",
 | 
			
		||||
    41484: "SpatialFrequencyResponse",
 | 
			
		||||
    41486: "FocalPlaneXResolution",
 | 
			
		||||
    41487: "FocalPlaneYResolution",
 | 
			
		||||
    41488: "FocalPlaneResolutionUnit",
 | 
			
		||||
    41492: "SubjectLocation",
 | 
			
		||||
    41493: "ExposureIndex",
 | 
			
		||||
    41495: "SensingMethod",
 | 
			
		||||
    41728: "FileSource",
 | 
			
		||||
    41729: "SceneType",
 | 
			
		||||
    41730: "CFAPattern",
 | 
			
		||||
    41985: "CustomRendered",
 | 
			
		||||
    41986: "ExposureMode",
 | 
			
		||||
    41987: "WhiteBalance",
 | 
			
		||||
    41988: "DigitalZoomRatio",
 | 
			
		||||
    41989: "FocalLengthIn35mmFilm",
 | 
			
		||||
    41990: "SceneCaptureType",
 | 
			
		||||
    41991: "GainControl",
 | 
			
		||||
    41992: "Contrast",
 | 
			
		||||
    41993: "Saturation",
 | 
			
		||||
    41994: "Sharpness",
 | 
			
		||||
    41995: "DeviceSettingDescription",
 | 
			
		||||
    41996: "SubjectDistanceRange",
 | 
			
		||||
    42016: "ImageUniqueID",
 | 
			
		||||
    42032: "CameraOwnerName",
 | 
			
		||||
    42033: "BodySerialNumber",
 | 
			
		||||
    42034: "LensSpecification",
 | 
			
		||||
    42035: "LensMake",
 | 
			
		||||
    42036: "LensModel",
 | 
			
		||||
    42037: "LensSerialNumber",
 | 
			
		||||
    42112: "GDAL_METADATA",
 | 
			
		||||
    42113: "GDAL_NODATA",
 | 
			
		||||
    42240: "Gamma",
 | 
			
		||||
    50215: "Oce Scanjob Description",
 | 
			
		||||
    50216: "Oce Application Selector",
 | 
			
		||||
    50217: "Oce Identification Number",
 | 
			
		||||
    50218: "Oce ImageLogic Characteristics",
 | 
			
		||||
    # Adobe DNG
 | 
			
		||||
        50706: 'DNGVersion',
 | 
			
		||||
        50707: 'DNGBackwardVersion',
 | 
			
		||||
        50708: 'UniqueCameraModel',
 | 
			
		||||
        50709: 'LocalizedCameraModel',
 | 
			
		||||
        50710: 'CFAPlaneColor',
 | 
			
		||||
        50711: 'CFALayout',
 | 
			
		||||
        50712: 'LinearizationTable',
 | 
			
		||||
        50713: 'BlackLevelRepeatDim',
 | 
			
		||||
        50714: 'BlackLevel',
 | 
			
		||||
        50715: 'BlackLevelDeltaH',
 | 
			
		||||
        50716: 'BlackLevelDeltaV',
 | 
			
		||||
        50717: 'WhiteLevel',
 | 
			
		||||
        50718: 'DefaultScale',
 | 
			
		||||
        50719: 'DefaultCropOrigin',
 | 
			
		||||
        50720: 'DefaultCropSize',
 | 
			
		||||
        50721: 'ColorMatrix1',
 | 
			
		||||
        50722: 'ColorMatrix2',
 | 
			
		||||
        50723: 'CameraCalibration1',
 | 
			
		||||
        50724: 'CameraCalibration2',
 | 
			
		||||
        50725: 'ReductionMatrix1',
 | 
			
		||||
        50726: 'ReductionMatrix2',
 | 
			
		||||
        50727: 'AnalogBalance',
 | 
			
		||||
        50728: 'AsShotNeutral',
 | 
			
		||||
        50729: 'AsShotWhiteXY',
 | 
			
		||||
        50730: 'BaselineExposure',
 | 
			
		||||
        50731: 'BaselineNoise',
 | 
			
		||||
        50732: 'BaselineSharpness',
 | 
			
		||||
        50733: 'BayerGreenSplit',
 | 
			
		||||
        50734: 'LinearResponseLimit',
 | 
			
		||||
        50735: 'CameraSerialNumber',
 | 
			
		||||
        50736: 'LensInfo',
 | 
			
		||||
        50737: 'ChromaBlurRadius',
 | 
			
		||||
        50738: 'AntiAliasStrength',
 | 
			
		||||
        50740: 'DNGPrivateData',
 | 
			
		||||
        50778: 'CalibrationIlluminant1',
 | 
			
		||||
        50779: 'CalibrationIlluminant2',
 | 
			
		||||
        50784: 'Alias Layer Metadata'
 | 
			
		||||
        }
 | 
			
		||||
    50706: "DNGVersion",
 | 
			
		||||
    50707: "DNGBackwardVersion",
 | 
			
		||||
    50708: "UniqueCameraModel",
 | 
			
		||||
    50709: "LocalizedCameraModel",
 | 
			
		||||
    50710: "CFAPlaneColor",
 | 
			
		||||
    50711: "CFALayout",
 | 
			
		||||
    50712: "LinearizationTable",
 | 
			
		||||
    50713: "BlackLevelRepeatDim",
 | 
			
		||||
    50714: "BlackLevel",
 | 
			
		||||
    50715: "BlackLevelDeltaH",
 | 
			
		||||
    50716: "BlackLevelDeltaV",
 | 
			
		||||
    50717: "WhiteLevel",
 | 
			
		||||
    50718: "DefaultScale",
 | 
			
		||||
    50719: "DefaultCropOrigin",
 | 
			
		||||
    50720: "DefaultCropSize",
 | 
			
		||||
    50721: "ColorMatrix1",
 | 
			
		||||
    50722: "ColorMatrix2",
 | 
			
		||||
    50723: "CameraCalibration1",
 | 
			
		||||
    50724: "CameraCalibration2",
 | 
			
		||||
    50725: "ReductionMatrix1",
 | 
			
		||||
    50726: "ReductionMatrix2",
 | 
			
		||||
    50727: "AnalogBalance",
 | 
			
		||||
    50728: "AsShotNeutral",
 | 
			
		||||
    50729: "AsShotWhiteXY",
 | 
			
		||||
    50730: "BaselineExposure",
 | 
			
		||||
    50731: "BaselineNoise",
 | 
			
		||||
    50732: "BaselineSharpness",
 | 
			
		||||
    50733: "BayerGreenSplit",
 | 
			
		||||
    50734: "LinearResponseLimit",
 | 
			
		||||
    50735: "CameraSerialNumber",
 | 
			
		||||
    50736: "LensInfo",
 | 
			
		||||
    50737: "ChromaBlurRadius",
 | 
			
		||||
    50738: "AntiAliasStrength",
 | 
			
		||||
    50740: "DNGPrivateData",
 | 
			
		||||
    50778: "CalibrationIlluminant1",
 | 
			
		||||
    50779: "CalibrationIlluminant2",
 | 
			
		||||
    50784: "Alias Layer Metadata",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _populate():
 | 
			
		||||
| 
						 | 
				
			
			@ -433,13 +435,48 @@ TYPES = {}
 | 
			
		|||
# some of these are not in our TAGS_V2 dict and were included from tiff.h
 | 
			
		||||
 | 
			
		||||
# This list also exists in encode.c
 | 
			
		||||
LIBTIFF_CORE = {255, 256, 257, 258, 259, 262, 263, 266, 274, 277,
 | 
			
		||||
                278, 280, 281, 340, 341, 282, 283, 284, 286, 287,
 | 
			
		||||
                296, 297, 321, 320, 338, 32995, 322, 323, 32998,
 | 
			
		||||
                32996, 339, 32997, 330, 531, 530, 301, 532, 333,
 | 
			
		||||
LIBTIFF_CORE = {
 | 
			
		||||
    255,
 | 
			
		||||
    256,
 | 
			
		||||
    257,
 | 
			
		||||
    258,
 | 
			
		||||
    259,
 | 
			
		||||
    262,
 | 
			
		||||
    263,
 | 
			
		||||
    266,
 | 
			
		||||
    274,
 | 
			
		||||
    277,
 | 
			
		||||
    278,
 | 
			
		||||
    280,
 | 
			
		||||
    281,
 | 
			
		||||
    340,
 | 
			
		||||
    341,
 | 
			
		||||
    282,
 | 
			
		||||
    283,
 | 
			
		||||
    284,
 | 
			
		||||
    286,
 | 
			
		||||
    287,
 | 
			
		||||
    296,
 | 
			
		||||
    297,
 | 
			
		||||
    321,
 | 
			
		||||
    320,
 | 
			
		||||
    338,
 | 
			
		||||
    32995,
 | 
			
		||||
    322,
 | 
			
		||||
    323,
 | 
			
		||||
    32998,
 | 
			
		||||
    32996,
 | 
			
		||||
    339,
 | 
			
		||||
    32997,
 | 
			
		||||
    330,
 | 
			
		||||
    531,
 | 
			
		||||
    530,
 | 
			
		||||
    301,
 | 
			
		||||
    532,
 | 
			
		||||
    333,
 | 
			
		||||
    # as above
 | 
			
		||||
                269  # this has been in our tests forever, and works
 | 
			
		||||
                }
 | 
			
		||||
    269,  # this has been in our tests forever, and works
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
LIBTIFF_CORE.remove(320)  # Array of short, crashes
 | 
			
		||||
LIBTIFF_CORE.remove(301)  # Array of short, crashes
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,6 +28,7 @@ try:
 | 
			
		|||
    import builtins
 | 
			
		||||
except ImportError:
 | 
			
		||||
    import __builtin__
 | 
			
		||||
 | 
			
		||||
    builtins = __builtin__
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -46,7 +47,7 @@ def open(filename):
 | 
			
		|||
 | 
			
		||||
    def imopen(fp):
 | 
			
		||||
        # read header fields
 | 
			
		||||
        header = fp.read(32+24+32+12)
 | 
			
		||||
        header = fp.read(32 + 24 + 32 + 12)
 | 
			
		||||
        size = i32(header, 32), i32(header, 36)
 | 
			
		||||
        offset = i32(header, 40)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -62,7 +63,7 @@ def open(filename):
 | 
			
		|||
 | 
			
		||||
        # strings are null-terminated
 | 
			
		||||
        im.info["name"] = header[:32].split(b"\0", 1)[0]
 | 
			
		||||
        next_name = header[56:56+32].split(b"\0", 1)[0]
 | 
			
		||||
        next_name = header[56 : 56 + 32].split(b"\0", 1)[0]
 | 
			
		||||
        if next_name:
 | 
			
		||||
            im.info["next_name"] = next_name
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,28 +1,23 @@
 | 
			
		|||
from . import Image, ImageFile
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    from . import _webp
 | 
			
		||||
 | 
			
		||||
    SUPPORTED = True
 | 
			
		||||
except ImportError:
 | 
			
		||||
    SUPPORTED = False
 | 
			
		||||
from io import BytesIO
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_VALID_WEBP_MODES = {
 | 
			
		||||
    "RGBX": True,
 | 
			
		||||
    "RGBA": True,
 | 
			
		||||
    "RGB": True,
 | 
			
		||||
    }
 | 
			
		||||
_VALID_WEBP_MODES = {"RGBX": True, "RGBA": True, "RGB": True}
 | 
			
		||||
 | 
			
		||||
_VALID_WEBP_LEGACY_MODES = {
 | 
			
		||||
    "RGB": True,
 | 
			
		||||
    "RGBA": True,
 | 
			
		||||
    }
 | 
			
		||||
_VALID_WEBP_LEGACY_MODES = {"RGB": True, "RGBA": True}
 | 
			
		||||
 | 
			
		||||
_VP8_MODES_BY_IDENTIFIER = {
 | 
			
		||||
    b"VP8 ": "RGB",
 | 
			
		||||
    b"VP8X": "RGBA",
 | 
			
		||||
    b"VP8L": "RGBA",  # lossless
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _accept(prefix):
 | 
			
		||||
| 
						 | 
				
			
			@ -32,8 +27,9 @@ def _accept(prefix):
 | 
			
		|||
 | 
			
		||||
    if is_riff_file_format and is_webp_file and is_valid_vp8_mode:
 | 
			
		||||
        if not SUPPORTED:
 | 
			
		||||
            return "image file could not be identified " \
 | 
			
		||||
                   "because WEBP support not installed"
 | 
			
		||||
            return (
 | 
			
		||||
                "image file could not be identified because WEBP support not installed"
 | 
			
		||||
            )
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -45,8 +41,9 @@ class WebPImageFile(ImageFile.ImageFile):
 | 
			
		|||
    def _open(self):
 | 
			
		||||
        if not _webp.HAVE_WEBPANIM:
 | 
			
		||||
            # Legacy mode
 | 
			
		||||
            data, width, height, self.mode, icc_profile, exif = \
 | 
			
		||||
                _webp.WebPDecode(self.fp.read())
 | 
			
		||||
            data, width, height, self.mode, icc_profile, exif = _webp.WebPDecode(
 | 
			
		||||
                self.fp.read()
 | 
			
		||||
            )
 | 
			
		||||
            if icc_profile:
 | 
			
		||||
                self.info["icc_profile"] = icc_profile
 | 
			
		||||
            if exif:
 | 
			
		||||
| 
						 | 
				
			
			@ -62,18 +59,18 @@ class WebPImageFile(ImageFile.ImageFile):
 | 
			
		|||
        self._decoder = _webp.WebPAnimDecoder(self.fp.read())
 | 
			
		||||
 | 
			
		||||
        # Get info from decoder
 | 
			
		||||
        width, height, loop_count, bgcolor, frame_count, mode = \
 | 
			
		||||
            self._decoder.get_info()
 | 
			
		||||
        width, height, loop_count, bgcolor, frame_count, mode = self._decoder.get_info()
 | 
			
		||||
        self._size = width, height
 | 
			
		||||
        self.info["loop"] = loop_count
 | 
			
		||||
        bg_a, bg_r, bg_g, bg_b = \
 | 
			
		||||
            (bgcolor >> 24) & 0xFF, \
 | 
			
		||||
            (bgcolor >> 16) & 0xFF, \
 | 
			
		||||
            (bgcolor >> 8) & 0xFF, \
 | 
			
		||||
            bgcolor & 0xFF
 | 
			
		||||
        bg_a, bg_r, bg_g, bg_b = (
 | 
			
		||||
            (bgcolor >> 24) & 0xFF,
 | 
			
		||||
            (bgcolor >> 16) & 0xFF,
 | 
			
		||||
            (bgcolor >> 8) & 0xFF,
 | 
			
		||||
            bgcolor & 0xFF,
 | 
			
		||||
        )
 | 
			
		||||
        self.info["background"] = (bg_r, bg_g, bg_b, bg_a)
 | 
			
		||||
        self._n_frames = frame_count
 | 
			
		||||
        self.mode = 'RGB' if mode == 'RGBX' else mode
 | 
			
		||||
        self.mode = "RGB" if mode == "RGBX" else mode
 | 
			
		||||
        self.rawmode = mode
 | 
			
		||||
        self.tile = []
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -186,7 +183,7 @@ def _save_all(im, fp, filename):
 | 
			
		|||
    # If total frame count is 1, then save using the legacy API, which
 | 
			
		||||
    # will preserve non-alpha modes
 | 
			
		||||
    total = 0
 | 
			
		||||
    for ims in [im]+append_images:
 | 
			
		||||
    for ims in [im] + append_images:
 | 
			
		||||
        total += getattr(ims, "n_frames", 1)
 | 
			
		||||
    if total == 1:
 | 
			
		||||
        _save(im, fp, filename)
 | 
			
		||||
| 
						 | 
				
			
			@ -202,7 +199,7 @@ def _save_all(im, fp, filename):
 | 
			
		|||
            # info["background"]. So it must be converted to an RGBA value
 | 
			
		||||
            palette = im.getpalette()
 | 
			
		||||
            if palette:
 | 
			
		||||
                r, g, b = palette[background*3:(background+1)*3]
 | 
			
		||||
                r, g, b = palette[background * 3 : (background + 1) * 3]
 | 
			
		||||
                background = (r, g, b, 0)
 | 
			
		||||
 | 
			
		||||
    duration = im.encoderinfo.get("duration", 0)
 | 
			
		||||
| 
						 | 
				
			
			@ -230,10 +227,15 @@ def _save_all(im, fp, filename):
 | 
			
		|||
        kmax = 17 if lossless else 5
 | 
			
		||||
 | 
			
		||||
    # Validate background color
 | 
			
		||||
    if (not isinstance(background, (list, tuple)) or len(background) != 4 or
 | 
			
		||||
            not all(v >= 0 and v < 256 for v in background)):
 | 
			
		||||
        raise IOError("Background color is not an RGBA tuple clamped "
 | 
			
		||||
                      "to (0-255): %s" % str(background))
 | 
			
		||||
    if (
 | 
			
		||||
        not isinstance(background, (list, tuple))
 | 
			
		||||
        or len(background) != 4
 | 
			
		||||
        or not all(v >= 0 and v < 256 for v in background)
 | 
			
		||||
    ):
 | 
			
		||||
        raise IOError(
 | 
			
		||||
            "Background color is not an RGBA tuple clamped to (0-255): %s"
 | 
			
		||||
            % str(background)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    # Convert to packed uint
 | 
			
		||||
    bg_r, bg_g, bg_b, bg_a = background
 | 
			
		||||
| 
						 | 
				
			
			@ -241,13 +243,15 @@ def _save_all(im, fp, filename):
 | 
			
		|||
 | 
			
		||||
    # Setup the WebP animation encoder
 | 
			
		||||
    enc = _webp.WebPAnimEncoder(
 | 
			
		||||
        im.size[0], im.size[1],
 | 
			
		||||
        im.size[0],
 | 
			
		||||
        im.size[1],
 | 
			
		||||
        background,
 | 
			
		||||
        loop,
 | 
			
		||||
        minimize_size,
 | 
			
		||||
        kmin, kmax,
 | 
			
		||||
        kmin,
 | 
			
		||||
        kmax,
 | 
			
		||||
        allow_mixed,
 | 
			
		||||
        verbose
 | 
			
		||||
        verbose,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # Add each frame
 | 
			
		||||
| 
						 | 
				
			
			@ -255,7 +259,7 @@ def _save_all(im, fp, filename):
 | 
			
		|||
    timestamp = 0
 | 
			
		||||
    cur_idx = im.tell()
 | 
			
		||||
    try:
 | 
			
		||||
        for ims in [im]+append_images:
 | 
			
		||||
        for ims in [im] + append_images:
 | 
			
		||||
            # Get # of frames in this image
 | 
			
		||||
            nfr = getattr(ims, "n_frames", 1)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -267,25 +271,28 @@ def _save_all(im, fp, filename):
 | 
			
		|||
                frame = ims
 | 
			
		||||
                rawmode = ims.mode
 | 
			
		||||
                if ims.mode not in _VALID_WEBP_MODES:
 | 
			
		||||
                    alpha = 'A' in ims.mode or 'a' in ims.mode \
 | 
			
		||||
                            or (ims.mode == 'P' and
 | 
			
		||||
                                'A' in ims.im.getpalettemode())
 | 
			
		||||
                    rawmode = 'RGBA' if alpha else 'RGB'
 | 
			
		||||
                    alpha = (
 | 
			
		||||
                        "A" in ims.mode
 | 
			
		||||
                        or "a" in ims.mode
 | 
			
		||||
                        or (ims.mode == "P" and "A" in ims.im.getpalettemode())
 | 
			
		||||
                    )
 | 
			
		||||
                    rawmode = "RGBA" if alpha else "RGB"
 | 
			
		||||
                    frame = ims.convert(rawmode)
 | 
			
		||||
 | 
			
		||||
                if rawmode == 'RGB':
 | 
			
		||||
                if rawmode == "RGB":
 | 
			
		||||
                    # For faster conversion, use RGBX
 | 
			
		||||
                    rawmode = 'RGBX'
 | 
			
		||||
                    rawmode = "RGBX"
 | 
			
		||||
 | 
			
		||||
                # Append the frame to the animation encoder
 | 
			
		||||
                enc.add(
 | 
			
		||||
                    frame.tobytes('raw', rawmode),
 | 
			
		||||
                    frame.tobytes("raw", rawmode),
 | 
			
		||||
                    timestamp,
 | 
			
		||||
                    frame.size[0], frame.size[1],
 | 
			
		||||
                    frame.size[0],
 | 
			
		||||
                    frame.size[1],
 | 
			
		||||
                    rawmode,
 | 
			
		||||
                    lossless,
 | 
			
		||||
                    quality,
 | 
			
		||||
                    method
 | 
			
		||||
                    method,
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
                # Update timestamp and frame index
 | 
			
		||||
| 
						 | 
				
			
			@ -299,11 +306,7 @@ def _save_all(im, fp, filename):
 | 
			
		|||
        im.seek(cur_idx)
 | 
			
		||||
 | 
			
		||||
    # Force encoder to flush frames
 | 
			
		||||
    enc.add(
 | 
			
		||||
        None,
 | 
			
		||||
        timestamp,
 | 
			
		||||
        0, 0, "", lossless, quality, 0
 | 
			
		||||
    )
 | 
			
		||||
    enc.add(None, timestamp, 0, 0, "", lossless, quality, 0)
 | 
			
		||||
 | 
			
		||||
    # Get the final output from the encoder
 | 
			
		||||
    data = enc.assemble(icc_profile, exif, xmp)
 | 
			
		||||
| 
						 | 
				
			
			@ -323,9 +326,12 @@ def _save(im, fp, filename):
 | 
			
		|||
    xmp = im.encoderinfo.get("xmp", "")
 | 
			
		||||
 | 
			
		||||
    if im.mode not in _VALID_WEBP_LEGACY_MODES:
 | 
			
		||||
        alpha = 'A' in im.mode or 'a' in im.mode \
 | 
			
		||||
                or (im.mode == 'P' and 'A' in im.im.getpalettemode())
 | 
			
		||||
        im = im.convert('RGBA' if alpha else 'RGB')
 | 
			
		||||
        alpha = (
 | 
			
		||||
            "A" in im.mode
 | 
			
		||||
            or "a" in im.mode
 | 
			
		||||
            or (im.mode == "P" and "A" in im.im.getpalettemode())
 | 
			
		||||
        )
 | 
			
		||||
        im = im.convert("RGBA" if alpha else "RGB")
 | 
			
		||||
 | 
			
		||||
    data = _webp.WebPEncode(
 | 
			
		||||
        im.tobytes(),
 | 
			
		||||
| 
						 | 
				
			
			@ -336,7 +342,7 @@ def _save(im, fp, filename):
 | 
			
		|||
        im.mode,
 | 
			
		||||
        icc_profile,
 | 
			
		||||
        exif,
 | 
			
		||||
        xmp
 | 
			
		||||
        xmp,
 | 
			
		||||
    )
 | 
			
		||||
    if data is None:
 | 
			
		||||
        raise IOError("cannot write file as WebP (encoder returned None)")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,8 +22,7 @@
 | 
			
		|||
from __future__ import print_function
 | 
			
		||||
 | 
			
		||||
from . import Image, ImageFile
 | 
			
		||||
from ._binary import i16le as word, si16le as short, \
 | 
			
		||||
                     i32le as dword, si32le as _long
 | 
			
		||||
from ._binary import i16le as word, si16le as short, i32le as dword, si32le as _long
 | 
			
		||||
from ._util import py3
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -51,7 +50,6 @@ if hasattr(Image.core, "drawwmf"):
 | 
			
		|||
    # install default handler (windows only)
 | 
			
		||||
 | 
			
		||||
    class WmfHandler(object):
 | 
			
		||||
 | 
			
		||||
        def open(self, im):
 | 
			
		||||
            im.mode = "RGB"
 | 
			
		||||
            self.bbox = im.info["wmf_bbox"]
 | 
			
		||||
| 
						 | 
				
			
			@ -59,9 +57,13 @@ if hasattr(Image.core, "drawwmf"):
 | 
			
		|||
        def load(self, im):
 | 
			
		||||
            im.fp.seek(0)  # rewind
 | 
			
		||||
            return Image.frombytes(
 | 
			
		||||
                "RGB", im.size,
 | 
			
		||||
                "RGB",
 | 
			
		||||
                im.size,
 | 
			
		||||
                Image.core.drawwmf(im.fp.read(), im.size, self.bbox),
 | 
			
		||||
                "raw", "BGR", (im.size[0]*3 + 3) & -4, -1
 | 
			
		||||
                "raw",
 | 
			
		||||
                "BGR",
 | 
			
		||||
                (im.size[0] * 3 + 3) & -4,
 | 
			
		||||
                -1,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    register_handler(WmfHandler())
 | 
			
		||||
| 
						 | 
				
			
			@ -73,14 +75,14 @@ if hasattr(Image.core, "drawwmf"):
 | 
			
		|||
 | 
			
		||||
def _accept(prefix):
 | 
			
		||||
    return (
 | 
			
		||||
        prefix[:6] == b"\xd7\xcd\xc6\x9a\x00\x00" or
 | 
			
		||||
        prefix[:4] == b"\x01\x00\x00\x00"
 | 
			
		||||
        prefix[:6] == b"\xd7\xcd\xc6\x9a\x00\x00" or prefix[:4] == b"\x01\x00\x00\x00"
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
# Image plugin for Windows metafiles.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class WmfStubImageFile(ImageFile.StubImageFile):
 | 
			
		||||
 | 
			
		||||
    format = "WMF"
 | 
			
		||||
| 
						 | 
				
			
			@ -160,6 +162,7 @@ def _save(im, fp, filename):
 | 
			
		|||
        raise IOError("WMF save handler not installed")
 | 
			
		||||
    _handler.save(im, fp, filename)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# --------------------------------------------------------------------
 | 
			
		||||
# Registry stuff
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,7 +31,9 @@ PALETTE = b""
 | 
			
		|||
for r in range(8):
 | 
			
		||||
    for g in range(8):
 | 
			
		||||
        for b in range(4):
 | 
			
		||||
            PALETTE = PALETTE + (o8((r*255)//7)+o8((g*255)//7)+o8((b*255)//3))
 | 
			
		||||
            PALETTE = PALETTE + (
 | 
			
		||||
                o8((r * 255) // 7) + o8((g * 255) // 7) + o8((b * 255) // 3)
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _accept(prefix):
 | 
			
		||||
| 
						 | 
				
			
			@ -41,6 +43,7 @@ def _accept(prefix):
 | 
			
		|||
##
 | 
			
		||||
# Image plugin for XV thumbnail images.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class XVThumbImageFile(ImageFile.ImageFile):
 | 
			
		||||
 | 
			
		||||
    format = "XVThumb"
 | 
			
		||||
| 
						 | 
				
			
			@ -71,10 +74,7 @@ class XVThumbImageFile(ImageFile.ImageFile):
 | 
			
		|||
 | 
			
		||||
        self.palette = ImagePalette.raw("RGB", PALETTE)
 | 
			
		||||
 | 
			
		||||
        self.tile = [
 | 
			
		||||
            ("raw", (0, 0)+self.size,
 | 
			
		||||
             self.fp.tell(), (self.mode, 0, 1)
 | 
			
		||||
             )]
 | 
			
		||||
        self.tile = [("raw", (0, 0) + self.size, self.fp.tell(), (self.mode, 0, 1))]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# --------------------------------------------------------------------
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,6 +45,7 @@ def _accept(prefix):
 | 
			
		|||
##
 | 
			
		||||
# Image plugin for X11 bitmaps.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class XbmImageFile(ImageFile.ImageFile):
 | 
			
		||||
 | 
			
		||||
    format = "XBM"
 | 
			
		||||
| 
						 | 
				
			
			@ -60,14 +61,12 @@ class XbmImageFile(ImageFile.ImageFile):
 | 
			
		|||
            ysize = int(m.group("height"))
 | 
			
		||||
 | 
			
		||||
            if m.group("hotspot"):
 | 
			
		||||
                self.info["hotspot"] = (
 | 
			
		||||
                    int(m.group("xhot")), int(m.group("yhot"))
 | 
			
		||||
                    )
 | 
			
		||||
                self.info["hotspot"] = (int(m.group("xhot")), int(m.group("yhot")))
 | 
			
		||||
 | 
			
		||||
            self.mode = "1"
 | 
			
		||||
            self._size = xsize, ysize
 | 
			
		||||
 | 
			
		||||
            self.tile = [("xbm", (0, 0)+self.size, m.end(), None)]
 | 
			
		||||
            self.tile = [("xbm", (0, 0) + self.size, m.end(), None)]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _save(im, fp, filename):
 | 
			
		||||
| 
						 | 
				
			
			@ -75,17 +74,17 @@ def _save(im, fp, filename):
 | 
			
		|||
    if im.mode != "1":
 | 
			
		||||
        raise IOError("cannot write mode %s as XBM" % im.mode)
 | 
			
		||||
 | 
			
		||||
    fp.write(("#define im_width %d\n" % im.size[0]).encode('ascii'))
 | 
			
		||||
    fp.write(("#define im_height %d\n" % im.size[1]).encode('ascii'))
 | 
			
		||||
    fp.write(("#define im_width %d\n" % im.size[0]).encode("ascii"))
 | 
			
		||||
    fp.write(("#define im_height %d\n" % im.size[1]).encode("ascii"))
 | 
			
		||||
 | 
			
		||||
    hotspot = im.encoderinfo.get("hotspot")
 | 
			
		||||
    if hotspot:
 | 
			
		||||
        fp.write(("#define im_x_hot %d\n" % hotspot[0]).encode('ascii'))
 | 
			
		||||
        fp.write(("#define im_y_hot %d\n" % hotspot[1]).encode('ascii'))
 | 
			
		||||
        fp.write(("#define im_x_hot %d\n" % hotspot[0]).encode("ascii"))
 | 
			
		||||
        fp.write(("#define im_y_hot %d\n" % hotspot[1]).encode("ascii"))
 | 
			
		||||
 | 
			
		||||
    fp.write(b"static char im_bits[] = {\n")
 | 
			
		||||
 | 
			
		||||
    ImageFile._save(im, fp, [("xbm", (0, 0)+im.size, 0, None)])
 | 
			
		||||
    ImageFile._save(im, fp, [("xbm", (0, 0) + im.size, 0, None)])
 | 
			
		||||
 | 
			
		||||
    fp.write(b"};\n")
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,7 +24,7 @@ from ._binary import i8, o8
 | 
			
		|||
__version__ = "0.2"
 | 
			
		||||
 | 
			
		||||
# XPM header
 | 
			
		||||
xpm_head = re.compile(b"\"([0-9]*) ([0-9]*) ([0-9]*) ([0-9]*)")
 | 
			
		||||
xpm_head = re.compile(b'"([0-9]*) ([0-9]*) ([0-9]*) ([0-9]*)')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _accept(prefix):
 | 
			
		||||
| 
						 | 
				
			
			@ -34,6 +34,7 @@ def _accept(prefix):
 | 
			
		|||
##
 | 
			
		||||
# Image plugin for X11 pixel maps.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class XpmImageFile(ImageFile.ImageFile):
 | 
			
		||||
 | 
			
		||||
    format = "XPM"
 | 
			
		||||
| 
						 | 
				
			
			@ -69,9 +70,9 @@ class XpmImageFile(ImageFile.ImageFile):
 | 
			
		|||
        for i in range(pal):
 | 
			
		||||
 | 
			
		||||
            s = self.fp.readline()
 | 
			
		||||
            if s[-2:] == b'\r\n':
 | 
			
		||||
            if s[-2:] == b"\r\n":
 | 
			
		||||
                s = s[:-2]
 | 
			
		||||
            elif s[-1:] in b'\r\n':
 | 
			
		||||
            elif s[-1:] in b"\r\n":
 | 
			
		||||
                s = s[:-1]
 | 
			
		||||
 | 
			
		||||
            c = i8(s[1])
 | 
			
		||||
| 
						 | 
				
			
			@ -82,15 +83,15 @@ class XpmImageFile(ImageFile.ImageFile):
 | 
			
		|||
                if s[i] == b"c":
 | 
			
		||||
 | 
			
		||||
                    # process colour key
 | 
			
		||||
                    rgb = s[i+1]
 | 
			
		||||
                    rgb = s[i + 1]
 | 
			
		||||
                    if rgb == b"None":
 | 
			
		||||
                        self.info["transparency"] = c
 | 
			
		||||
                    elif rgb[0:1] == b"#":
 | 
			
		||||
                        # FIXME: handle colour names (see ImagePalette.py)
 | 
			
		||||
                        rgb = int(rgb[1:], 16)
 | 
			
		||||
                        palette[c] = (o8((rgb >> 16) & 255) +
 | 
			
		||||
                                      o8((rgb >> 8) & 255) +
 | 
			
		||||
                                      o8(rgb & 255))
 | 
			
		||||
                        palette[c] = (
 | 
			
		||||
                            o8((rgb >> 16) & 255) + o8((rgb >> 8) & 255) + o8(rgb & 255)
 | 
			
		||||
                        )
 | 
			
		||||
                    else:
 | 
			
		||||
                        # unknown colour
 | 
			
		||||
                        raise ValueError("cannot read this XPM file")
 | 
			
		||||
| 
						 | 
				
			
			@ -104,7 +105,7 @@ class XpmImageFile(ImageFile.ImageFile):
 | 
			
		|||
        self.mode = "P"
 | 
			
		||||
        self.palette = ImagePalette.raw("RGB", b"".join(palette))
 | 
			
		||||
 | 
			
		||||
        self.tile = [("raw", (0, 0)+self.size, self.fp.tell(), ("P", 0, 1))]
 | 
			
		||||
        self.tile = [("raw", (0, 0) + self.size, self.fp.tell(), ("P", 0, 1))]
 | 
			
		||||
 | 
			
		||||
    def load_read(self, bytes):
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -116,10 +117,11 @@ class XpmImageFile(ImageFile.ImageFile):
 | 
			
		|||
        s = [None] * ysize
 | 
			
		||||
 | 
			
		||||
        for i in range(ysize):
 | 
			
		||||
            s[i] = self.fp.readline()[1:xsize+1].ljust(xsize)
 | 
			
		||||
            s[i] = self.fp.readline()[1 : xsize + 1].ljust(xsize)
 | 
			
		||||
 | 
			
		||||
        return b"".join(s)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Registry
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,48 +24,50 @@ PILLOW_VERSION = __version__ = _version.__version__
 | 
			
		|||
del _version
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_plugins = ['BlpImagePlugin',
 | 
			
		||||
            'BmpImagePlugin',
 | 
			
		||||
            'BufrStubImagePlugin',
 | 
			
		||||
            'CurImagePlugin',
 | 
			
		||||
            'DcxImagePlugin',
 | 
			
		||||
            'DdsImagePlugin',
 | 
			
		||||
            'EpsImagePlugin',
 | 
			
		||||
            'FitsStubImagePlugin',
 | 
			
		||||
            'FliImagePlugin',
 | 
			
		||||
            'FpxImagePlugin',
 | 
			
		||||
            'FtexImagePlugin',
 | 
			
		||||
            'GbrImagePlugin',
 | 
			
		||||
            'GifImagePlugin',
 | 
			
		||||
            'GribStubImagePlugin',
 | 
			
		||||
            'Hdf5StubImagePlugin',
 | 
			
		||||
            'IcnsImagePlugin',
 | 
			
		||||
            'IcoImagePlugin',
 | 
			
		||||
            'ImImagePlugin',
 | 
			
		||||
            'ImtImagePlugin',
 | 
			
		||||
            'IptcImagePlugin',
 | 
			
		||||
            'JpegImagePlugin',
 | 
			
		||||
            'Jpeg2KImagePlugin',
 | 
			
		||||
            'McIdasImagePlugin',
 | 
			
		||||
            'MicImagePlugin',
 | 
			
		||||
            'MpegImagePlugin',
 | 
			
		||||
            'MpoImagePlugin',
 | 
			
		||||
            'MspImagePlugin',
 | 
			
		||||
            'PalmImagePlugin',
 | 
			
		||||
            'PcdImagePlugin',
 | 
			
		||||
            'PcxImagePlugin',
 | 
			
		||||
            'PdfImagePlugin',
 | 
			
		||||
            'PixarImagePlugin',
 | 
			
		||||
            'PngImagePlugin',
 | 
			
		||||
            'PpmImagePlugin',
 | 
			
		||||
            'PsdImagePlugin',
 | 
			
		||||
            'SgiImagePlugin',
 | 
			
		||||
            'SpiderImagePlugin',
 | 
			
		||||
            'SunImagePlugin',
 | 
			
		||||
            'TgaImagePlugin',
 | 
			
		||||
            'TiffImagePlugin',
 | 
			
		||||
            'WebPImagePlugin',
 | 
			
		||||
            'WmfImagePlugin',
 | 
			
		||||
            'XbmImagePlugin',
 | 
			
		||||
            'XpmImagePlugin',
 | 
			
		||||
            'XVThumbImagePlugin']
 | 
			
		||||
_plugins = [
 | 
			
		||||
    "BlpImagePlugin",
 | 
			
		||||
    "BmpImagePlugin",
 | 
			
		||||
    "BufrStubImagePlugin",
 | 
			
		||||
    "CurImagePlugin",
 | 
			
		||||
    "DcxImagePlugin",
 | 
			
		||||
    "DdsImagePlugin",
 | 
			
		||||
    "EpsImagePlugin",
 | 
			
		||||
    "FitsStubImagePlugin",
 | 
			
		||||
    "FliImagePlugin",
 | 
			
		||||
    "FpxImagePlugin",
 | 
			
		||||
    "FtexImagePlugin",
 | 
			
		||||
    "GbrImagePlugin",
 | 
			
		||||
    "GifImagePlugin",
 | 
			
		||||
    "GribStubImagePlugin",
 | 
			
		||||
    "Hdf5StubImagePlugin",
 | 
			
		||||
    "IcnsImagePlugin",
 | 
			
		||||
    "IcoImagePlugin",
 | 
			
		||||
    "ImImagePlugin",
 | 
			
		||||
    "ImtImagePlugin",
 | 
			
		||||
    "IptcImagePlugin",
 | 
			
		||||
    "JpegImagePlugin",
 | 
			
		||||
    "Jpeg2KImagePlugin",
 | 
			
		||||
    "McIdasImagePlugin",
 | 
			
		||||
    "MicImagePlugin",
 | 
			
		||||
    "MpegImagePlugin",
 | 
			
		||||
    "MpoImagePlugin",
 | 
			
		||||
    "MspImagePlugin",
 | 
			
		||||
    "PalmImagePlugin",
 | 
			
		||||
    "PcdImagePlugin",
 | 
			
		||||
    "PcxImagePlugin",
 | 
			
		||||
    "PdfImagePlugin",
 | 
			
		||||
    "PixarImagePlugin",
 | 
			
		||||
    "PngImagePlugin",
 | 
			
		||||
    "PpmImagePlugin",
 | 
			
		||||
    "PsdImagePlugin",
 | 
			
		||||
    "SgiImagePlugin",
 | 
			
		||||
    "SpiderImagePlugin",
 | 
			
		||||
    "SunImagePlugin",
 | 
			
		||||
    "TgaImagePlugin",
 | 
			
		||||
    "TiffImagePlugin",
 | 
			
		||||
    "WebPImagePlugin",
 | 
			
		||||
    "WmfImagePlugin",
 | 
			
		||||
    "XbmImagePlugin",
 | 
			
		||||
    "XpmImagePlugin",
 | 
			
		||||
    "XVThumbImagePlugin",
 | 
			
		||||
]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,12 +15,16 @@ from struct import unpack_from, pack
 | 
			
		|||
from ._util import py3
 | 
			
		||||
 | 
			
		||||
if py3:
 | 
			
		||||
 | 
			
		||||
    def i8(c):
 | 
			
		||||
        return c if c.__class__ is int else c[0]
 | 
			
		||||
 | 
			
		||||
    def o8(i):
 | 
			
		||||
        return bytes((i & 255,))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
else:
 | 
			
		||||
 | 
			
		||||
    def i8(c):
 | 
			
		||||
        return ord(c)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,7 +7,7 @@ if sys.version_info.major > 2:
 | 
			
		|||
else:
 | 
			
		||||
    from Tkinter import tkinter as tk
 | 
			
		||||
 | 
			
		||||
if hasattr(sys, 'pypy_find_executable'):
 | 
			
		||||
if hasattr(sys, "pypy_find_executable"):
 | 
			
		||||
    # Tested with packages at https://bitbucket.org/pypy/pypy/downloads.
 | 
			
		||||
    # PyPies 1.6, 2.0 do not have tkinter built in.  PyPy3-2.3.1 gives an
 | 
			
		||||
    # OSError trying to import tkinter. Otherwise:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,6 +5,7 @@ py3 = sys.version_info.major >= 3
 | 
			
		|||
py36 = sys.version_info[0:2] >= (3, 6)
 | 
			
		||||
 | 
			
		||||
if py3:
 | 
			
		||||
 | 
			
		||||
    def isStringType(t):
 | 
			
		||||
        return isinstance(t, str)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -13,10 +14,15 @@ if py3:
 | 
			
		|||
 | 
			
		||||
        def isPath(f):
 | 
			
		||||
            return isinstance(f, (bytes, str, Path))
 | 
			
		||||
 | 
			
		||||
    else:
 | 
			
		||||
 | 
			
		||||
        def isPath(f):
 | 
			
		||||
            return isinstance(f, (bytes, str))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
else:
 | 
			
		||||
 | 
			
		||||
    def isStringType(t):
 | 
			
		||||
        return isinstance(t, basestring)  # noqa: F821
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,2 +1,2 @@
 | 
			
		|||
# Master version for Pillow
 | 
			
		||||
__version__ = '6.1.0.dev0'
 | 
			
		||||
__version__ = "6.1.0.dev0"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,12 +26,7 @@ def get_supported_modules():
 | 
			
		|||
    return [f for f in modules if check_module(f)]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
codecs = {
 | 
			
		||||
    "jpg": "jpeg",
 | 
			
		||||
    "jpg_2000": "jpeg2k",
 | 
			
		||||
    "zlib": "zip",
 | 
			
		||||
    "libtiff": "libtiff"
 | 
			
		||||
}
 | 
			
		||||
codecs = {"jpg": "jpeg", "jpg_2000": "jpeg2k", "zlib": "zip", "libtiff": "libtiff"}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def check_codec(feature):
 | 
			
		||||
| 
						 | 
				
			
			@ -48,8 +43,8 @@ def get_supported_codecs():
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
features = {
 | 
			
		||||
    "webp_anim": ("PIL._webp", 'HAVE_WEBPANIM'),
 | 
			
		||||
    "webp_mux": ("PIL._webp", 'HAVE_WEBPMUX'),
 | 
			
		||||
    "webp_anim": ("PIL._webp", "HAVE_WEBPANIM"),
 | 
			
		||||
    "webp_mux": ("PIL._webp", "HAVE_WEBPMUX"),
 | 
			
		||||
    "transp_webp": ("PIL._webp", "HAVE_TRANSPARENCY"),
 | 
			
		||||
    "raqm": ("PIL._imagingft", "HAVE_RAQM"),
 | 
			
		||||
    "libjpeg_turbo": ("PIL._imaging", "HAVE_LIBJPEGTURBO"),
 | 
			
		||||
| 
						 | 
				
			
			@ -63,7 +58,7 @@ def check_feature(feature):
 | 
			
		|||
    module, flag = features[feature]
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        imported_module = __import__(module, fromlist=['PIL'])
 | 
			
		||||
        imported_module = __import__(module, fromlist=["PIL"])
 | 
			
		||||
        return getattr(imported_module, flag)
 | 
			
		||||
    except ImportError:
 | 
			
		||||
        return None
 | 
			
		||||
| 
						 | 
				
			
			@ -74,9 +69,14 @@ def get_supported_features():
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
def check(feature):
 | 
			
		||||
    return (feature in modules and check_module(feature) or
 | 
			
		||||
            feature in codecs and check_codec(feature) or
 | 
			
		||||
            feature in features and check_feature(feature))
 | 
			
		||||
    return (
 | 
			
		||||
        feature in modules
 | 
			
		||||
        and check_module(feature)
 | 
			
		||||
        or feature in codecs
 | 
			
		||||
        and check_codec(feature)
 | 
			
		||||
        or feature in features
 | 
			
		||||
        and check_feature(feature)
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_supported():
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user