rebuild for 10.11+

This commit is contained in:
j 2019-01-10 14:33:55 +05:30
commit 901b731582
234 changed files with 1522 additions and 927 deletions

View file

@ -50,16 +50,17 @@ def _accept(prefix):
return prefix[:2] == b"BM"
# ==============================================================================
# =============================================================================
# Image plugin for the Windows BMP format.
# ==============================================================================
# =============================================================================
class BmpImageFile(ImageFile.ImageFile):
""" Image plugin for the Windows Bitmap format (BMP) """
# -------------------------------------------------------------- Description
# ------------------------------------------------------------- Description
format_description = "Windows Bitmap"
format = "BMP"
# --------------------------------------------------- BMP Compression values
# -------------------------------------------------- BMP Compression values
COMPRESSIONS = {
'RAW': 0,
'RLE8': 1,
@ -79,12 +80,14 @@ class BmpImageFile(ImageFile.ImageFile):
# read bmp header size @offset 14 (this is part of the header size)
file_info['header_size'] = i32(read(4))
file_info['direction'] = -1
# --------------------- If requested, read header at a specific position
# -------------------- 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)
# --------------------------------------------------- IBM OS/2 Bitmap v1
# ------ This format has different offsets because of width/height types
# -------------------------------------------------- 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])
@ -92,8 +95,10 @@ class BmpImageFile(ImageFile.ImageFile):
file_info['bits'] = i16(header_data[6:8])
file_info['compression'] = self.RAW
file_info['palette_padding'] = 3
# ---------------------------------------------- Windows Bitmap v2 to v5
elif file_info['header_size'] in (40, 64, 108, 124): # v3, OS/2 v2, v4, v5
# --------------------------------------------- Windows Bitmap v2 to v5
# v3, OS/2 v2, v4, v5
elif file_info['header_size'] in (40, 64, 108, 124):
if file_info['header_size'] >= 40: # v3 and OS/2
file_info['y_flip'] = i8(header_data[7]) == 0xff
file_info['direction'] = 1 if file_info['y_flip'] else -1
@ -119,12 +124,15 @@ class BmpImageFile(ImageFile.ImageFile):
'g_mask',
'b_mask',
'a_mask']):
file_info[mask] = i32(header_data[36+idx*4:40+idx*4])
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: https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
# See also https://github.com/python-pillow/Pillow/issues/1293
# bitfields masks, ref:
# https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
# See also
# https://github.com/python-pillow/Pillow/issues/1293
# 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
@ -141,20 +149,27 @@ class BmpImageFile(ImageFile.ImageFile):
else:
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']
# -------- If color count was not found in the header, compute from bits
file_info['colors'] = file_info['colors'] if file_info.get('colors', 0) else (1 << file_info['bits'])
# -------------------------------- Check abnormal values for DOS attacks
# ------- If color count was not found in the header, compute from bits
file_info["colors"] = (file_info["colors"]
if file_info.get("colors", 0)
else (1 << file_info["bits"]))
# ------------------------------- Check abnormal values for DOS attacks
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
# ---------------------- Check bit depth for unusual unsupported values
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'])
# ----------------- Process BMP with Bitfields compression (not palette)
# ---------------- Process BMP with Bitfields compression (not palette)
if file_info['compression'] == self.BITFIELDS:
SUPPORTED = {
32: [(0xff0000, 0xff00, 0xff, 0x0),
@ -176,7 +191,9 @@ class BmpImageFile(ImageFile.ImageFile):
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'])]
raw_mode = MASK_MODES[
(file_info["bits"], file_info["rgba_mask"])
]
self.mode = "RGBA" if raw_mode in ("BGRA",) else self.mode
elif (file_info['bits'] in (24, 16) and
file_info['rgb_mask'] in SUPPORTED[file_info['bits']]):
@ -193,9 +210,11 @@ class BmpImageFile(ImageFile.ImageFile):
else:
raise IOError("Unsupported BMP compression (%d)" %
file_info['compression'])
# ---------------- Once the header is processed, process the palette/LUT
# --------------- Once the header is processed, process the palette/LUT
if self.mode == "P": # Paletted for 1, 4 and 8 bit images
# ----------------------------------------------------- 1-bit images
# ---------------------------------------------------- 1-bit images
if not (0 < file_info['colors'] <= 65536):
raise IOError("Unsupported BMP Palette size (%d)" %
file_info['colors'])
@ -205,12 +224,14 @@ class BmpImageFile(ImageFile.ImageFile):
greyscale = True
indices = (0, 255) if file_info['colors'] == 2 else \
list(range(file_info['colors']))
# ------------------ Check if greyscale and ignore palette if so
# ----------------- Check if greyscale and ignore palette if so
for ind, val in enumerate(indices):
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 all colors are grey, white or black, ditch palette
if greyscale:
self.mode = "1" if file_info['colors'] == 2 else "L"
raw_mode = self.mode
@ -219,7 +240,7 @@ class BmpImageFile(ImageFile.ImageFile):
self.palette = ImagePalette.raw(
"BGRX" if padding == 4 else "BGR", palette)
# ----------------------------- Finally set the tile data for the plugin
# ---------------------------- Finally set the tile data for the plugin
self.info['compression'] = file_info['compression']
self.tile = [
('raw',
@ -243,9 +264,9 @@ class BmpImageFile(ImageFile.ImageFile):
self._bitmap(offset=offset)
# ==============================================================================
# =============================================================================
# Image plugin for the DIB format (BMP alias)
# ==============================================================================
# =============================================================================
class DibImageFile(BmpImageFile):
format = "DIB"

View file

@ -81,6 +81,15 @@ class DcxImageFile(PcxImageFile):
def tell(self):
return self.frame
def _close__fp(self):
try:
if self.__fp != self.fp:
self.__fp.close()
except AttributeError:
pass
finally:
self.__fp = None
Image.register_open(DcxImageFile.format, DcxImageFile, _accept)

View file

@ -118,7 +118,7 @@ class DdsImageFile(ImageFile.ImageFile):
self.mode = "RGBA"
pitch, depth, mipmaps = struct.unpack("<3I", header.read(12))
reserved = struct.unpack("<11I", header.read(44))
struct.unpack("<11I", header.read(44)) # reserved
# pixel format
pfsize, pfflags = struct.unpack("<2I", header.read(8))

View file

@ -125,10 +125,11 @@ def Ghostscript(tile, size, fp, scale=1):
"-dSAFER", # safe mode
"-sDEVICE=ppmraw", # ppm driver
"-sOutputFile=%s" % outfile, # output file
# adjust for image origin
"-c", "%d %d translate" % (-bbox[0], -bbox[1]),
# adjust for image origin
"-f", infile, # input file
"-c", "showpage", # showpage (see: https://bugs.ghostscript.com/show_bug.cgi?id=698272)
# showpage (see https://bugs.ghostscript.com/show_bug.cgi?id=698272)
"-c", "showpage",
]
if gs_windows_binary is not None:

View file

@ -131,6 +131,9 @@ class FliImageFile(ImageFile.ImageFile):
self.__frame = -1
self.__fp.seek(self.__rewind)
self.__offset = 128
else:
# ensure that the previous frame was loaded
self.load()
if frame != self.__frame + 1:
raise ValueError("cannot seek to frame %d" % frame)
@ -154,6 +157,15 @@ class FliImageFile(ImageFile.ImageFile):
def tell(self):
return self.__frame
def _close__fp(self):
try:
if self.__fp != self.fp:
self.__fp.close()
except AttributeError:
pass
finally:
self.__fp = None
#
# registry

View file

@ -20,7 +20,14 @@ has the following structure:
{format_directory}
{data}
Where:
{header} = { u32:magic, u32:version, u32:width, u32:height, u32:mipmap_count, u32:format_count }
{header} = {
u32:magic,
u32:version,
u32:width,
u32:height,
u32:mipmap_count,
u32:format_count
}
* The "magic" number is "FTEX".
* "width" and "height" are the dimensions of the texture.
@ -59,8 +66,8 @@ class FtexImageFile(ImageFile.ImageFile):
format_description = "Texture File Format (IW2:EOC)"
def _open(self):
magic = struct.unpack("<I", self.fp.read(4))
version = struct.unpack("<i", self.fp.read(4))
struct.unpack("<I", self.fp.read(4)) # magic
struct.unpack("<i", self.fp.read(4)) # version
self._size = struct.unpack("<2i", self.fp.read(8))
mipmap_count, format_count = struct.unpack("<2i", self.fp.read(8))

View file

@ -59,7 +59,8 @@ class GdImageFile(ImageFile.ImageFile):
if tindex < 256:
self.info["transparency"] = tindex
self.palette = ImagePalette.raw("XBGR", s[7+trueColorOffset+4:7+trueColorOffset+4+256*4])
self.palette = ImagePalette.raw(
"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))]

View file

@ -201,7 +201,13 @@ class GifImageFile(ImageFile.ImageFile):
#
# comment extension
#
info["comment"] = block
while block:
if "comment" in info:
info["comment"] += block
else:
info["comment"] = block
block = self.data()
continue
elif i8(s) == 255:
#
# application extension
@ -296,6 +302,15 @@ class GifImageFile(ImageFile.ImageFile):
self.im = self._prev_im
self._prev_im = self.im.copy()
def _close__fp(self):
try:
if self.__fp != self.fp:
self.__fp.close()
except AttributeError:
pass
finally:
self.__fp = None
# --------------------------------------------------------------------
# Write GIF files
@ -379,6 +394,8 @@ def _normalize_palette(im, palette, info):
def _write_single_frame(im, fp, palette):
im_out = _normalize_mode(im, True)
for k, v in im_out.info.items():
im.encoderinfo.setdefault(k, v)
im_out = _normalize_palette(im_out, palette, im.encoderinfo)
for s in _get_global_header(im_out, im.encoderinfo):
@ -399,8 +416,8 @@ def _write_single_frame(im, fp, palette):
def _write_multiple_frames(im, fp, palette):
duration = im.encoderinfo.get("duration", None)
disposal = im.encoderinfo.get('disposal', None)
duration = im.encoderinfo.get("duration", im.info.get("duration"))
disposal = im.encoderinfo.get("disposal", im.info.get("disposal"))
im_frames = []
frame_count = 0
@ -409,6 +426,9 @@ def _write_multiple_frames(im, fp, palette):
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())
if frame_count == 0:
for k, v in im_frame.info.items():
im.encoderinfo.setdefault(k, v)
im_frame = _normalize_palette(im_frame, palette, im.encoderinfo)
encoderinfo = im.encoderinfo.copy()
@ -467,12 +487,10 @@ def _save_all(im, fp, filename):
def _save(im, fp, filename, save_all=False):
for k, v in im.info.items():
im.encoderinfo.setdefault(k, v)
# header
try:
palette = im.encoderinfo["palette"]
except KeyError:
if "palette" in im.encoderinfo or "palette" in im.info:
palette = im.encoderinfo.get("palette", im.info.get("palette"))
else:
palette = None
im.encoderinfo["optimize"] = im.encoderinfo.get("optimize", True)
@ -536,12 +554,14 @@ def _write_local_header(fp, im, offset, flags):
o8(0))
if "comment" in im.encoderinfo and \
1 <= len(im.encoderinfo["comment"]) <= 255:
1 <= len(im.encoderinfo["comment"]):
fp.write(b"!" +
o8(254) + # extension intro
o8(len(im.encoderinfo["comment"])) +
im.encoderinfo["comment"] +
o8(0))
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)
fp.write(o8(0))
if "loop" in im.encoderinfo:
number_of_loops = im.encoderinfo["loop"]
fp.write(b"!" +
@ -711,11 +731,18 @@ def _get_global_header(im, info):
if im.info.get("version") == b"89a":
version = b"89a"
background = 0
if "background" in info:
background = info["background"]
if isinstance(background, tuple):
# WebPImagePlugin stores an RGBA value in info["background"]
# So it must be converted to the same format as GifImagePlugin's
# info["background"] - a global color table index
background = im.palette.getcolor(background)
palette_bytes = _get_palette_bytes(im)
color_table_size = _get_color_table_size(palette_bytes)
background = info["background"] if "background" in info else 0
return [
b"GIF"+version + # signature + version
o16(im.size[0]) + # canvas width

View file

@ -284,7 +284,7 @@ class IcnsImageFile(ImageFile.ImageFile):
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']]
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']:
@ -311,6 +311,8 @@ class IcnsImageFile(ImageFile.ImageFile):
self.im = im.im
self.mode = im.mode
self.size = im.size
if self._exclusive_fp:
self.fp.close()
self.fp = None
self.icns = None
self.tile = ()
@ -333,6 +335,7 @@ def _save(im, fp, filename):
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)

View file

@ -153,7 +153,7 @@ class ImImageFile(ImageFile.ImageFile):
try:
m = split.match(s)
except re.error as v:
except re.error:
raise SyntaxError("not an IM file")
if m:
@ -290,6 +290,15 @@ class ImImageFile(ImageFile.ImageFile):
def tell(self):
return self.frame
def _close__fp(self):
try:
if self.__fp != self.fp:
self.__fp.close()
except AttributeError:
pass
finally:
self.__fp = None
#
# --------------------------------------------------------------------
# Save IM files

View file

@ -34,6 +34,36 @@ import logging
import warnings
import math
try:
import builtins
except ImportError:
import __builtin__
builtins = __builtin__
from . import ImageMode
from ._binary import i8
from ._util import isPath, isStringType, deferred_error
import os
import sys
import io
import struct
import atexit
# type stuff
import numbers
try:
# Python 3
from collections.abc import Callable
except ImportError:
# Python 2.7
from collections import Callable
# Silence warnings
assert VERSION
assert PILLOW_VERSION
logger = logging.getLogger(__name__)
@ -104,39 +134,13 @@ except ImportError as v:
# see docs/porting.rst
raise
try:
import builtins
except ImportError:
import __builtin__
builtins = __builtin__
from . import ImageMode
from ._binary import i8
from ._util import isPath, isStringType, deferred_error
import os
import sys
import io
import struct
import atexit
# type stuff
import numbers
try:
# Python 3
from collections.abc import Callable
except ImportError:
# Python 2.7
from collections import Callable
# works everywhere, win for pypy, not cpython
USE_CFFI_ACCESS = hasattr(sys, 'pypy_version_info')
try:
import cffi
HAS_CFFI = True
except ImportError:
HAS_CFFI = False
cffi = None
try:
from pathlib import Path
@ -164,7 +168,7 @@ def isImageType(t):
#
# Constants (also defined in _imagingmodule.c!)
# Constants
NONE = 0
@ -177,14 +181,14 @@ ROTATE_270 = 4
TRANSPOSE = 5
TRANSVERSE = 6
# transforms
# transforms (also defined in Imaging.h)
AFFINE = 0
EXTENT = 1
PERSPECTIVE = 2
QUAD = 3
MESH = 4
# resampling filters
# resampling filters (also defined in Imaging.h)
NEAREST = NONE = 0
BOX = 4
BILINEAR = LINEAR = 2
@ -376,26 +380,32 @@ def preinit():
try:
from . import BmpImagePlugin
assert BmpImagePlugin
except ImportError:
pass
try:
from . import GifImagePlugin
assert GifImagePlugin
except ImportError:
pass
try:
from . import JpegImagePlugin
assert JpegImagePlugin
except ImportError:
pass
try:
from . import PpmImagePlugin
assert PpmImagePlugin
except ImportError:
pass
try:
from . import PngImagePlugin
assert PngImagePlugin
except ImportError:
pass
# try:
# import TiffImagePlugin
# assert TiffImagePlugin
# except ImportError:
# pass
@ -564,7 +574,7 @@ class Image(object):
new.info = self.info.copy()
return new
# Context Manager Support
# Context manager support
def __enter__(self):
return self
@ -584,6 +594,8 @@ class Image(object):
:ref:`file-handling` for more information.
"""
try:
if hasattr(self, "_close__fp"):
self._close__fp()
self.fp.close()
self.fp = None
except Exception as msg:
@ -599,6 +611,8 @@ class Image(object):
if sys.version_info.major >= 3:
def __del__(self):
if hasattr(self, "_close__fp"):
self._close__fp()
if (hasattr(self, 'fp') and hasattr(self, '_exclusive_fp')
and self.fp and self._exclusive_fp):
self.fp.close()
@ -813,8 +827,10 @@ class Image(object):
Image class automatically loads an opened image when it is
accessed for the first time.
This method will close the file associated with the image. See
:ref:`file-handling` for more information.
If the file associated with the image was opened by Pillow, then this
method will close it. The exception to this is if the image has
multiple frames, in which case the file will be left open for seek
operations. See :ref:`file-handling` for more information.
:returns: An image access object.
:rtype: :ref:`PixelAccess` or :py:class:`PIL.PyAccess`
@ -833,7 +849,7 @@ class Image(object):
self.palette.mode = "RGBA"
if self.im:
if HAS_CFFI and USE_CFFI_ACCESS:
if cffi and USE_CFFI_ACCESS:
if self.pyaccess:
return self.pyaccess
from . import PyAccess
@ -865,7 +881,7 @@ class Image(object):
"L", "RGB" and "CMYK." The **matrix** argument only supports "L"
and "RGB".
When translating a color image to black and white (mode "L"),
When translating a color image to greyscale (mode "L"),
the library uses the ITU-R 601-2 luma transform::
L = R * 299/1000 + G * 587/1000 + B * 114/1000
@ -873,9 +889,9 @@ class Image(object):
The default method of converting a greyscale ("L") or "RGB"
image into a bilevel (mode "1") image uses Floyd-Steinberg
dither to approximate the original image luminosity levels. If
dither is NONE, all non-zero values are set to 255 (white). To
use other thresholds, use the :py:meth:`~PIL.Image.Image.point`
method.
dither is NONE, all values larger than 128 are set to 255 (white),
all other values to 0 (black). To use other thresholds, use the
:py:meth:`~PIL.Image.Image.point` method.
When converting from "RGBA" to "P" without a **matrix** argument,
this passes the operation to :py:meth:`~PIL.Image.Image.quantize`,
@ -961,7 +977,7 @@ class Image(object):
if isinstance(t, tuple):
try:
t = trns_im.palette.getcolor(t)
except:
except Exception:
raise ValueError("Couldn't allocate a palette "
"color for transparency")
trns_im.putpixel((0, 0), t)
@ -998,7 +1014,7 @@ class Image(object):
if trns is not None:
try:
new.info['transparency'] = new.palette.getcolor(trns)
except:
except Exception:
# if we can't make a transparent color, don't leave the old
# transparency hanging around to mess us up.
del(new.info['transparency'])
@ -1028,7 +1044,7 @@ class Image(object):
if new_im.mode == 'P':
try:
new_im.info['transparency'] = new_im.palette.getcolor(trns)
except:
except Exception:
del(new_im.info['transparency'])
warnings.warn("Couldn't allocate palette entry " +
"for transparency")
@ -1047,7 +1063,8 @@ class Image(object):
2 = fast octree
3 = libimagequant
:param kmeans: Integer
:param palette: Quantize to the palette of given :py:class:`PIL.Image.Image`.
:param palette: Quantize to the palette of given
:py:class:`PIL.Image.Image`.
:returns: A new image
"""
@ -1634,7 +1651,8 @@ class Image(object):
"""
Modifies the pixel at the given position. The color is given as
a single numerical value for single-band images, and a tuple for
multi-band images.
multi-band images. In addition to this, RGB and RGBA tuples are
accepted for P images.
Note that this method is relatively slow. For more extensive changes,
use :py:meth:`~PIL.Image.Image.paste` or the :py:mod:`~PIL.ImageDraw`
@ -1657,6 +1675,11 @@ class Image(object):
if self.pyaccess:
return self.pyaccess.putpixel(xy, value)
if self.mode == "P" and \
isinstance(value, (list, tuple)) and len(value) in [3, 4]:
# RGB or RGBA value for a P image
value = self.palette.getcolor(value)
return self.im.putpixel(xy, value)
def remap_palette(self, dest_map, source_palette=None):
@ -1773,11 +1796,10 @@ class Image(object):
if self.mode in ("1", "P"):
resample = NEAREST
if self.mode == 'LA':
return self.convert('La').resize(size, resample, box).convert('LA')
if self.mode == 'RGBA':
return self.convert('RGBa').resize(size, resample, box).convert('RGBA')
if self.mode in ['LA', 'RGBA']:
im = self.convert(self.mode[:-1]+'a')
im = im.resize(size, resample, box)
return im.convert(self.mode)
self.load()
@ -1849,7 +1871,8 @@ class Image(object):
else:
post_trans = translate
if center is None:
rotn_center = (w / 2.0, h / 2.0) # FIXME These should be rounded to ints?
# FIXME These should be rounded to ints?
rotn_center = (w / 2.0, h / 2.0)
else:
rotn_center = center
@ -1864,7 +1887,8 @@ class Image(object):
return a*x + b*y + c, d*x + e*y + f
matrix[2], matrix[5] = transform(-rotn_center[0] - post_trans[0],
-rotn_center[1] - post_trans[1], matrix)
-rotn_center[1] - post_trans[1],
matrix)
matrix[2] += rotn_center[0]
matrix[5] += rotn_center[1]
@ -1887,7 +1911,8 @@ class Image(object):
matrix)
w, h = nw, nh
return self.transform((w, h), AFFINE, matrix, resample, fillcolor=fillcolor)
return self.transform((w, h), AFFINE, matrix, resample,
fillcolor=fillcolor)
def save(self, fp, format=None, **params):
"""
@ -2154,8 +2179,8 @@ class Image(object):
:param fill: If **method** is an
:py:class:`~PIL.Image.ImageTransformHandler` object, this is one of
the arguments passed to it. Otherwise, it is unused.
:param fillcolor: Optional fill color for the area outside the transform
in the output image.
:param fillcolor: Optional fill color for the area outside the
transform in the output image.
:returns: An :py:class:`~PIL.Image.Image` object.
"""
@ -2620,6 +2645,7 @@ def open(fp, mode="r"):
preinit()
accept_warnings = []
def _open_core(fp, filename, prefix):
for i in ID:
try:
@ -2637,6 +2663,10 @@ def open(fp, mode="r"):
# opening failures that are entirely expected.
# logger.debug("", exc_info=True)
continue
except Exception:
if exclusive_fp:
fp.close()
raise
return None
im = _open_core(fp, filename, prefix)

View file

@ -54,7 +54,7 @@ def invert(image):
def lighter(image1, image2):
"""
Compares the two images, pixel by pixel, and returns a new image containing
the lighter values.
the lighter values. At least one of the images must have mode "1".
.. code-block:: python
@ -70,8 +70,8 @@ def lighter(image1, image2):
def darker(image1, image2):
"""
Compares the two images, pixel by pixel, and returns a new image
containing the darker values.
Compares the two images, pixel by pixel, and returns a new image containing
the darker values. At least one of the images must have mode "1".
.. code-block:: python
@ -88,7 +88,7 @@ def darker(image1, image2):
def difference(image1, image2):
"""
Returns the absolute value of the pixel-by-pixel difference between the two
images.
images. At least one of the images must have mode "1".
.. code-block:: python
@ -107,7 +107,8 @@ def multiply(image1, image2):
Superimposes two images on top of each other.
If you multiply an image with a solid black image, the result is black. If
you multiply with a solid white image, the image is unaffected.
you multiply with a solid white image, the image is unaffected. At least
one of the images must have mode "1".
.. code-block:: python
@ -123,7 +124,8 @@ def multiply(image1, image2):
def screen(image1, image2):
"""
Superimposes two inverted images on top of each other.
Superimposes two inverted images on top of each other. At least one of the
images must have mode "1".
.. code-block:: python
@ -141,6 +143,7 @@ def add(image1, image2, scale=1.0, offset=0):
"""
Adds two images, dividing the result by scale and adding the
offset. If omitted, scale defaults to 1.0, and offset to 0.0.
At least one of the images must have mode "1".
.. code-block:: python
@ -156,8 +159,9 @@ def add(image1, image2, scale=1.0, offset=0):
def subtract(image1, image2, scale=1.0, offset=0):
"""
Subtracts two images, dividing the result by scale and adding the
offset. If omitted, scale defaults to 1.0, and offset to 0.0.
Subtracts two images, dividing the result by scale and adding the offset.
If omitted, scale defaults to 1.0, and offset to 0.0. At least one of the
images must have mode "1".
.. code-block:: python
@ -172,7 +176,8 @@ def subtract(image1, image2, scale=1.0, offset=0):
def add_modulo(image1, image2):
"""Add two images, without clipping the result.
"""Add two images, without clipping the result. At least one of the images
must have mode "1".
.. code-block:: python
@ -187,7 +192,8 @@ def add_modulo(image1, image2):
def subtract_modulo(image1, image2):
"""Subtract two images, without clipping the result.
"""Subtract two images, without clipping the result. At least one of the
images must have mode "1".
.. code-block:: python
@ -202,7 +208,8 @@ def subtract_modulo(image1, image2):
def logical_and(image1, image2):
"""Logical AND between two images.
"""Logical AND between two images. At least one of the images must have
mode "1".
.. code-block:: python
@ -217,7 +224,8 @@ def logical_and(image1, image2):
def logical_or(image1, image2):
"""Logical OR between two images.
"""Logical OR between two images. At least one of the images must have
mode "1".
.. code-block:: python
@ -232,7 +240,8 @@ def logical_or(image1, image2):
def logical_xor(image1, image2):
"""Logical XOR between two images.
"""Logical XOR between two images. At least one of the images must have
mode "1".
.. code-block:: python

View file

@ -647,7 +647,7 @@ def createProfile(colorSpace, colorTemp=-1):
if colorSpace == "LAB":
try:
colorTemp = float(colorTemp)
except:
except (TypeError, ValueError):
raise PyCMSError(
"Color temperature must be numeric, \"%s\" not valid"
% colorTemp)
@ -727,7 +727,7 @@ def getProfileInfo(profile):
# add an extra newline to preserve pyCMS compatibility
# Python, not C. the white point bits weren't working well,
# so skipping.
# // info was description \r\n\r\n copyright \r\n\r\n K007 tag \r\n\r\n whitepoint
# info was description \r\n\r\n copyright \r\n\r\n K007 tag \r\n\r\n whitepoint
description = profile.profile.product_description
cpright = profile.profile.product_copyright
arr = []

View file

@ -87,7 +87,10 @@ def getrgb(color):
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)
m = re.match(
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(
@ -101,7 +104,10 @@ def getrgb(color):
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)
m = re.match(
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(

View file

@ -391,8 +391,8 @@ def floodfill(image, xy, value, border=None, thresh=0):
pixel.
:param thresh: Optional threshold value which specifies a maximum
tolerable difference of a pixel value from the 'background' in
order for it to be replaced. Useful for filling regions of non-
homogeneous, but similar, colors.
order for it to be replaced. Useful for filling regions of
non-homogeneous, but similar, colors.
"""
# based on an implementation by Eric S. Raymond
# amended by yo1995 @20180806
@ -406,7 +406,9 @@ def floodfill(image, xy, value, border=None, thresh=0):
except (ValueError, IndexError):
return # seed point outside image
edge = {(x, y)}
full_edge = set() # use a set to keep record of current and previous edge pixels to reduce memory consumption
# use a set to keep record of current and previous edge pixels
# to reduce memory consumption
full_edge = set()
while edge:
new_edge = set()
for (x, y) in edge: # 4 adjacent method

View file

@ -79,6 +79,8 @@ class ImageFile(Image.Image):
self._min_frame = 0
self.custom_mimetype = None
self.tile = None
self.readonly = 1 # until we know better
@ -120,7 +122,7 @@ class ImageFile(Image.Image):
def get_format_mimetype(self):
if self.format is None:
return
return Image.MIME.get(self.format.upper())
return self.custom_mimetype or Image.MIME.get(self.format.upper())
def verify(self):
"Check file integrity"

View file

@ -197,7 +197,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):
@ -467,7 +467,7 @@ class Color3DLUT(MultibandFilter):
def __repr__(self):
r = [
"{} from {}".format(self.__class__.__name__,
self.table.__class__.__name__),
self.table.__class__.__name__),
"size={:d}x{:d}x{:d}".format(*self.size),
"channels={:d}".format(self.channels),
]

View file

@ -72,7 +72,7 @@ class ImageFont(object):
try:
fullname = os.path.splitext(filename)[0] + ext
image = Image.open(fullname)
except:
except Exception:
pass
else:
if image and image.mode in ("1", "L"):
@ -203,7 +203,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=self.layout_engine if layout_engine is None else layout_engine
layout_engine=layout_engine or self.layout_engine
)

View file

@ -54,9 +54,10 @@ def grabclipboard():
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",
" write (the clipboard as JPEG picture) to theFile",
"end try",
"close access theFile"
]

View file

@ -380,9 +380,10 @@ def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)):
(width, height) tuple.
:param method: What resampling method to use. Default is
:py:attr:`PIL.Image.NEAREST`.
:param bleed: Remove a border around the outside of the image (from all
:param bleed: Remove a border around the outside of the image from all
four edges. The value is a decimal percentage (use 0.01 for
one percent). The default value is 0 (no border).
Cannot be greater than or equal to 0.5.
:param centering: Control the cropping position. Use (0.5, 0.5) for
center cropping (e.g. if cropping the width, take 50% off
of the left side, and therefore 50% off the right side).
@ -400,66 +401,53 @@ def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)):
# kevin@cazabon.com
# http://www.cazabon.com
# ensure inputs are valid
if not isinstance(centering, list):
centering = [centering[0], centering[1]]
# ensure centering is mutable
centering = list(centering)
if centering[0] > 1.0 or centering[0] < 0.0:
centering[0] = 0.50
if centering[1] > 1.0 or centering[1] < 0.0:
centering[1] = 0.50
if not 0.0 <= centering[0] <= 1.0:
centering[0] = 0.5
if not 0.0 <= centering[1] <= 1.0:
centering[1] = 0.5
if bleed > 0.49999 or bleed < 0.0:
if not 0.0 <= bleed < 0.5:
bleed = 0.0
# calculate the area to use for resizing and cropping, subtracting
# the 'bleed' around the edges
# number of pixels to trim off on Top and Bottom, Left and Right
bleedPixels = (
int((float(bleed) * float(image.size[0])) + 0.5),
int((float(bleed) * float(image.size[1])) + 0.5)
)
bleed_pixels = (bleed * image.size[0], bleed * image.size[1])
liveArea = (0, 0, image.size[0], image.size[1])
if bleed > 0.0:
liveArea = (
bleedPixels[0], bleedPixels[1], image.size[0] - bleedPixels[0] - 1,
image.size[1] - bleedPixels[1] - 1
)
live_size = (image.size[0] - bleed_pixels[0] * 2,
image.size[1] - bleed_pixels[1] * 2)
liveSize = (liveArea[2] - liveArea[0], liveArea[3] - liveArea[1])
# calculate the aspect ratio of the liveArea
liveAreaAspectRatio = float(liveSize[0])/float(liveSize[1])
# calculate the aspect ratio of the live_size
live_size_ratio = float(live_size[0]) / live_size[1]
# calculate the aspect ratio of the output image
aspectRatio = float(size[0]) / float(size[1])
output_ratio = float(size[0]) / size[1]
# figure out if the sides or top/bottom will be cropped off
if liveAreaAspectRatio >= aspectRatio:
# liveArea is wider than what's needed, crop the sides
cropWidth = int((aspectRatio * float(liveSize[1])) + 0.5)
cropHeight = liveSize[1]
if live_size_ratio >= output_ratio:
# live_size is wider than what's needed, crop the sides
crop_width = output_ratio * live_size[1]
crop_height = live_size[1]
else:
# liveArea is taller than what's needed, crop the top and bottom
cropWidth = liveSize[0]
cropHeight = int((float(liveSize[0])/aspectRatio) + 0.5)
# live_size is taller than what's needed, crop the top and bottom
crop_width = live_size[0]
crop_height = live_size[0] / output_ratio
# make the crop
leftSide = int(liveArea[0] + (float(liveSize[0]-cropWidth) * centering[0]))
if leftSide < 0:
leftSide = 0
topSide = int(liveArea[1] + (float(liveSize[1]-cropHeight) * centering[1]))
if topSide < 0:
topSide = 0
crop_left = bleed_pixels[0] + (live_size[0]-crop_width) * centering[0]
crop_top = bleed_pixels[1] + (live_size[1]-crop_height) * centering[1]
out = image.crop(
(leftSide, topSide, leftSide + cropWidth, topSide + cropHeight)
)
crop = (
crop_left, crop_top,
crop_left + crop_width, crop_top + crop_height
)
# resize the image and return it
return out.resize(size, method)
return image.resize(size, method, box=crop)
def flip(image):

View file

@ -140,7 +140,7 @@ def _toqclass_helper(im):
if py3:
im = str(im.toUtf8(), "utf-8")
else:
im = unicode(im.toUtf8(), "utf-8")
im = unicode(im.toUtf8(), "utf-8") # noqa: F821
if isPath(im):
im = Image.open(im)
@ -185,8 +185,8 @@ if qt_is_installed:
An PIL image wrapper for Qt. This is a subclass of PyQt's QImage
class.
:param im: A PIL Image object, or a file name (given either as Python
string or a PyQt string object).
:param im: A PIL Image object, or a file name (given either as
Python string or a PyQt string object).
"""
im_data = _toqclass_helper(im)
# must keep a reference, or Qt will crash!

View file

@ -17,6 +17,8 @@ from __future__ import print_function
from PIL import Image
import os
import sys
import subprocess
import tempfile
if sys.version_info.major >= 3:
from shlex import quote
@ -128,6 +130,21 @@ elif sys.platform == "darwin":
quote(file))
return command
def show_file(self, file, **options):
"""Display given file"""
fd, path = tempfile.mkstemp()
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)
os.remove(path)
return 1
register(MacViewer)
else:
@ -148,11 +165,23 @@ else:
format = "PNG"
options = {'compress_level': 1}
def get_command(self, file, **options):
command = self.get_command_ex(file, **options)[0]
return "(%s %s; rm -f %s)&" % (command, quote(file), quote(file))
def show_file(self, file, **options):
command, executable = self.get_command_ex(file, **options)
command = "(%s %s; rm -f %s)&" % (command, quote(file),
quote(file))
os.system(command)
"""Display given file"""
fd, path = tempfile.mkstemp()
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)
os.remove(path)
return 1
# implementations

View file

@ -26,15 +26,15 @@
#
import sys
from io import BytesIO
from . import Image
if sys.version_info.major > 2:
import tkinter
else:
import Tkinter as tkinter
from . import Image
from io import BytesIO
# --------------------------------------------------------------------
# Check for Tkinter interface hooks
@ -124,7 +124,7 @@ class PhotoImage(object):
self.__photo.name = None
try:
self.__photo.tk.call("image", "delete", name)
except:
except Exception:
pass # ignore internal errors
def __str__(self):
@ -244,7 +244,7 @@ class BitmapImage(object):
self.__photo.name = None
try:
self.__photo.tk.call("image", "delete", name)
except:
except Exception:
pass # ignore internal errors
def width(self):

View file

@ -181,14 +181,14 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
try:
fd = self.fp.fileno()
length = os.fstat(fd).st_size
except:
except Exception:
fd = -1
try:
pos = self.fp.tell()
self.fp.seek(0, 2)
length = self.fp.tell()
self.fp.seek(pos, 0)
except:
except Exception:
length = -1
self.tile = [('jpeg2k', (0, 0) + self.size, 0,
@ -232,6 +232,13 @@ def _save(im, fp, filename):
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])
):
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)
@ -243,7 +250,7 @@ def _save(im, fp, filename):
if hasattr(fp, "fileno"):
try:
fd = fp.fileno()
except:
except Exception:
fd = -1
im.encoderconfig = (

View file

@ -75,7 +75,7 @@ def APP(self, marker):
try:
jfif_unit = i8(s[7])
jfif_density = i16(s, 8), i16(s, 10)
except:
except Exception:
pass
else:
if jfif_unit == 1:
@ -107,7 +107,7 @@ def APP(self, marker):
# extract Adobe custom properties
try:
adobe_transform = i8(s[1])
except:
except Exception:
pass
else:
self.info["adobe_transform"] = adobe_transform
@ -441,7 +441,7 @@ def _fixup_dict(src_dict):
try:
if len(value) == 1 and not isinstance(value, dict):
return value[0]
except:
except Exception:
pass
return value
@ -512,7 +512,7 @@ def _getmp(self):
info = TiffImagePlugin.ImageFileDirectory_v2(head)
info.load(file_contents)
mp = dict(info)
except:
except Exception:
raise SyntaxError("malformed MP Index (unreadable directory)")
# it's an error not to have a number of images
try:
@ -578,7 +578,7 @@ RAWMODE = {
"YCbCr": "YCbCr",
}
zigzag_index = (0, 1, 5, 6, 14, 15, 27, 28,
zigzag_index = (0, 1, 5, 6, 14, 15, 27, 28, # noqa: E128
2, 4, 7, 13, 16, 26, 29, 42,
3, 8, 12, 17, 25, 30, 41, 43,
9, 11, 18, 24, 31, 40, 44, 53,

View file

@ -62,11 +62,12 @@ The tables format between im.quantization and quantization in presets differ in
You can convert the dict format to the preset format with the
`JpegImagePlugin.convert_dict_qtables(dict_qtables)` function.
Libjpeg ref.: https://web.archive.org/web/20120328125543/http://www.jpegcameras.com/libjpeg/libjpeg-3.html
Libjpeg ref.:
https://web.archive.org/web/20120328125543/http://www.jpegcameras.com/libjpeg/libjpeg-3.html
"""
presets = {
presets = { # noqa: E128
'web_low': {'subsampling': 2, # "4:2:0"
'quantization': [
[20, 16, 25, 39, 50, 46, 62, 68,

View file

@ -95,9 +95,17 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
self.frame = frame
def tell(self):
return self.frame
def _close__fp(self):
try:
if self.__fp != self.fp:
self.__fp.close()
except AttributeError:
pass
finally:
self.__fp = None
#
# --------------------------------------------------------------------

View file

@ -84,6 +84,15 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
def tell(self):
return self.__frame
def _close__fp(self):
try:
if self.__fp != self.fp:
self.__fp.close()
except AttributeError:
pass
finally:
self.__fp = None
# ---------------------------------------------------------------------
# Registry stuff

View file

@ -12,7 +12,7 @@ from ._binary import o8, o16be as o16b
__version__ = "1.0"
_Palm8BitColormapValues = (
_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),
(255, 153, 204), (255, 102, 204), (255, 51, 204), (255, 0, 204),

View file

@ -230,7 +230,7 @@ class PcfFontFile(FontFile.FontFile):
firstCol, lastCol = i16(fp.read(2)), i16(fp.read(2))
firstRow, lastRow = i16(fp.read(2)), i16(fp.read(2))
default = i16(fp.read(2))
i16(fp.read(2)) # default
nencoding = (lastCol - firstCol + 1) * (lastRow - firstRow + 1)

View file

@ -373,7 +373,7 @@ def pdf_repr(x):
elif isinstance(x, list):
return bytes(PdfArray(x))
elif ((py3 and isinstance(x, str)) or
(not py3 and isinstance(x, unicode))):
(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
@ -386,7 +386,8 @@ def pdf_repr(x):
class PdfParser:
"""Based on https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/PDF32000_2008.pdf
"""Based on
https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/PDF32000_2008.pdf
Supports PDF up to 1.4
"""
@ -863,7 +864,8 @@ class PdfParser:
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)|(\()|(\))")
re_lit_str_token = re.compile(
br"(\\[nrtbf()\\])|(\\[0-9]{1,3})|(\\(\r\n|\r|\n))|(\r\n|\r|\n)|(\()|(\))")
escaped_chars = {
b"n": b"\n",
b"r": b"\r",

View file

@ -296,6 +296,7 @@ class PngStream(ChunkStream):
self.im_mode = None
self.im_tile = None
self.im_palette = None
self.im_custom_mimetype = None
self.text_memory = 0
@ -340,7 +341,7 @@ class PngStream(ChunkStream):
self.im_size = i32(s), i32(s[4:])
try:
self.im_mode, self.im_rawmode = _MODES[(i8(s[8]), i8(s[9]))]
except:
except Exception:
pass
if i8(s[12]):
self.im_info["interlace"] = 1
@ -526,6 +527,20 @@ class PngStream(ChunkStream):
return s
# APNG chunks
def chunk_acTL(self, pos, length):
s = ImageFile._safe_read(self.fp, length)
self.im_custom_mimetype = 'image/apng'
return s
def chunk_fcTL(self, pos, length):
s = ImageFile._safe_read(self.fp, length)
return s
def chunk_fdAT(self, pos, length):
s = ImageFile._safe_read(self.fp, length)
return s
# --------------------------------------------------------------------
# PNG reader
@ -579,8 +594,9 @@ class PngImageFile(ImageFile.ImageFile):
self.mode = self.png.im_mode
self._size = self.png.im_size
self.info = self.png.im_info
self.text = self.png.im_text # experimental
self._text = None
self.tile = self.png.im_tile
self.custom_mimetype = self.png.im_custom_mimetype
if self.png.im_palette:
rawmode, data = self.png.im_palette
@ -588,6 +604,15 @@ class PngImageFile(ImageFile.ImageFile):
self.__idat = length # used by load_read()
@property
def text(self):
# experimental
if self._text is None:
# iTxt, tEXt and zTXt chunks may appear at the end of the file
# So load the file to ensure that they are read
self.load()
return self._text
def verify(self):
"Verify PNG file"
@ -600,6 +625,8 @@ class PngImageFile(ImageFile.ImageFile):
self.png.verify()
self.png.close()
if self._exclusive_fp:
self.fp.close()
self.fp = None
def load_prepare(self):
@ -638,7 +665,24 @@ class PngImageFile(ImageFile.ImageFile):
def load_end(self):
"internal: finished reading image data"
while True:
self.fp.read(4) # CRC
try:
cid, pos, length = self.png.read()
except (struct.error, SyntaxError):
break
if cid == b"IEND":
break
try:
self.png.call(cid, pos, length)
except UnicodeDecodeError:
break
except EOFError:
ImageFile._safe_read(self.fp, length)
self._text = self.png.im_text
self.png.close()
self.png = None
@ -866,6 +910,6 @@ def getchunks(im, **params):
Image.register_open(PngImageFile.format, PngImageFile, _accept)
Image.register_save(PngImageFile.format, _save)
Image.register_extension(PngImageFile.format, ".png")
Image.register_extensions(PngImageFile.format, [".png", ".apng"])
Image.register_mime(PngImageFile.format, "image/png")

View file

@ -92,7 +92,7 @@ class PsdImageFile(ImageFile.ImageFile):
# load resources
end = self.fp.tell() + size
while self.fp.tell() < end:
signature = read(4)
read(4) # signature
id = i16(read(2))
name = read(i8(read(1)))
if not (len(name) & 1):
@ -207,17 +207,13 @@ def _layerinfo(file):
mode = None # unknown
# skip over blend flags and extra information
filler = read(12)
read(12) # filler
name = ""
size = i32(read(4))
combined = 0
if size:
length = i32(read(4))
if length:
mask_y = i32(read(4))
mask_x = i32(read(4))
mask_h = i32(read(4)) - mask_y
mask_w = i32(read(4)) - mask_x
file.seek(length - 16, 1)
combined += length + 4

View file

@ -53,6 +53,8 @@ class PyAccess(object):
# Keep pointer to im object to prevent dereferencing.
self._im = img.im
if self._im.mode == "P":
self._palette = img.palette
# Debugging is polluting test traces, only useful here
# when hacking on PyAccess
@ -74,7 +76,18 @@ class PyAccess(object):
"""
if self.readonly:
raise ValueError('Attempt to putpixel a read only image')
(x, y) = self.check_xy(xy)
(x, y) = xy
if x < 0:
x = self.xsize + x
if y < 0:
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]:
# RGB or RGBA value for a P image
color = self._palette.getcolor(color)
return self.set_pixel(x, y, color)
def __getitem__(self, xy):
@ -88,8 +101,12 @@ class PyAccess(object):
:returns: a pixel value for single band images, a tuple of
pixel values for multiband images.
"""
(x, y) = self.check_xy(xy)
(x, y) = xy
if x < 0:
x = self.xsize + x
if y < 0:
y = self.ysize + y
(x, y) = self.check_xy((x, y))
return self.get_pixel(x, y)
putpixel = __setitem__
@ -205,7 +222,7 @@ class _PyAccessI16_L(PyAccess):
except TypeError:
color = min(color[0], 65535)
pixel.l = color & 0xFF
pixel.l = color & 0xFF # noqa: E741
pixel.r = color >> 8
@ -222,10 +239,10 @@ class _PyAccessI16_B(PyAccess):
pixel = self.pixels[y][x]
try:
color = min(color, 65535)
except:
except Exception:
color = min(color[0], 65535)
pixel.l = color >> 8
pixel.l = color >> 8 # noqa: E741
pixel.r = color & 0xFF

View file

@ -193,6 +193,15 @@ class SpiderImageFile(ImageFile.ImageFile):
from PIL import ImageTk
return ImageTk.PhotoImage(self.convert2byte(), palette=256)
def _close__fp(self):
try:
if self.__fp != self.fp:
self.__fp.close()
except AttributeError:
pass
finally:
self.__fp = None
# --------------------------------------------------------------------
# Image series
@ -210,7 +219,7 @@ def loadImageSeries(filelist=None):
continue
try:
im = Image.open(img).convert2byte()
except:
except Exception:
if not isSpiderImage(img):
print(img + " is not a Spider image file")
continue

View file

@ -14,6 +14,7 @@
# See the README file for information on usage and redistribution.
#
import sys
from . import ContainerIO
@ -30,11 +31,11 @@ class TarIO(ContainerIO.ContainerIO):
:param tarfile: Name of TAR file.
:param file: Name of member file.
"""
fh = open(tarfile, "rb")
self.fh = open(tarfile, "rb")
while True:
s = fh.read(512)
s = self.fh.read(512)
if len(s) != 512:
raise IOError("unexpected end of tar file")
@ -50,7 +51,21 @@ class TarIO(ContainerIO.ContainerIO):
if file == name:
break
fh.seek((size + 511) & (~511), 1)
self.fh.seek((size + 511) & (~511), 1)
# Open region
ContainerIO.ContainerIO.__init__(self, fh, fh.tell(), size)
ContainerIO.ContainerIO.__init__(self, self.fh, self.fh.tell(), size)
# Context manager support
def __enter__(self):
return self
def __exit__(self, *args):
self.close()
if sys.version_info.major >= 3:
def __del__(self):
self.close()
def close(self):
self.fh.close()

View file

@ -54,6 +54,7 @@ import os
import struct
import sys
import warnings
import distutils.version
from .TiffTags import TYPES
@ -284,6 +285,10 @@ def _limit_rational(val, max_val):
return n_d[::-1] if inv else n_d
def _libtiff_version():
return Image.core.libtiff_version.split("\n")[0].split("Version ")[1]
##
# Wrapper for TIFF IFDs.
@ -541,7 +546,7 @@ class ImageFileDirectory_v2(MutableMapping):
def _setitem(self, tag, value, legacy_api):
basetypes = (Number, bytes, str)
if not py3:
basetypes += unicode,
basetypes += unicode, # noqa: F821
info = TiffTags.lookup(tag)
values = [value] if isinstance(value, basetypes) else value
@ -552,26 +557,26 @@ class ImageFileDirectory_v2(MutableMapping):
else:
self.tagtype[tag] = 7
if all(isinstance(v, IFDRational) for v in values):
self.tagtype[tag] = 5
self.tagtype[tag] = TiffTags.RATIONAL
elif all(isinstance(v, int) for v in values):
if all(v < 2 ** 16 for v in values):
self.tagtype[tag] = 3
self.tagtype[tag] = TiffTags.SHORT
else:
self.tagtype[tag] = 4
self.tagtype[tag] = TiffTags.LONG
elif all(isinstance(v, float) for v in values):
self.tagtype[tag] = 12
self.tagtype[tag] = TiffTags.DOUBLE
else:
if py3:
if all(isinstance(v, str) for v in values):
self.tagtype[tag] = 2
self.tagtype[tag] = TiffTags.ASCII
else:
# Never treat data as binary by default on Python 2.
self.tagtype[tag] = 2
self.tagtype[tag] = TiffTags.ASCII
if self.tagtype[tag] == 7 and py3:
if self.tagtype[tag] == TiffTags.UNDEFINED and py3:
values = [value.encode("ascii", 'replace') if isinstance(
value, str) else value]
elif self.tagtype[tag] == 5:
elif self.tagtype[tag] == TiffTags.RATIONAL:
values = [float(v) if isinstance(v, int) else v
for v in values]
@ -587,14 +592,18 @@ class ImageFileDirectory_v2(MutableMapping):
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 [5, 10]: # rationals
if legacy_api and self.tagtype[tag] in [
TiffTags.RATIONAL,
TiffTags.SIGNED_RATIONAL
]: # rationals
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" % (
"Metadata Warning, tag %s had too many entries: "
"%s, expected 1" % (
tag, len(values)))
dest[tag] = values[0]
@ -622,13 +631,13 @@ class ImageFileDirectory_v2(MutableMapping):
from .TiffTags import TYPES
if func.__name__.startswith("load_"):
TYPES[idx] = func.__name__[5:].replace("_", " ")
_load_dispatch[idx] = size, func
_load_dispatch[idx] = size, func # noqa: F821
return func
return decorator
def _register_writer(idx):
def decorator(func):
_write_dispatch[idx] = func
_write_dispatch[idx] = func # noqa: F821
return func
return decorator
@ -637,19 +646,19 @@ class ImageFileDirectory_v2(MutableMapping):
idx, fmt, name = idx_fmt_name
TYPES[idx] = name
size = struct.calcsize("=" + fmt)
_load_dispatch[idx] = size, lambda self, data, legacy_api=True: (
_load_dispatch[idx] = size, lambda self, data, legacy_api=True: ( # noqa: F821
self._unpack("{}{}".format(len(data) // size, fmt), data))
_write_dispatch[idx] = lambda self, *values: (
_write_dispatch[idx] = lambda self, *values: ( # noqa: F821
b"".join(self._pack(fmt, value) for value in values))
list(map(_register_basic,
[(3, "H", "short"),
(4, "L", "long"),
(6, "b", "signed byte"),
(8, "h", "signed short"),
(9, "l", "signed long"),
(11, "f", "float"),
(12, "d", "double")]))
[(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")]))
@_register_loader(1, 1) # Basic type, except for the legacy API.
def load_byte(self, data, legacy_api=True):
@ -805,7 +814,10 @@ class ImageFileDirectory_v2(MutableMapping):
print("- value:", values)
# count is sum of lengths for string and arbitrary data
count = len(data) if typ in [2, 7] else len(values)
if typ in [TiffTags.ASCII, TiffTags.UNDEFINED]:
count = len(data)
else:
count = len(values)
# figure out if data fits into the entry
if len(data) <= 4:
entries.append((tag, typ, count, data.ljust(4, b"\0"), b""))
@ -1347,6 +1359,15 @@ class TiffImageFile(ImageFile.ImageFile):
palette = [o8(b // 256) for b in self.tag_v2[COLORMAP]]
self.palette = ImagePalette.raw("RGB;L", b"".join(palette))
def _close__fp(self):
try:
if self.__fp != self.fp:
self.__fp.close()
except AttributeError:
pass
finally:
self.__fp = None
#
# --------------------------------------------------------------------
@ -1412,7 +1433,7 @@ def _save(im, fp, filename):
ifd[key] = info.get(key)
try:
ifd.tagtype[key] = info.tagtype[key]
except:
except Exception:
pass # might not be an IFD, Might not have populated type
# additions written by Greg Couch, gregc@cgl.ucsf.edu
@ -1499,14 +1520,19 @@ def _save(im, fp, filename):
getattr(im, 'tag_v2', {}).items(),
legacy_ifd.items()):
# Libtiff can only process certain core items without adding
# them to the custom dictionary. It will segfault if it attempts
# to add a custom tag without the dictionary entry
#
# UNDONE -- add code for the custom dictionary
# them to the custom dictionary.
# Support for custom items has only been been added
# for int, float, unicode, string and byte values
if tag not in TiffTags.LIBTIFF_CORE:
continue
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
continue
if tag not in atts and tag not in blocklist:
if isinstance(value, str if py3 else unicode):
if isinstance(value, str if py3 else unicode): # noqa: F821
atts[tag] = value.encode('ascii', 'replace') + b"\0"
elif isinstance(value, IFDRational):
atts[tag] = float(value)
@ -1784,7 +1810,7 @@ class AppendingTiffWriter:
# local (not referenced with another offset)
self.rewriteLastShortToLong(offset)
self.f.seek(-10, os.SEEK_CUR)
self.writeShort(4) # rewrite the type to LONG
self.writeShort(TiffTags.LONG) # rewrite the type to LONG
self.f.seek(8, os.SEEK_CUR)
elif isShort:
self.rewriteLastShort(offset)

View file

@ -29,7 +29,10 @@ class TagInfo(namedtuple("_TagInfo", "value name type length enum")):
cls, value, name, type, length, enum or {})
def cvt_enum(self, value):
return self.enum.get(value, value)
# Using get will call hash(value), which can be expensive
# for some types (e.g. Fraction). Since self.enum is rarely
# used, it's usually better to test it first.
return self.enum.get(value, value) if self.enum else value
def lookup(tag):
@ -61,8 +64,12 @@ ASCII = 2
SHORT = 3
LONG = 4
RATIONAL = 5
SIGNED_BYTE = 6
UNDEFINED = 7
SIGNED_SHORT = 8
SIGNED_LONG = 9
SIGNED_RATIONAL = 10
FLOAT = 11
DOUBLE = 12
TAGS_V2 = {
@ -425,6 +432,7 @@ 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,

View file

@ -2,7 +2,7 @@ from . import Image, ImageFile
try:
from . import _webp
SUPPORTED = True
except ImportError as e:
except ImportError:
SUPPORTED = False
from io import BytesIO
@ -32,7 +32,8 @@ 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
@ -163,7 +164,7 @@ class WebPImageFile(ImageFile.ImageFile):
self.__loaded = self.__logical_frame
# Set tile
if self.fp:
if self.fp and self._exclusive_fp:
self.fp.close()
self.fp = BytesIO(data)
self.tile = [("raw", (0, 0) + self.size, 0, self.rawmode)]
@ -190,7 +191,19 @@ def _save_all(im, fp, filename):
_save(im, fp, filename)
return
background = encoderinfo.get("background", (0, 0, 0, 0))
background = (0, 0, 0, 0)
if "background" in encoderinfo:
background = encoderinfo["background"]
elif "background" in im.info:
background = im.info["background"]
if isinstance(background, int):
# GifImagePlugin stores a global color table index in
# 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]
background = (r, g, b, 0)
duration = im.encoderinfo.get("duration", 0)
loop = im.encoderinfo.get("loop", 0)
minimize_size = im.encoderinfo.get("minimize_size", False)
@ -255,7 +268,8 @@ def _save_all(im, fp, filename):
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())
or (ims.mode == 'P' and
'A' in ims.im.getpalettemode())
rawmode = 'RGBA' if alpha else 'RGB'
frame = ims.convert(rawmode)

View file

@ -11,10 +11,10 @@ if py3:
return isinstance(f, (bytes, str))
else:
def isStringType(t):
return isinstance(t, basestring)
return isinstance(t, basestring) # noqa: F821
def isPath(f):
return isinstance(f, basestring)
return isinstance(f, basestring) # noqa: F821
# Checks if an object is a string, and that it points to a directory.

View file

@ -1,2 +1,2 @@
# Master version for Pillow
__version__ = '5.3.0'
__version__ = '5.4.1'

View file

@ -51,7 +51,8 @@ features = {
"webp_anim": ("PIL._webp", 'HAVE_WEBPANIM'),
"webp_mux": ("PIL._webp", 'HAVE_WEBPMUX'),
"transp_webp": ("PIL._webp", "HAVE_TRANSPARENCY"),
"raqm": ("PIL._imagingft", "HAVE_RAQM")
"raqm": ("PIL._imagingft", "HAVE_RAQM"),
"libjpeg_turbo": ("PIL._imaging", "HAVE_LIBJPEGTURBO"),
}