update Python module for Linux.

pull/236/head
Apex Liu 2020-06-05 15:17:58 +08:00
parent bc494cac5c
commit 45ee30b95d
308 changed files with 30938 additions and 30145 deletions

View File

@ -32,7 +32,7 @@
<div class="footer">
<div class="container">
<p><a href="https://tp4a.com/" target="_blank">TELEPORT</a> | &copy;2015 - 2019,保留所有权利。</p>
<p><a href="https://tp4a.com/" target="_blank">TELEPORT</a> | &copy;2015 - 2020,保留所有权利。</p>
</div>
</div>

View File

@ -45,7 +45,7 @@
<div class="footer">
<div class="container">
<p><a href="https://tp4a.com/" target="_blank">TELEPORT</a> | &copy;2015 - 2019,保留所有权利。</p>
<p><a href="https://tp4a.com/" target="_blank">TELEPORT</a> | &copy;2015 - 2020,保留所有权利。</p>
</div>
</div>

View File

@ -32,7 +32,7 @@
<div class="footer">
<div class="container">
<p><a href="https://tp4a.com/" target="_blank">TELEPORT</a> | &copy;2015 - 2019,保留所有权利。</p>
<p><a href="https://tp4a.com/" target="_blank">TELEPORT</a> | &copy;2015 - 2020,保留所有权利。</p>
</div>
</div>

View File

@ -45,7 +45,7 @@
<div class="footer">
<div class="container">
<p><a href="https://tp4a.com/" target="_blank">TELEPORT</a> | &copy;2015 - 2019,保留所有权利。</p>
<p><a href="https://tp4a.com/" target="_blank">TELEPORT</a> | &copy;2015 - 2020,保留所有权利。</p>
</div>
</div>

Binary file not shown.

View File

@ -17,7 +17,6 @@
# See the README file for information on usage and redistribution.
#
from __future__ import print_function
from . import FontFile, Image
@ -85,8 +84,7 @@ def bdf_char(f):
class BdfFontFile(FontFile.FontFile):
def __init__(self, fp):
FontFile.FontFile.__init__(self)
super().__init__()
s = fp.readline()
if s[:13] != b"STARTFONT 2.1":

View File

@ -119,7 +119,7 @@ def decode_dxt3(data):
bits = struct.unpack_from("<8B", block)
color0, color1 = struct.unpack_from("<HH", block, 8)
code, = struct.unpack_from("<I", block, 12)
(code,) = struct.unpack_from("<I", block, 12)
r0, g0, b0 = unpack_565(color0)
r1, g1, b1 = unpack_565(color1)
@ -177,7 +177,7 @@ def decode_dxt5(data):
color0, color1 = struct.unpack_from("<HH", block, 8)
code, = struct.unpack_from("<I", block, 12)
(code,) = struct.unpack_from("<I", block, 12)
r0, g0, b0 = unpack_565(color0)
r1, g1, b1 = unpack_565(color1)
@ -255,19 +255,19 @@ class BlpImageFile(ImageFile.ImageFile):
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))
(self._blp_compression,) = struct.unpack("<i", self.fp.read(4))
self._blp_encoding, = struct.unpack("<b", self.fp.read(1))
self._blp_alpha_depth, = struct.unpack("<b", self.fp.read(1))
self._blp_alpha_encoding, = struct.unpack("<b", self.fp.read(1))
self._blp_mips, = struct.unpack("<b", self.fp.read(1))
(self._blp_encoding,) = struct.unpack("<b", self.fp.read(1))
(self._blp_alpha_depth,) = struct.unpack("<b", self.fp.read(1))
(self._blp_alpha_encoding,) = struct.unpack("<b", self.fp.read(1))
(self._blp_mips,) = struct.unpack("<b", self.fp.read(1))
self._size = struct.unpack("<II", self.fp.read(8))
if self.magic == b"BLP1":
# Only present for BLP1
self._blp_encoding, = struct.unpack("<i", self.fp.read(4))
self._blp_subtype, = struct.unpack("<i", self.fp.read(4))
(self._blp_encoding,) = struct.unpack("<i", self.fp.read(4))
(self._blp_subtype,) = struct.unpack("<i", self.fp.read(4))
self._blp_offsets = struct.unpack("<16I", self.fp.read(16 * 4))
self._blp_lengths = struct.unpack("<16I", self.fp.read(16 * 4))
@ -283,7 +283,7 @@ class _BLPBaseDecoder(ImageFile.PyDecoder):
self._read_blp_header()
self._load()
except struct.error:
raise IOError("Truncated Blp file")
raise OSError("Truncated Blp file")
return 0, 0
def _read_palette(self):
@ -297,19 +297,19 @@ class _BLPBaseDecoder(ImageFile.PyDecoder):
return ret
def _read_blp_header(self):
self._blp_compression, = struct.unpack("<i", self.fd.read(4))
(self._blp_compression,) = struct.unpack("<i", self.fd.read(4))
self._blp_encoding, = struct.unpack("<b", self.fd.read(1))
self._blp_alpha_depth, = struct.unpack("<b", self.fd.read(1))
self._blp_alpha_encoding, = struct.unpack("<b", self.fd.read(1))
self._blp_mips, = struct.unpack("<b", self.fd.read(1))
(self._blp_encoding,) = struct.unpack("<b", self.fd.read(1))
(self._blp_alpha_depth,) = struct.unpack("<b", self.fd.read(1))
(self._blp_alpha_encoding,) = struct.unpack("<b", self.fd.read(1))
(self._blp_mips,) = struct.unpack("<b", self.fd.read(1))
self.size = struct.unpack("<II", self.fd.read(8))
if self.magic == b"BLP1":
# Only present for BLP1
self._blp_encoding, = struct.unpack("<i", self.fd.read(4))
self._blp_subtype, = struct.unpack("<i", self.fd.read(4))
(self._blp_encoding,) = struct.unpack("<i", self.fd.read(4))
(self._blp_subtype,) = struct.unpack("<i", self.fd.read(4))
self._blp_offsets = struct.unpack("<16I", self.fd.read(16 * 4))
self._blp_lengths = struct.unpack("<16I", self.fd.read(16 * 4))
@ -327,7 +327,7 @@ class BLP1Decoder(_BLPBaseDecoder):
_data = BytesIO(self.fd.read(self._blp_lengths[0]))
while True:
try:
offset, = struct.unpack("<B", _data.read(1))
(offset,) = struct.unpack("<B", _data.read(1))
except struct.error:
break
b, g, r, a = palette[offset]
@ -346,7 +346,7 @@ class BLP1Decoder(_BLPBaseDecoder):
def _decode_jpeg_stream(self):
from PIL.JpegImagePlugin import JpegImageFile
jpeg_header_size, = struct.unpack("<I", self.fd.read(4))
(jpeg_header_size,) = struct.unpack("<I", self.fd.read(4))
jpeg_header = self.fd.read(jpeg_header_size)
self.fd.read(self._blp_offsets[0] - self.fd.tell()) # What IS this?
data = self.fd.read(self._blp_lengths[0])
@ -372,7 +372,7 @@ class BLP2Decoder(_BLPBaseDecoder):
_data = BytesIO(self.fd.read(self._blp_lengths[0]))
while True:
try:
offset, = struct.unpack("<B", _data.read(1))
(offset,) = struct.unpack("<B", _data.read(1))
except struct.error:
break
b, g, r, a = palette[offset]

View File

@ -27,10 +27,6 @@
from . import Image, ImageFile, ImagePalette
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.
__version__ = "0.7"
#
# --------------------------------------------------------------------
# Read BMP file
@ -148,7 +144,7 @@ class BmpImageFile(ImageFile.ImageFile):
file_info["a_mask"],
)
else:
raise IOError("Unsupported BMP header type (%d)" % file_info["header_size"])
raise OSError("Unsupported BMP header type (%d)" % file_info["header_size"])
# ------------------ Special case : header is reported 40, which
# ---------------------- is shorter than real size for bpp >= 16
@ -163,12 +159,12 @@ class BmpImageFile(ImageFile.ImageFile):
# ------------------------------- Check abnormal values for DOS attacks
if file_info["width"] * file_info["height"] > 2 ** 31:
raise IOError("Unsupported BMP Size: (%dx%d)" % self.size)
raise OSError("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))
if self.mode is None:
raise IOError("Unsupported BMP pixel depth (%d)" % file_info["bits"])
raise OSError("Unsupported BMP pixel depth (%d)" % file_info["bits"])
# ---------------- Process BMP with Bitfields compression (not palette)
if file_info["compression"] == self.BITFIELDS:
@ -206,21 +202,21 @@ class BmpImageFile(ImageFile.ImageFile):
):
raw_mode = MASK_MODES[(file_info["bits"], file_info["rgb_mask"])]
else:
raise IOError("Unsupported BMP bitfields layout")
raise OSError("Unsupported BMP bitfields layout")
else:
raise IOError("Unsupported BMP bitfields layout")
raise OSError("Unsupported BMP bitfields layout")
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 OSError("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"])
raise OSError("Unsupported BMP Palette size (%d)" % file_info["colors"])
else:
padding = file_info["palette_padding"]
palette = read(padding * file_info["colors"])
@ -309,7 +305,7 @@ def _save(im, fp, filename, bitmap_header=True):
try:
rawmode, bits, colors = SAVE[im.mode]
except KeyError:
raise IOError("cannot write mode %s as BMP" % im.mode)
raise OSError("cannot write mode %s as BMP" % im.mode)
info = im.encoderinfo
@ -325,12 +321,15 @@ def _save(im, fp, filename, bitmap_header=True):
# bitmap header
if bitmap_header:
offset = 14 + header + colors * 4
file_size = offset + image
if file_size > 2 ** 32 - 1:
raise ValueError("File size is too large for the BMP format")
fp.write(
b"BM"
+ o32(offset + image) # file type (magic)
+ o32(0) # file size
+ o32(offset) # reserved
) # image data offset
b"BM" # file type (magic)
+ o32(file_size) # file size
+ o32(0) # reserved
+ o32(offset) # image data offset
)
# bitmap info header
fp.write(

View File

@ -60,7 +60,7 @@ class BufrStubImageFile(ImageFile.StubImageFile):
def _save(im, fp, filename):
if _handler is None or not hasattr("_handler", "save"):
raise IOError("BUFR save handler not installed")
raise OSError("BUFR save handler not installed")
_handler.save(im, fp, filename)

View File

@ -21,7 +21,7 @@
import io
class ContainerIO(object):
class ContainerIO:
def __init__(self, file, offset, length):
"""
Create file object.
@ -82,7 +82,7 @@ class ContainerIO(object):
else:
n = self.length - self.pos
if not n: # EOF
return ""
return b"" if "b" in self.fh.mode else ""
self.pos = self.pos + n
return self.fh.read(n)
@ -92,13 +92,14 @@ class ContainerIO(object):
:returns: An 8-bit string.
"""
s = ""
s = b"" if "b" in self.fh.mode else ""
newline_character = b"\n" if "b" in self.fh.mode else "\n"
while True:
c = self.read(1)
if not c:
break
s = s + c
if c == "\n":
if c == newline_character:
break
return s

View File

@ -15,16 +15,9 @@
#
# See the README file for information on usage and redistribution.
#
from __future__ import print_function
from . import BmpImagePlugin, Image
from ._binary import i8, i16le as i16, i32le as i32
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.1"
#
# --------------------------------------------------------------------

View File

@ -25,10 +25,6 @@ from . import Image
from ._binary import i32le as i32
from .PcxImagePlugin import PcxImageFile
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.2"
MAGIC = 0x3ADE68B1 # QUIZ: what's this value, then?

View File

@ -106,10 +106,10 @@ class DdsImageFile(ImageFile.ImageFile):
def _open(self):
magic, header_size = struct.unpack("<II", self.fp.read(8))
if header_size != 124:
raise IOError("Unsupported header size %r" % (header_size))
raise OSError("Unsupported header size %r" % (header_size))
header_bytes = self.fp.read(header_size - 4)
if len(header_bytes) != 120:
raise IOError("Incomplete header: %s bytes" % len(header_bytes))
raise OSError("Incomplete header: %s bytes" % len(header_bytes))
header = BytesIO(header_bytes)
flags, height, width = struct.unpack("<3I", header.read(12))
@ -122,7 +122,7 @@ class DdsImageFile(ImageFile.ImageFile):
# pixel format
pfsize, pfflags = struct.unpack("<2I", header.read(8))
fourcc = header.read(4)
bitcount, = struct.unpack("<I", header.read(4))
(bitcount,) = struct.unpack("<I", header.read(4))
masks = struct.unpack("<4I", header.read(16))
if pfflags & 0x40:
# DDPF_RGB - Texture contains uncompressed RGB data
@ -155,7 +155,7 @@ class DdsImageFile(ImageFile.ImageFile):
n = 7
elif dxgi_format == DXGI_FORMAT_BC7_UNORM_SRGB:
self.pixel_format = "BC7"
self.im_info["gamma"] = 1 / 2.2
self.info["gamma"] = 1 / 2.2
n = 7
else:
raise NotImplementedError(

View File

@ -23,15 +23,13 @@
import io
import os
import re
import subprocess
import sys
import tempfile
from . import Image, ImageFile
from ._binary import i32le as i32
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.5"
#
# --------------------------------------------------------------------
@ -42,15 +40,8 @@ gs_windows_binary = None
if sys.platform.startswith("win"):
import shutil
if hasattr(shutil, "which"):
which = shutil.which
else:
# Python 2
import distutils.spawn
which = distutils.spawn.find_executable
for binary in ("gswin32c", "gswin64c", "gs"):
if which(binary) is not None:
if shutil.which(binary) is not None:
gs_windows_binary = binary
break
else:
@ -61,11 +52,8 @@ def has_ghostscript():
if gs_windows_binary:
return True
if not sys.platform.startswith("win"):
import subprocess
try:
with open(os.devnull, "wb") as devnull:
subprocess.check_call(["gs", "--version"], stdout=devnull)
subprocess.check_call(["gs", "--version"], stdout=subprocess.DEVNULL)
return True
except OSError:
# No Ghostscript
@ -87,13 +75,10 @@ def Ghostscript(tile, size, fp, scale=1):
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])),
72.0 * size[0] / (bbox[2] - bbox[0]),
72.0 * size[1] / (bbox[3] - bbox[1]),
)
import subprocess
import tempfile
out_fd, outfile = tempfile.mkstemp()
os.close(out_fd)
@ -146,7 +131,7 @@ def Ghostscript(tile, size, fp, scale=1):
if gs_windows_binary is not None:
if not gs_windows_binary:
raise WindowsError("Unable to locate Ghostscript on paths")
raise OSError("Unable to locate Ghostscript on paths")
command[0] = gs_windows_binary
# push data through Ghostscript
@ -156,8 +141,8 @@ def Ghostscript(tile, size, fp, scale=1):
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
subprocess.check_call(command, startupinfo=startupinfo)
im = Image.open(outfile)
im.load()
out_im = Image.open(outfile)
out_im.load()
finally:
try:
os.unlink(outfile)
@ -166,10 +151,12 @@ def Ghostscript(tile, size, fp, scale=1):
except OSError:
pass
return im.im.copy()
im = out_im.im.copy()
out_im.close()
return im
class PSFile(object):
class PSFile:
"""
Wrapper for bytesio object that treats either CR or LF as end of line.
"""
@ -279,7 +266,7 @@ class EpsImageFile(ImageFile.ImageFile):
# tools mistakenly put in the Comments section
pass
else:
raise IOError("bad EPS header")
raise OSError("bad EPS header")
s_raw = fp.readline()
s = s_raw.strip("\r\n")
@ -314,7 +301,7 @@ class EpsImageFile(ImageFile.ImageFile):
break
if not box:
raise IOError("cannot determine EPS bounding box")
raise OSError("cannot determine EPS bounding box")
def _find_offset(self, fp):
@ -378,9 +365,8 @@ def _save(im, fp, filename, eps=1):
base_fp = fp
wrapped_fp = False
if fp != sys.stdout:
if sys.version_info.major > 2:
fp = io.TextIOWrapper(fp, encoding="latin-1")
wrapped_fp = True
fp = io.TextIOWrapper(fp, encoding="latin-1")
wrapped_fp = True
try:
if eps:

View File

@ -152,6 +152,12 @@ TAGS = {
0x9290: "SubsecTime",
0x9291: "SubsecTimeOriginal",
0x9292: "SubsecTimeDigitized",
0x9400: "AmbientTemperature",
0x9401: "Humidity",
0x9402: "Pressure",
0x9403: "WaterDepth",
0x9404: "Acceleration",
0x9405: "CameraElevationAngle",
0x9C9B: "XPTitle",
0x9C9C: "XPComment",
0x9C9D: "XPAuthor",

View File

@ -63,7 +63,7 @@ class FITSStubImageFile(ImageFile.StubImageFile):
def _save(im, fp, filename):
if _handler is None or not hasattr("_handler", "save"):
raise IOError("FITS save handler not installed")
raise OSError("FITS save handler not installed")
_handler.save(im, fp, filename)

View File

@ -19,11 +19,6 @@
from . import Image, ImageFile, ImagePalette
from ._binary import i8, i16le as i16, i32le as i32, o8
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.2"
#
# decoder

View File

@ -14,7 +14,6 @@
# See the README file for information on usage and redistribution.
#
from __future__ import print_function
import os
@ -35,7 +34,7 @@ def puti16(fp, values):
# Base class for raster font file handlers.
class FontFile(object):
class FontFile:
bitmap = None

View File

@ -14,18 +14,11 @@
#
# See the README file for information on usage and redistribution.
#
from __future__ import print_function
import olefile
from . import Image, ImageFile
from ._binary import i8, i32le as i32
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.1"
# we map from colour field tuples to (mode, rawmode) descriptors
MODES = {
# opacity
@ -66,7 +59,7 @@ class FpxImageFile(ImageFile.ImageFile):
try:
self.ole = olefile.OleFileIO(self.fp)
except IOError:
except OSError:
raise SyntaxError("not an FPX file; invalid OLE file")
if self.ole.root.clsid != "56616700-C154-11CE-8553-00AA00A1F95B":
@ -104,7 +97,10 @@ class FpxImageFile(ImageFile.ImageFile):
s = prop[0x2000002 | id]
colors = []
for i in range(i32(s, 4)):
bands = i32(s, 4)
if bands > 4:
raise IOError("Invalid number of bands")
for i in range(bands):
# note: for now, we ignore the "uncalibrated" flag
colors.append(i32(s, 8 + i * 4) & 0x7FFFFFFF)
@ -145,7 +141,7 @@ class FpxImageFile(ImageFile.ImageFile):
length = i32(s, 32)
if size != self.size:
raise IOError("subimage mismatch")
raise OSError("subimage mismatch")
# get tile descriptors
fp.seek(28 + offset)
@ -218,7 +214,7 @@ class FpxImageFile(ImageFile.ImageFile):
self.tile_prefix = self.jpeg[jpeg_tables]
else:
raise IOError("unknown/invalid compression")
raise OSError("unknown/invalid compression")
x = x + xtile
if x >= xsize:

View File

@ -79,7 +79,7 @@ class FtexImageFile(ImageFile.ImageFile):
format, where = struct.unpack("<2i", self.fp.read(8))
self.fp.seek(where)
mipmap_size, = struct.unpack("<i", self.fp.read(4))
(mipmap_size,) = struct.unpack("<i", self.fp.read(4))
data = self.fp.read(mipmap_size)

View File

@ -14,7 +14,7 @@
# See the README file for information on usage and redistribution.
#
#
# See https://github.com/GNOME/gimp/blob/master/devel-docs/gbr.txt for
# See https://github.com/GNOME/gimp/blob/mainline/devel-docs/gbr.txt for
# format documentation.
#
# This code Interprets version 1 and 2 .gbr files.

View File

@ -23,14 +23,9 @@
# purposes only.
from . import ImageFile, ImagePalette
from . import ImageFile, ImagePalette, UnidentifiedImageError
from ._binary import i8, i16be as i16, i32be as i32
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.1"
##
# Image plugin for the GD uncompressed format. Note that this format
# is not supported by the standard <b>Image.open</b> function. To use
@ -87,4 +82,4 @@ def open(fp, mode="r"):
try:
return GdImageFile(fp)
except SyntaxError:
raise IOError("cannot identify this image file")
raise UnidentifiedImageError("cannot identify this image file")

View File

@ -25,15 +25,13 @@
#
import itertools
import math
import os
import subprocess
from . import Image, ImageChops, ImageFile, ImagePalette, ImageSequence
from ._binary import i8, i16le as i16, o8, o16le as o16
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.9"
# --------------------------------------------------------------------
# Identify/read GIF files
@ -572,8 +570,11 @@ def _write_local_header(fp, im, offset, flags):
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]
comment = im.encoderinfo["comment"]
if isinstance(comment, str):
comment = comment.encode()
for i in range(0, len(comment), 255):
subblock = comment[i : i + 255]
fp.write(o8(len(subblock)) + subblock)
fp.write(o8(0))
if "loop" in im.encoderinfo:
@ -617,42 +618,44 @@ def _save_netpbm(im, fp, filename):
# If you need real GIF compression and/or RGB quantization, you
# can use the external NETPBM/PBMPLUS utilities. See comments
# below for information on how to enable this.
import os
from subprocess import Popen, check_call, PIPE, CalledProcessError
tempfile = im._dump()
with open(filename, "wb") as f:
if im.mode != "RGB":
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:
quant_proc = Popen(quant_cmd, stdout=PIPE, stderr=devnull)
togif_proc = Popen(
togif_cmd, stdin=quant_proc.stdout, stdout=f, stderr=devnull
try:
with open(filename, "wb") as f:
if im.mode != "RGB":
subprocess.check_call(
["ppmtogif", tempfile], stdout=f, stderr=subprocess.DEVNULL
)
else:
# Pipe ppmquant output into ppmtogif
# "ppmquant 256 %s | ppmtogif > %s" % (tempfile, filename)
quant_cmd = ["ppmquant", "256", tempfile]
togif_cmd = ["ppmtogif"]
quant_proc = subprocess.Popen(
quant_cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL
)
togif_proc = subprocess.Popen(
togif_cmd,
stdin=quant_proc.stdout,
stdout=f,
stderr=subprocess.DEVNULL,
)
# Allow ppmquant to receive SIGPIPE if ppmtogif exits
quant_proc.stdout.close()
# Allow ppmquant to receive SIGPIPE if ppmtogif exits
quant_proc.stdout.close()
retcode = quant_proc.wait()
if retcode:
raise CalledProcessError(retcode, quant_cmd)
retcode = quant_proc.wait()
if retcode:
raise subprocess.CalledProcessError(retcode, quant_cmd)
retcode = togif_proc.wait()
if retcode:
raise CalledProcessError(retcode, togif_cmd)
try:
os.unlink(tempfile)
except OSError:
pass
retcode = togif_proc.wait()
if retcode:
raise subprocess.CalledProcessError(retcode, togif_cmd)
finally:
try:
os.unlink(tempfile)
except OSError:
pass
# Force optimization so that we can test performance against
@ -699,14 +702,12 @@ def _get_optimize(im, info):
def _get_color_table_size(palette_bytes):
# calculate the palette size for the header
import math
if not palette_bytes:
return 0
elif len(palette_bytes) < 9:
return 1
else:
return int(math.ceil(math.log(len(palette_bytes) // 3, 2))) - 1
return math.ceil(math.log(len(palette_bytes) // 3, 2)) - 1
def _get_header_palette(palette_bytes):
@ -853,7 +854,7 @@ def getdata(im, offset=(0, 0), **params):
"""
class Collector(object):
class Collector:
data = []
def write(self, data):

View File

@ -60,7 +60,7 @@ def sphere_decreasing(middle, pos):
SEGMENTS = [linear, curved, sine, sphere_increasing, sphere_decreasing]
class GradientFile(object):
class GradientFile:
gradient = None
@ -73,7 +73,7 @@ class GradientFile(object):
for i in range(entries):
x = i / float(entries - 1)
x = i / (entries - 1)
while x1 < x:
ix += 1
@ -132,7 +132,7 @@ class GimpGradientFile(GradientFile):
cspace = int(s[12])
if cspace != 0:
raise IOError("cannot handle HSV colour space")
raise OSError("cannot handle HSV colour space")
gradient.append((x0, x1, xm, rgb0, rgb1, segment))

View File

@ -22,7 +22,7 @@ from ._binary import o8
# File handler for GIMP's palette format.
class GimpPaletteFile(object):
class GimpPaletteFile:
rawmode = "RGB"

View File

@ -61,7 +61,7 @@ class GribStubImageFile(ImageFile.StubImageFile):
def _save(im, fp, filename):
if _handler is None or not hasattr("_handler", "save"):
raise IOError("GRIB save handler not installed")
raise OSError("GRIB save handler not installed")
_handler.save(im, fp, filename)

View File

@ -60,7 +60,7 @@ class HDF5StubImageFile(ImageFile.StubImageFile):
def _save(im, fp, filename):
if _handler is None or not hasattr("_handler", "save"):
raise IOError("HDF5 save handler not installed")
raise OSError("HDF5 save handler not installed")
_handler.save(im, fp, filename)

View File

@ -19,6 +19,7 @@ import io
import os
import shutil
import struct
import subprocess
import sys
import tempfile
@ -128,7 +129,7 @@ def read_png_or_jpeg2000(fobj, start_length, size):
raise ValueError("Unsupported icon subimage format")
class IcnsFile(object):
class IcnsFile:
SIZES = {
(512, 512, 2): [(b"ic10", read_png_or_jpeg2000)],
@ -313,41 +314,40 @@ 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", [])}
last_w = None
second_path = None
for w in [16, 32, 128, 256, 512]:
prefix = "icon_{}x{}".format(w, w)
with tempfile.TemporaryDirectory(".iconset") as 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)
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)
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))
im_w2.save(second_path)
last_w = w * 2
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
# iconutil -c icns -o {} {}
from subprocess import Popen, PIPE, CalledProcessError
# iconutil -c icns -o {} {}
convert_cmd = ["iconutil", "-c", "icns", "-o", filename, iconset]
with open(os.devnull, "wb") as devnull:
convert_proc = Popen(convert_cmd, stdout=PIPE, stderr=devnull)
convert_cmd = ["iconutil", "-c", "icns", "-o", filename, iconset]
convert_proc = subprocess.Popen(
convert_cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL
)
convert_proc.stdout.close()
convert_proc.stdout.close()
retcode = convert_proc.wait()
retcode = convert_proc.wait()
# remove the temporary files
shutil.rmtree(iconset)
if retcode:
raise CalledProcessError(retcode, convert_cmd)
if retcode:
raise subprocess.CalledProcessError(retcode, convert_cmd)
Image.register_open(IcnsImageFile.format, IcnsImageFile, lambda x: x[:4] == b"icns")
@ -365,13 +365,12 @@ if __name__ == "__main__":
print("Syntax: python IcnsImagePlugin.py [file]")
sys.exit()
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 = Image.open(sys.argv[1])
im.save("out.png")
if sys.platform == "windows":
os.startfile("out.png")
with open(sys.argv[1], "rb") as fp:
imf = IcnsImageFile(fp)
for size in imf.info["sizes"]:
imf.size = size
imf.save("out-%s-%s-%s.png" % size)
with Image.open(sys.argv[1]) as im:
im.save("out.png")
if sys.platform == "windows":
os.startfile("out.png")

View File

@ -30,10 +30,6 @@ from math import ceil, log
from . import BmpImagePlugin, Image, ImageFile, PngImagePlugin
from ._binary import i8, i16le as i16, i32le as i32
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.1"
#
# --------------------------------------------------------------------
@ -67,8 +63,9 @@ def _save(im, fp, filename):
fp.write(struct.pack("<H", 32)) # wBitCount(2)
image_io = BytesIO()
# TODO: invent a more convenient method for proportional scalings
tmp = im.copy()
tmp.thumbnail(size, Image.LANCZOS)
tmp.thumbnail(size, Image.LANCZOS, reducing_gap=None)
tmp.save(image_io, "png")
image_io.seek(0)
image_bytes = image_io.read()
@ -86,7 +83,7 @@ def _accept(prefix):
return prefix[:4] == _MAGIC
class IcoFile(object):
class IcoFile:
def __init__(self, buf):
"""
Parse image from file-like object containing ico file data

View File

@ -26,16 +26,12 @@
#
import os
import re
from . import Image, ImageFile, ImagePalette
from ._binary import i8
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.7"
# --------------------------------------------------------------------
# Standard tags
@ -352,7 +348,14 @@ def _save(im, fp, filename):
fp.write(("Image type: %s image\r\n" % image_type).encode("ascii"))
if filename:
fp.write(("Name: %s\r\n" % filename).encode("ascii"))
# Each line must be 100 characters or less,
# or: SyntaxError("not an IM file")
# 8 characters are used for "Name: " and "\r\n"
# Keep just the filename, ditch the potentially overlong path
name, ext = os.path.splitext(os.path.basename(filename))
name = "".join([name[: 92 - len(ext)], ext])
fp.write(("Name: %s\r\n" % name).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"]:

View File

@ -25,6 +25,7 @@
#
import atexit
import builtins
import io
import logging
import math
@ -32,33 +33,41 @@ import numbers
import os
import struct
import sys
import tempfile
import warnings
from collections.abc import Callable, MutableMapping
from pathlib import Path
# VERSION was removed in Pillow 6.0.0.
# PILLOW_VERSION is deprecated and will be removed in Pillow 7.0.0.
# PILLOW_VERSION is deprecated and will be removed in a future release.
# Use __version__ instead.
from . import PILLOW_VERSION, ImageMode, TiffTags, __version__, _plugins
from . import (
ImageMode,
TiffTags,
UnidentifiedImageError,
__version__,
_plugins,
_raise_version_warning,
)
from ._binary import i8, i32le
from ._util import deferred_error, isPath, isStringType, py3
from ._util import deferred_error, isPath
try:
import builtins
except ImportError:
import __builtin__
if sys.version_info >= (3, 7):
builtins = __builtin__
def __getattr__(name):
if name == "PILLOW_VERSION":
_raise_version_warning()
return __version__
raise AttributeError("module '{}' has no attribute '{}'".format(__name__, name))
try:
# Python 3
from collections.abc import Callable, MutableMapping
except ImportError:
# Python 2.7
from collections import Callable, MutableMapping
else:
from . import PILLOW_VERSION
# Silence warning
assert PILLOW_VERSION
# Silence warning
assert PILLOW_VERSION
logger = logging.getLogger(__name__)
@ -71,12 +80,6 @@ class DecompressionBombError(Exception):
pass
class _imaging_not_installed(object):
# module placeholder
def __getattr__(self, id):
raise ImportError("The _imaging C module is not installed")
# Limit to around a quarter gigabyte for a 24 bit (3 bpp) image
MAX_IMAGE_PIXELS = int(1024 * 1024 * 1024 // 4 // 3)
@ -97,7 +100,7 @@ try:
)
except ImportError as v:
core = _imaging_not_installed()
core = deferred_error(ImportError("The _imaging C module is not installed."))
# Explanations for ways that we know we might have an import error
if str(v).startswith("Module use of python"):
# The _imaging C module is present, but not compiled for
@ -109,22 +112,6 @@ except ImportError as v:
)
elif str(v).startswith("The _imaging extension"):
warnings.warn(str(v), RuntimeWarning)
elif "Symbol not found: _PyUnicodeUCS2_" in str(v):
# should match _PyUnicodeUCS2_FromString and
# _PyUnicodeUCS2_AsLatin1String
warnings.warn(
"The _imaging extension was built for Python with UCS2 support; "
"recompile Pillow or build Python --without-wide-unicode. ",
RuntimeWarning,
)
elif "Symbol not found: _PyUnicodeUCS4_" in str(v):
# should match _PyUnicodeUCS4_FromString and
# _PyUnicodeUCS4_AsLatin1String
warnings.warn(
"The _imaging extension was built for Python with UCS4 support; "
"recompile Pillow or build Python --with-wide-unicode. ",
RuntimeWarning,
)
# Fail here anyway. Don't let people run with a mostly broken Pillow.
# see docs/porting.rst
raise
@ -137,18 +124,6 @@ try:
except ImportError:
cffi = None
try:
from pathlib import Path
HAS_PATHLIB = True
except ImportError:
try:
from pathlib2 import Path
HAS_PATHLIB = True
except ImportError:
HAS_PATHLIB = False
def isImageType(t):
"""
@ -193,6 +168,9 @@ HAMMING = 5
BICUBIC = CUBIC = 3
LANCZOS = ANTIALIAS = 1
_filters_support = {BOX: 0.5, BILINEAR: 1.0, HAMMING: 1.0, BICUBIC: 2.0, LANCZOS: 3.0}
# dithers
NEAREST = NONE = 0
ORDERED = 1 # Not yet implemented
@ -447,15 +425,17 @@ def _getdecoder(mode, decoder_name, args, extra=()):
try:
decoder = DECODERS[decoder_name]
return decoder(mode, *args + extra)
except KeyError:
pass
else:
return decoder(mode, *args + extra)
try:
# get decoder
decoder = getattr(core, decoder_name + "_decoder")
return decoder(mode, *args + extra)
except AttributeError:
raise IOError("decoder %s not available" % decoder_name)
raise OSError("decoder %s not available" % decoder_name)
return decoder(mode, *args + extra)
def _getencoder(mode, encoder_name, args, extra=()):
@ -468,15 +448,17 @@ def _getencoder(mode, encoder_name, args, extra=()):
try:
encoder = ENCODERS[encoder_name]
return encoder(mode, *args + extra)
except KeyError:
pass
else:
return encoder(mode, *args + extra)
try:
# get encoder
encoder = getattr(core, encoder_name + "_encoder")
return encoder(mode, *args + extra)
except AttributeError:
raise IOError("encoder %s not available" % encoder_name)
raise OSError("encoder %s not available" % encoder_name)
return encoder(mode, *args + extra)
# --------------------------------------------------------------------
@ -487,7 +469,7 @@ def coerce_e(value):
return value if isinstance(value, _E) else _E(value)
class _E(object):
class _E:
def __init__(self, data):
self.data = data
@ -528,7 +510,7 @@ def _getscaleoffset(expr):
# Implementation wrapper
class Image(object):
class Image:
"""
This class represents an image object. To create
:py:class:`~PIL.Image.Image` objects, use the appropriate factory
@ -624,11 +606,6 @@ class Image(object):
# object is gone.
self.im = deferred_error(ValueError("Operation on closed image"))
if sys.version_info.major >= 3:
def __del__(self):
self.__exit__()
def _copy(self):
self.load()
self.im = self.im.copy()
@ -642,8 +619,6 @@ class Image(object):
self.load()
def _dump(self, file=None, format=None, **options):
import tempfile
suffix = ""
if format:
suffix = "." + format
@ -677,10 +652,6 @@ class Image(object):
and self.tobytes() == other.tobytes()
)
def __ne__(self, other):
eq = self == other
return not eq
def __repr__(self):
return "<%s.%s image mode=%s size=%dx%d at 0x%X>" % (
self.__class__.__module__,
@ -1186,16 +1157,18 @@ class Image(object):
"""
Configures the image file loader so it returns a version of the
image that as closely as possible matches the given mode and
size. For example, you can use this method to convert a color
JPEG to greyscale while loading it, or to extract a 128x192
version from a PCD file.
size. For example, you can use this method to convert a color
JPEG to greyscale while loading it.
If any changes are made, returns a tuple with the chosen ``mode`` and
``box`` with coordinates of the original image within the altered one.
Note that this method modifies the :py:class:`~PIL.Image.Image` object
in place. If the image has already been loaded, this method has no
in place. If the image has already been loaded, this method has no
effect.
Note: This method is not implemented for most images. It is
currently implemented only for JPEG and PCD images.
currently implemented only for JPEG and MPO images.
:param mode: The requested mode.
:param size: The requested size.
@ -1350,10 +1323,7 @@ class Image(object):
self.load()
try:
if py3:
return list(self.im.getpalette())
else:
return [i8(c) for c in self.im.getpalette()]
return list(self.im.getpalette())
except ValueError:
return None # no palette
@ -1504,7 +1474,7 @@ class Image(object):
raise ValueError("cannot determine region size; use 4-item box")
box += (box[0] + size[0], box[1] + size[1])
if isStringType(im):
if isinstance(im, str):
from . import ImageColor
im = ImageColor.getcolor(im, self.mode)
@ -1704,10 +1674,7 @@ class Image(object):
palette = ImagePalette.raw(data.rawmode, data.palette)
else:
if not isinstance(data, bytes):
if py3:
data = bytes(data)
else:
data = "".join(chr(x) for x in data)
data = bytes(data)
palette = ImagePalette.raw(rawmode, data)
self.mode = "PA" if "A" in self.mode else "P"
self.palette = palette
@ -1827,7 +1794,24 @@ class Image(object):
return m_im
def resize(self, size, resample=NEAREST, box=None):
def _get_safe_box(self, size, resample, box):
"""Expands the box so it includes adjacent pixels
that may be used by resampling with the given resampling filter.
"""
filter_support = _filters_support[resample] - 0.5
scale_x = (box[2] - box[0]) / size[0]
scale_y = (box[3] - box[1]) / size[1]
support_x = filter_support * scale_x
support_y = filter_support * scale_y
return (
max(0, int(box[0] - support_x)),
max(0, int(box[1] - support_y)),
min(self.size[0], math.ceil(box[2] + support_x)),
min(self.size[1], math.ceil(box[3] + support_y)),
)
def resize(self, size, resample=BICUBIC, box=None, reducing_gap=None):
"""
Returns a resized copy of this image.
@ -1837,13 +1821,26 @@ class Image(object):
one of :py:attr:`PIL.Image.NEAREST`, :py:attr:`PIL.Image.BOX`,
:py:attr:`PIL.Image.BILINEAR`, :py:attr:`PIL.Image.HAMMING`,
:py:attr:`PIL.Image.BICUBIC` or :py:attr:`PIL.Image.LANCZOS`.
If omitted, or if the image has mode "1" or "P", it is
set :py:attr:`PIL.Image.NEAREST`.
Default filter is :py:attr:`PIL.Image.BICUBIC`.
If the image has mode "1" or "P", it is
always set to :py:attr:`PIL.Image.NEAREST`.
See: :ref:`concept-filters`.
:param box: An optional 4-tuple of floats giving the region
of the source image which should be scaled.
The values should be within (0, 0, width, height) rectangle.
:param box: An optional 4-tuple of floats providing
the source image region to be scaled.
The values must be within (0, 0, width, height) rectangle.
If omitted or None, the entire source is used.
:param reducing_gap: Apply optimization by resizing the image
in two steps. First, reducing the image by integer times
using :py:meth:`~PIL.Image.Image.reduce`.
Second, resizing using regular resampling. The last step
changes size no less than by ``reducing_gap`` times.
``reducing_gap`` may be None (no first step is performed)
or should be greater than 1.0. The bigger ``reducing_gap``,
the closer the result to the fair resampling.
The smaller ``reducing_gap``, the faster resizing.
With ``reducing_gap`` greater or equal to 3.0, the result is
indistinguishable from fair resampling in most cases.
The default value is None (no optimization).
:returns: An :py:class:`~PIL.Image.Image` object.
"""
@ -1865,6 +1862,9 @@ class Image(object):
message + " Use " + ", ".join(filters[:-1]) + " or " + filters[-1]
)
if reducing_gap is not None and reducing_gap < 1.0:
raise ValueError("reducing_gap must be 1.0 or greater")
size = tuple(size)
if box is None:
@ -1885,8 +1885,58 @@ class Image(object):
self.load()
if reducing_gap is not None and resample != NEAREST:
factor_x = int((box[2] - box[0]) / size[0] / reducing_gap) or 1
factor_y = int((box[3] - box[1]) / size[1] / reducing_gap) or 1
if factor_x > 1 or factor_y > 1:
reduce_box = self._get_safe_box(size, resample, box)
factor = (factor_x, factor_y)
if callable(self.reduce):
self = self.reduce(factor, box=reduce_box)
else:
self = Image.reduce(self, factor, box=reduce_box)
box = (
(box[0] - reduce_box[0]) / factor_x,
(box[1] - reduce_box[1]) / factor_y,
(box[2] - reduce_box[0]) / factor_x,
(box[3] - reduce_box[1]) / factor_y,
)
return self._new(self.im.resize(size, resample, box))
def reduce(self, factor, box=None):
"""
Returns a copy of the image reduced by `factor` times.
If the size of the image is not dividable by the `factor`,
the resulting size will be rounded up.
:param factor: A greater than 0 integer or tuple of two integers
for width and height separately.
:param box: An optional 4-tuple of ints providing
the source image region to be reduced.
The values must be within (0, 0, width, height) rectangle.
If omitted or None, the entire source is used.
"""
if not isinstance(factor, (list, tuple)):
factor = (factor, factor)
if box is None:
box = (0, 0) + self.size
else:
box = tuple(box)
if factor == (1, 1) and box == (0, 0) + self.size:
return self.copy()
if self.mode in ["LA", "RGBA"]:
im = self.convert(self.mode[:-1] + "a")
im = im.reduce(factor, box)
return im.convert(self.mode)
self.load()
return self._new(self.im.reduce(factor, box))
def rotate(
self,
angle,
@ -1908,7 +1958,7 @@ class Image(object):
environment), or :py:attr:`PIL.Image.BICUBIC`
(cubic spline interpolation in a 4x4 environment).
If omitted, or if the image has mode "1" or "P", it is
set :py:attr:`PIL.Image.NEAREST`. See :ref:`concept-filters`.
set to :py:attr:`PIL.Image.NEAREST`. See :ref:`concept-filters`.
:param expand: Optional expansion flag. If true, expands the output
image to make it large enough to hold the entire rotated image.
If false or omitted, make the output image the same size as the
@ -1993,8 +2043,8 @@ class Image(object):
x, y = transform(x, y, matrix)
xx.append(x)
yy.append(y)
nw = int(math.ceil(max(xx)) - math.floor(min(xx)))
nh = int(math.ceil(max(yy)) - math.floor(min(yy)))
nw = math.ceil(max(xx)) - math.floor(min(xx))
nh = math.ceil(max(yy)) - math.floor(min(yy))
# We multiply a translation matrix from the right. Because of its
# special form, this is the same as taking the image of the
@ -2039,7 +2089,7 @@ class Image(object):
if isPath(fp):
filename = fp
open_fp = True
elif HAS_PATHLIB and isinstance(fp, Path):
elif isinstance(fp, Path):
filename = str(fp)
open_fp = True
if not filename and hasattr(fp, "name") and isPath(fp.name):
@ -2161,7 +2211,7 @@ class Image(object):
"""
self.load()
if isStringType(channel):
if isinstance(channel, str):
try:
channel = self.getbands().index(channel)
except ValueError:
@ -2177,7 +2227,7 @@ class Image(object):
"""
return 0
def thumbnail(self, size, resample=BICUBIC):
def thumbnail(self, size, resample=BICUBIC, reducing_gap=2.0):
"""
Make this image into a thumbnail. This method modifies the
image to contain a thumbnail version of itself, no larger than
@ -2196,27 +2246,47 @@ class Image(object):
of :py:attr:`PIL.Image.NEAREST`, :py:attr:`PIL.Image.BILINEAR`,
:py:attr:`PIL.Image.BICUBIC`, or :py:attr:`PIL.Image.LANCZOS`.
If omitted, it defaults to :py:attr:`PIL.Image.BICUBIC`.
(was :py:attr:`PIL.Image.NEAREST` prior to version 2.5.0)
(was :py:attr:`PIL.Image.NEAREST` prior to version 2.5.0).
:param reducing_gap: Apply optimization by resizing the image
in two steps. First, reducing the image by integer times
using :py:meth:`~PIL.Image.Image.reduce` or
:py:meth:`~PIL.Image.Image.draft` for JPEG images.
Second, resizing using regular resampling. The last step
changes size no less than by ``reducing_gap`` times.
``reducing_gap`` may be None (no first step is performed)
or should be greater than 1.0. The bigger ``reducing_gap``,
the closer the result to the fair resampling.
The smaller ``reducing_gap``, the faster resizing.
With ``reducing_gap`` greater or equal to 3.0, the result is
indistinguishable from fair resampling in most cases.
The default value is 2.0 (very close to fair resampling
while still being faster in many cases).
:returns: None
"""
# preserve aspect ratio
x, y = self.size
if x > size[0]:
y = int(max(y * size[0] / x, 1))
x = int(size[0])
if y > size[1]:
x = int(max(x * size[1] / y, 1))
y = int(size[1])
size = x, y
if size == self.size:
x, y = map(math.floor, size)
if x >= self.width and y >= self.height:
return
self.draft(None, size)
def round_aspect(number, key):
return max(min(math.floor(number), math.ceil(number), key=key), 1)
# preserve aspect ratio
aspect = self.width / self.height
if x / y >= aspect:
x = round_aspect(y * aspect, key=lambda n: abs(aspect - n / y))
else:
y = round_aspect(x / aspect, key=lambda n: abs(aspect - x / n))
size = (x, y)
box = None
if reducing_gap is not None:
res = self.draft(None, (size[0] * reducing_gap, size[1] * reducing_gap))
if res is not None:
box = res[1]
if self.size != size:
im = self.resize(size, resample)
im = self.resize(size, resample, box=box, reducing_gap=reducing_gap)
self.im = im.im
self._size = size
@ -2246,12 +2316,14 @@ class Image(object):
It may also be an :py:class:`~PIL.Image.ImageTransformHandler`
object::
class Example(Image.ImageTransformHandler):
def transform(size, method, data, resample, fill=1):
# Return result
It may also be an object with a :py:meth:`~method.getdata` method
that returns a tuple supplying new **method** and **data** values::
class Example(object):
def getdata(self):
method = Image.EXTENT
@ -2297,6 +2369,7 @@ class Image(object):
raise ValueError("missing method data")
im = new(self.mode, size, fillcolor)
im.info = self.info.copy()
if method == MESH:
# list of quads
for box, quad in data:
@ -2318,8 +2391,8 @@ class Image(object):
elif method == EXTENT:
# convert extent to an affine transform
x0, y0, x1, y1 = data
xs = float(x1 - x0) / w
ys = float(y1 - y0) / h
xs = (x1 - x0) / w
ys = (y1 - y0) / h
method = AFFINE
data = (xs, 0, x0, 0, ys, y0)
@ -2425,12 +2498,12 @@ class Image(object):
# Abstract handlers.
class ImagePointHandler(object):
class ImagePointHandler:
# used as a mixin by point transforms (for use with im.point)
pass
class ImageTransformHandler(object):
class ImageTransformHandler:
# used as a mixin by geometry transforms (for use with im.transform)
pass
@ -2488,7 +2561,7 @@ def new(mode, size, color=0):
# don't initialize
return Image()._new(core.new(mode, size))
if isStringType(color):
if isinstance(color, str):
# css3-style specifier
from . import ImageColor
@ -2592,17 +2665,10 @@ def frombuffer(mode, size, data, decoder_name="raw", *args):
if decoder_name == "raw":
if args == ():
warnings.warn(
"the frombuffer defaults will change in Pillow 7.0.0; "
"for portability, change the call to read:\n"
" frombuffer(mode, size, data, 'raw', mode, 0, 1)",
RuntimeWarning,
stacklevel=2,
)
args = mode, 0, -1 # may change to (mode, 0, 1) post-1.1.6
args = mode, 0, 1
if args[0] in _MAPMODES:
im = new(mode, (1, 1))
im = im._new(core.map_buffer(data, size, decoder_name, None, 0, args))
im = im._new(core.map_buffer(data, size, decoder_name, 0, args))
im.readonly = 1
return im
@ -2642,9 +2708,12 @@ def fromarray(obj, mode=None):
if mode is None:
try:
typekey = (1, 1) + shape[2:], arr["typestr"]
mode, rawmode = _fromarray_typemap[typekey]
except KeyError:
raise TypeError("Cannot handle this data type")
try:
mode, rawmode = _fromarray_typemap[typekey]
except KeyError:
raise TypeError("Cannot handle this data type: %s, %s" % typekey)
else:
rawmode = mode
if mode in ["1", "L", "I", "P", "F"]:
@ -2748,16 +2817,24 @@ def open(fp, mode="r"):
and be opened in binary mode.
:param mode: The mode. If given, this argument must be "r".
:returns: An :py:class:`~PIL.Image.Image` object.
:exception IOError: If the file cannot be found, or the image cannot be
opened and identified.
:exception FileNotFoundError: If the file cannot be found.
:exception PIL.UnidentifiedImageError: If the image cannot be opened and
identified.
:exception ValueError: If the ``mode`` is not "r", or if a ``StringIO``
instance is used for ``fp``.
"""
if mode != "r":
raise ValueError("bad mode %r" % mode)
elif isinstance(fp, io.StringIO):
raise ValueError(
"StringIO cannot be used to open an image. "
"Binary data must be used instead."
)
exclusive_fp = False
filename = ""
if HAS_PATHLIB and isinstance(fp, Path):
if isinstance(fp, Path):
filename = str(fp.resolve())
elif isPath(fp):
filename = fp
@ -2815,7 +2892,9 @@ def open(fp, mode="r"):
fp.close()
for message in accept_warnings:
warnings.warn(message)
raise IOError("cannot identify image file %r" % (filename if filename else fp))
raise UnidentifiedImageError(
"cannot identify image file %r" % (filename if filename else fp)
)
#
@ -3238,7 +3317,7 @@ class Exif(MutableMapping):
continue
size = count * unit_size
if size > 4:
offset, = struct.unpack("<L", data)
(offset,) = struct.unpack("<L", data)
data = ifd_data[offset - 12 : offset + size - 12]
else:
data = data[:size]
@ -3268,7 +3347,7 @@ class Exif(MutableMapping):
)
if ifd_tag == 0x1101:
# CameraInfo
offset, = struct.unpack(">L", data)
(offset,) = struct.unpack(">L", data)
self.fp.seek(offset)
camerainfo = {"ModelID": self.fp.read(4)}
@ -3321,11 +3400,6 @@ class Exif(MutableMapping):
def __contains__(self, tag):
return tag in self._data or (self._info is not None and tag in self._info)
if not py3:
def has_key(self, tag):
return tag in self
def __setitem__(self, tag, value):
if self._info is not None and tag in self._info:
del self._info[tag]

View File

@ -139,6 +139,42 @@ def screen(image1, image2):
return image1._new(image1.im.chop_screen(image2.im))
def soft_light(image1, image2):
"""
Superimposes two images on top of each other using the Soft Light algorithm
:rtype: :py:class:`~PIL.Image.Image`
"""
image1.load()
image2.load()
return image1._new(image1.im.chop_soft_light(image2.im))
def hard_light(image1, image2):
"""
Superimposes two images on top of each other using the Hard Light algorithm
:rtype: :py:class:`~PIL.Image.Image`
"""
image1.load()
image2.load()
return image1._new(image1.im.chop_hard_light(image2.im))
def overlay(image1, image2):
"""
Superimposes two images on top of each other using the Overlay algorithm
:rtype: :py:class:`~PIL.Image.Image`
"""
image1.load()
image2.load()
return image1._new(image1.im.chop_overlay(image2.im))
def add(image1, image2, scale=1.0, offset=0):
"""
Adds two images, dividing the result by scale and adding the

View File

@ -15,12 +15,9 @@
# See the README file for information on usage and redistribution. See
# below for the original description.
from __future__ import print_function
import sys
from PIL import Image
from PIL._util import isStringType
try:
from PIL import _imagingcms
@ -152,7 +149,7 @@ for flag in FLAGS.values():
# Profile.
class ImageCmsProfile(object):
class ImageCmsProfile:
def __init__(self, profile):
"""
:param profile: Either a string representing a filename,
@ -161,7 +158,7 @@ class ImageCmsProfile(object):
"""
if isStringType(profile):
if isinstance(profile, str):
self._set(core.profile_open(profile), profile)
elif hasattr(profile, "read"):
self._set(core.profile_frombytes(profile.read()))
@ -257,20 +254,17 @@ def get_display_profile(handle=None):
:returns: None if the profile is not known.
"""
if sys.platform == "win32":
from PIL import ImageWin
if sys.platform != "win32":
return None
if isinstance(handle, ImageWin.HDC):
profile = core.get_display_profile_win32(handle, 1)
else:
profile = core.get_display_profile_win32(handle or 0)
from PIL import ImageWin
if isinstance(handle, ImageWin.HDC):
profile = core.get_display_profile_win32(handle, 1)
else:
try:
get = _imagingcms.get_display_profile
except AttributeError:
return None
else:
profile = get()
profile = core.get_display_profile_win32(handle or 0)
if profile is None:
return None
return ImageCmsProfile(profile)
@ -374,7 +368,7 @@ def profileToProfile(
imOut = None
else:
imOut = transform.apply(im)
except (IOError, TypeError, ValueError) as v:
except (OSError, TypeError, ValueError) as v:
raise PyCMSError(v)
return imOut
@ -398,7 +392,7 @@ def getOpenProfile(profileFilename):
try:
return ImageCmsProfile(profileFilename)
except (IOError, TypeError, ValueError) as v:
except (OSError, TypeError, ValueError) as v:
raise PyCMSError(v)
@ -479,7 +473,7 @@ def buildTransform(
return ImageCmsTransform(
inputProfile, outputProfile, inMode, outMode, renderingIntent, flags=flags
)
except (IOError, TypeError, ValueError) as v:
except (OSError, TypeError, ValueError) as v:
raise PyCMSError(v)
@ -590,7 +584,7 @@ def buildProofTransform(
proofRenderingIntent,
flags,
)
except (IOError, TypeError, ValueError) as v:
except (OSError, TypeError, ValueError) as v:
raise PyCMSError(v)
@ -733,9 +727,9 @@ def getProfileName(profile):
return (profile.profile.profile_description or "") + "\n"
if not manufacturer or len(model) > 30:
return model + "\n"
return "%s - %s\n" % (model, manufacturer)
return "{} - {}\n".format(model, manufacturer)
except (AttributeError, IOError, TypeError, ValueError) as v:
except (AttributeError, OSError, TypeError, ValueError) as v:
raise PyCMSError(v)
@ -775,7 +769,7 @@ def getProfileInfo(profile):
arr.append(elt)
return "\r\n\r\n".join(arr) + "\r\n\r\n"
except (AttributeError, IOError, TypeError, ValueError) as v:
except (AttributeError, OSError, TypeError, ValueError) as v:
raise PyCMSError(v)
@ -803,7 +797,7 @@ def getProfileCopyright(profile):
if not isinstance(profile, ImageCmsProfile):
profile = ImageCmsProfile(profile)
return (profile.profile.copyright or "") + "\n"
except (AttributeError, IOError, TypeError, ValueError) as v:
except (AttributeError, OSError, TypeError, ValueError) as v:
raise PyCMSError(v)
@ -831,7 +825,7 @@ def getProfileManufacturer(profile):
if not isinstance(profile, ImageCmsProfile):
profile = ImageCmsProfile(profile)
return (profile.profile.manufacturer or "") + "\n"
except (AttributeError, IOError, TypeError, ValueError) as v:
except (AttributeError, OSError, TypeError, ValueError) as v:
raise PyCMSError(v)
@ -860,7 +854,7 @@ def getProfileModel(profile):
if not isinstance(profile, ImageCmsProfile):
profile = ImageCmsProfile(profile)
return (profile.profile.model or "") + "\n"
except (AttributeError, IOError, TypeError, ValueError) as v:
except (AttributeError, OSError, TypeError, ValueError) as v:
raise PyCMSError(v)
@ -889,7 +883,7 @@ def getProfileDescription(profile):
if not isinstance(profile, ImageCmsProfile):
profile = ImageCmsProfile(profile)
return (profile.profile.profile_description or "") + "\n"
except (AttributeError, IOError, TypeError, ValueError) as v:
except (AttributeError, OSError, TypeError, ValueError) as v:
raise PyCMSError(v)
@ -928,7 +922,7 @@ def getDefaultIntent(profile):
if not isinstance(profile, ImageCmsProfile):
profile = ImageCmsProfile(profile)
return profile.profile.rendering_intent
except (AttributeError, IOError, TypeError, ValueError) as v:
except (AttributeError, OSError, TypeError, ValueError) as v:
raise PyCMSError(v)
@ -979,7 +973,7 @@ def isIntentSupported(profile, intent, direction):
return 1
else:
return -1
except (AttributeError, IOError, TypeError, ValueError) as v:
except (AttributeError, OSError, TypeError, ValueError) as v:
raise PyCMSError(v)

View File

@ -134,7 +134,9 @@ def getcolor(color, mode):
if Image.getmodebase(mode) == "L":
r, g, b = color
color = (r * 299 + g * 587 + b * 114) // 1000
# ITU-R Recommendation 601-2 for nonlinear RGB
# scaled to 24 bits to match the convert's implementation.
color = (r * 19595 + g * 38470 + b * 7471 + 0x8000) >> 16
if mode[-1] == "A":
return (color, alpha)
else:

View File

@ -34,7 +34,6 @@ import math
import numbers
from . import Image, ImageColor
from ._util import isStringType
"""
@ -45,7 +44,7 @@ directly.
"""
class ImageDraw(object):
class ImageDraw:
def __init__(self, im, mode=None):
"""
Create a drawing instance.
@ -107,13 +106,13 @@ class ImageDraw(object):
ink = self.ink
else:
if ink is not None:
if isStringType(ink):
if isinstance(ink, str):
ink = ImageColor.getcolor(ink, self.mode)
if self.palette and not isinstance(ink, numbers.Number):
ink = self.palette.getcolor(ink)
ink = self.draw.draw_ink(ink)
if fill is not None:
if isStringType(fill):
if isinstance(fill, str):
fill = ImageColor.getcolor(fill, self.mode)
if self.palette and not isinstance(fill, numbers.Number):
fill = self.palette.getcolor(fill)
@ -135,20 +134,20 @@ class ImageDraw(object):
if ink is not None:
self.draw.draw_bitmap(xy, bitmap.im, ink)
def chord(self, xy, start, end, fill=None, outline=None, width=0):
def chord(self, xy, start, end, fill=None, outline=None, width=1):
"""Draw a chord."""
ink, fill = self._getink(outline, fill)
if fill is not None:
self.draw.draw_chord(xy, start, end, fill, 1)
if ink is not None and ink != fill:
if ink is not None and ink != fill and width != 0:
self.draw.draw_chord(xy, start, end, ink, 0, width)
def ellipse(self, xy, fill=None, outline=None, width=0):
def ellipse(self, xy, fill=None, outline=None, width=1):
"""Draw an ellipse."""
ink, fill = self._getink(outline, fill)
if fill is not None:
self.draw.draw_ellipse(xy, fill, 1)
if ink is not None and ink != fill:
if ink is not None and ink != fill and width != 0:
self.draw.draw_ellipse(xy, ink, 0, width)
def line(self, xy, fill=None, width=0, joint=None):
@ -220,12 +219,12 @@ class ImageDraw(object):
if ink is not None and ink != fill:
self.draw.draw_outline(shape, ink, 0)
def pieslice(self, xy, start, end, fill=None, outline=None, width=0):
def pieslice(self, xy, start, end, fill=None, outline=None, width=1):
"""Draw a pieslice."""
ink, fill = self._getink(outline, fill)
if fill is not None:
self.draw.draw_pieslice(xy, start, end, fill, 1)
if ink is not None and ink != fill:
if ink is not None and ink != fill and width != 0:
self.draw.draw_pieslice(xy, start, end, ink, 0, width)
def point(self, xy, fill=None):
@ -242,12 +241,12 @@ class ImageDraw(object):
if ink is not None and ink != fill:
self.draw.draw_polygon(xy, ink, 0)
def rectangle(self, xy, fill=None, outline=None, width=0):
def rectangle(self, xy, fill=None, outline=None, width=1):
"""Draw a rectangle."""
ink, fill = self._getink(outline, fill)
if fill is not None:
self.draw.draw_rectangle(xy, fill, 1)
if ink is not None and ink != fill:
if ink is not None and ink != fill and width != 0:
self.draw.draw_rectangle(xy, ink, 0, width)
def _multiline_check(self, text):
@ -314,7 +313,7 @@ class ImageDraw(object):
language=language,
stroke_width=stroke_width,
*args,
**kwargs
**kwargs,
)
coord = coord[0] + offset[0], coord[1] + offset[1]
except AttributeError:
@ -327,7 +326,7 @@ class ImageDraw(object):
language,
stroke_width,
*args,
**kwargs
**kwargs,
)
except TypeError:
mask = font.getmask(text)

View File

@ -19,25 +19,25 @@
from . import Image, ImageColor, ImageDraw, ImageFont, ImagePath
class Pen(object):
class Pen:
def __init__(self, color, width=1, opacity=255):
self.color = ImageColor.getrgb(color)
self.width = width
class Brush(object):
class Brush:
def __init__(self, color, opacity=255):
self.color = ImageColor.getrgb(color)
class Font(object):
class Font:
def __init__(self, color, file, size=12):
# FIXME: add support for bitmap fonts
self.color = ImageColor.getrgb(color)
self.font = ImageFont.truetype(file, size)
class Draw(object):
class Draw:
def __init__(self, image, size=None, color=None):
if not hasattr(image, "im"):
image = Image.new(image, size, color)

View File

@ -21,7 +21,7 @@
from . import Image, ImageFilter, ImageStat
class _Enhance(object):
class _Enhance:
def enhance(self, factor):
"""
Returns an enhanced image.

View File

@ -56,7 +56,7 @@ def raise_ioerror(error):
message = ERRORS.get(error)
if not message:
message = "decoder error %d" % error
raise IOError(message + " when reading image file")
raise OSError(message + " when reading image file")
#
@ -78,7 +78,7 @@ class ImageFile(Image.Image):
"Base class for image file format handlers."
def __init__(self, fp=None, filename=None):
Image.Image.__init__(self)
super().__init__()
self._min_frame = 0
@ -103,26 +103,24 @@ class ImageFile(Image.Image):
self._exclusive_fp = None
try:
self._open()
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:
try:
self._open()
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:
raise SyntaxError(v)
if not self.mode or self.size[0] <= 0:
raise SyntaxError("not identified by this driver")
except BaseException:
# close the file only if we have opened it this constructor
if self._exclusive_fp:
self.fp.close()
raise SyntaxError(v)
if not self.mode or self.size[0] <= 0:
raise SyntaxError("not identified by this driver")
def draft(self, mode, size):
"""Set draft mode"""
pass
raise
def get_format_mimetype(self):
if self.custom_mimetype:
@ -145,7 +143,7 @@ class ImageFile(Image.Image):
pixel = Image.Image.load(self)
if self.tile is None:
raise IOError("cannot load this image")
raise OSError("cannot load this image")
if not self.tile:
return pixel
@ -196,14 +194,14 @@ class ImageFile(Image.Image):
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, offset, args
)
readonly = 1
# After trashing self.im,
# we might need to reload the palette data.
if self.palette:
self.palette.dirty = 1
except (AttributeError, EnvironmentError, ImportError):
except (AttributeError, OSError, ImportError):
self.map = None
self.load_prepare()
@ -238,13 +236,13 @@ class ImageFile(Image.Image):
if LOAD_TRUNCATED_IMAGES:
break
else:
raise IOError("image file is truncated")
raise OSError("image file is truncated")
if not s: # truncated jpeg
if LOAD_TRUNCATED_IMAGES:
break
else:
raise IOError(
raise OSError(
"image file is truncated "
"(%d bytes not processed)" % len(b)
)
@ -322,7 +320,7 @@ class StubImageFile(ImageFile):
def load(self):
loader = self._load()
if loader is None:
raise IOError("cannot find loader for this %s file" % self.format)
raise OSError("cannot find loader for this %s file" % self.format)
image = loader.load(self)
assert image is not None
# become the other object (!)
@ -334,7 +332,7 @@ class StubImageFile(ImageFile):
raise NotImplementedError("StubImageFile subclass must implement _load")
class Parser(object):
class Parser:
"""
Incremental image parser. This class implements the standard
feed/close consumer interface.
@ -411,7 +409,7 @@ class Parser(object):
try:
with io.BytesIO(self.data) as fp:
im = Image.open(fp)
except IOError:
except OSError:
# traceback.print_exc()
pass # not enough data
else:
@ -456,9 +454,9 @@ class Parser(object):
self.feed(b"")
self.data = self.decoder = None
if not self.finished:
raise IOError("image was incomplete")
raise OSError("image was incomplete")
if not self.image:
raise IOError("cannot parse this image")
raise OSError("cannot parse this image")
if self.data:
# incremental parsing not possible; reopen the file
# not that we have all data
@ -514,7 +512,7 @@ def _save(im, fp, tile, bufsize=0):
if s:
break
if s < 0:
raise IOError("encoder error %d when writing image file" % s)
raise OSError("encoder error %d when writing image file" % s)
e.cleanup()
else:
# slight speedup: compress to real file object
@ -529,7 +527,7 @@ def _save(im, fp, tile, bufsize=0):
else:
s = e.encode_to_file(fh, bufsize)
if s < 0:
raise IOError("encoder error %d when writing image file" % s)
raise OSError("encoder error %d when writing image file" % s)
e.cleanup()
if hasattr(fp, "flush"):
fp.flush()
@ -559,7 +557,7 @@ def _safe_read(fp, size):
return b"".join(data)
class PyCodecState(object):
class PyCodecState:
def __init__(self):
self.xsize = 0
self.ysize = 0
@ -570,7 +568,7 @@ class PyCodecState(object):
return (self.xoff, self.yoff, self.xoff + self.xsize, self.yoff + self.ysize)
class PyDecoder(object):
class PyDecoder:
"""
Python implementation of a format decoder. Override this class and
add the decoding logic in the `decode` method.

View File

@ -14,9 +14,6 @@
#
# See the README file for information on usage and redistribution.
#
from __future__ import division
import functools
try:
@ -25,7 +22,7 @@ except ImportError: # pragma: no cover
numpy = None
class Filter(object):
class Filter:
pass
@ -498,7 +495,7 @@ class Color3DLUT(MultibandFilter):
r / (size1D - 1),
g / (size2D - 1),
b / (size3D - 1),
*values
*values,
)
else:
values = callback(*values)

View File

@ -25,17 +25,19 @@
# See the README file for information on usage and redistribution.
#
import base64
import os
import sys
from io import BytesIO
from . import Image
from ._util import isDirectory, isPath, py3
from ._util import isDirectory, isPath
LAYOUT_BASIC = 0
LAYOUT_RAQM = 1
class _imagingft_not_installed(object):
class _imagingft_not_installed:
# module placeholder
def __getattr__(self, id):
raise ImportError("The _imagingft C module is not installed")
@ -63,13 +65,16 @@ except ImportError:
# --------------------------------------------------------------------
class ImageFont(object):
class ImageFont:
"PIL font wrapper"
def _load_pilfont(self, filename):
with open(filename, "rb") as fp:
image = None
for ext in (".png", ".gif", ".pbm"):
if image:
image.close()
try:
fullname = os.path.splitext(filename)[0] + ext
image = Image.open(fullname)
@ -79,11 +84,14 @@ class ImageFont(object):
if image and image.mode in ("1", "L"):
break
else:
raise IOError("cannot find glyph data file")
if image:
image.close()
raise OSError("cannot find glyph data file")
self.file = fullname
return self._load_pilfont_data(fp, image)
self._load_pilfont_data(fp, image)
image.close()
def _load_pilfont_data(self, file, image):
@ -145,7 +153,7 @@ class ImageFont(object):
# <b>truetype</b> factory function to create font objects.
class FreeTypeFont(object):
class FreeTypeFont:
"FreeType font wrapper (requires _imagingft service)"
def __init__(self, font=None, size=10, index=0, encoding="", layout_engine=None):
@ -542,7 +550,7 @@ class FreeTypeFont(object):
raise NotImplementedError("FreeType 2.9.1 or greater is required")
class TransposedFont(object):
class TransposedFont:
"Wrapper for writing rotated or mirrored text"
def __init__(self, font, orientation=None):
@ -638,7 +646,7 @@ def truetype(font=None, size=10, index=0, encoding="", layout_engine=None):
try:
return freetype(font)
except IOError:
except OSError:
if not isPath(font):
raise
ttf_filename = os.path.basename(font)
@ -695,15 +703,12 @@ def load_path(filename):
for directory in sys.path:
if isDirectory(directory):
if not isinstance(filename, str):
if py3:
filename = filename.decode("utf-8")
else:
filename = filename.encode("utf-8")
filename = filename.decode("utf-8")
try:
return load(os.path.join(directory, filename))
except IOError:
except OSError:
pass
raise IOError("cannot find font file")
raise OSError("cannot find font file")
def load_default():
@ -713,9 +718,6 @@ def load_default():
:return: A font object.
"""
from io import BytesIO
import base64
f = ImageFont()
f._load_pilfont_data(
# courB08

View File

@ -19,42 +19,52 @@ import sys
from . import Image
if sys.platform == "win32":
grabber = Image.core.grabscreen
elif sys.platform == "darwin":
if sys.platform == "darwin":
import os
import tempfile
import subprocess
else:
raise ImportError("ImageGrab is macOS and Windows only")
def grab(bbox=None, include_layered_windows=False, all_screens=False):
if sys.platform == "darwin":
fh, filepath = tempfile.mkstemp(".png")
os.close(fh)
subprocess.call(["screencapture", "-x", filepath])
im = Image.open(filepath)
im.load()
os.unlink(filepath)
if bbox:
im = im.crop(bbox)
else:
offset, size, data = grabber(include_layered_windows, all_screens)
im = Image.frombytes(
"RGB",
size,
data,
# RGB, 32-bit line padding, origin lower left corner
"raw",
"BGR",
(size[0] * 3 + 3) & -4,
-1,
)
if bbox:
x0, y0 = offset
left, top, right, bottom = bbox
im = im.crop((left - x0, top - y0, right - x0, bottom - y0))
def grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=None):
if xdisplay is None:
if sys.platform == "darwin":
fh, filepath = tempfile.mkstemp(".png")
os.close(fh)
subprocess.call(["screencapture", "-x", filepath])
im = Image.open(filepath)
im.load()
os.unlink(filepath)
if bbox:
im_cropped = im.crop(bbox)
im.close()
return im_cropped
return im
elif sys.platform == "win32":
offset, size, data = Image.core.grabscreen_win32(
include_layered_windows, all_screens
)
im = Image.frombytes(
"RGB",
size,
data,
# RGB, 32-bit line padding, origin lower left corner
"raw",
"BGR",
(size[0] * 3 + 3) & -4,
-1,
)
if bbox:
x0, y0 = offset
left, top, right, bottom = bbox
im = im.crop((left - x0, top - y0, right - x0, bottom - y0))
return im
# use xdisplay=None for default display on non-win32/macOS systems
if not Image.core.HAVE_XCB:
raise IOError("Pillow was built without XCB support")
size, data = Image.core.grabscreen_x11(xdisplay)
im = Image.frombytes("RGB", size, data, "raw", "BGRX", size[0] * 4, 1)
if bbox:
im = im.crop(bbox)
return im
@ -82,11 +92,13 @@ def grabclipboard():
im.load()
os.unlink(filepath)
return im
else:
data = Image.core.grabclipboard()
elif sys.platform == "win32":
data = Image.core.grabclipboard_win32()
if isinstance(data, bytes):
from . import BmpImagePlugin
import io
return BmpImagePlugin.DibImageFile(io.BytesIO(data))
return data
else:
raise NotImplementedError("ImageGrab.grabclipboard() is macOS and Windows only")

View File

@ -15,15 +15,9 @@
# See the README file for information on usage and redistribution.
#
import builtins
from . import Image, _imagingmath
from ._util import py3
try:
import builtins
except ImportError:
import __builtin__
builtins = __builtin__
VERBOSE = 0
@ -32,7 +26,7 @@ def _isconstant(v):
return isinstance(v, (int, float))
class _Operand(object):
class _Operand:
"""Wraps an image operand, providing standard operators"""
def __init__(self, im):
@ -101,11 +95,6 @@ class _Operand(object):
# an image is "true" if it contains at least one non-zero pixel
return self.im.getbbox() is not None
if not py3:
# Provide __nonzero__ for pre-Py3k
__nonzero__ = __bool__
del __bool__
def __abs__(self):
return self.apply("abs", self)
@ -152,13 +141,6 @@ class _Operand(object):
def __rpow__(self, other):
return self.apply("pow", other, self)
if not py3:
# Provide __div__ and __rdiv__ for pre-Py3k
__div__ = __truediv__
__rdiv__ = __rtruediv__
del __truediv__
del __rtruediv__
# bitwise
def __invert__(self):
return self.apply("invert", self)

View File

@ -17,7 +17,7 @@
_modes = None
class ModeDescriptor(object):
class ModeDescriptor:
"""Wrapper for mode strings."""
def __init__(self, mode, bands, basemode, basetype):

View File

@ -5,8 +5,6 @@
#
# Copyright (c) 2014 Dov Grobgeld <dov.grobgeld@gmail.com>
from __future__ import print_function
import re
from . import Image, _imagingmorph
@ -27,7 +25,7 @@ MIRROR_MATRIX = [
# fmt: on
class LutBuilder(object):
class LutBuilder:
"""A class for building a MorphLut from a descriptive language
The input patterns is a list of a strings sequences like these::
@ -178,7 +176,7 @@ class LutBuilder(object):
return self.lut
class MorphOp(object):
class MorphOp:
"""A class for binary morphological operators"""
def __init__(self, lut=None, op_name=None, patterns=None):

View File

@ -21,7 +21,6 @@ import functools
import operator
from . import Image
from ._util import isStringType
#
# helpers
@ -39,7 +38,7 @@ def _border(border):
def _color(color, mode):
if isStringType(color):
if isinstance(color, str):
from . import ImageColor
color = ImageColor.getcolor(color, mode)
@ -55,7 +54,7 @@ def _lut(image, lut):
lut = lut + lut + lut
return image.point(lut)
else:
raise IOError("not supported for this image mode")
raise OSError("not supported for this image mode")
#
@ -222,7 +221,7 @@ def colorize(image, black, white, mid=None, blackpoint=0, whitepoint=255, midpoi
return _lut(image, red + green + blue)
def pad(image, size, method=Image.NEAREST, color=None, centering=(0.5, 0.5)):
def pad(image, size, method=Image.BICUBIC, color=None, centering=(0.5, 0.5)):
"""
Returns a sized and padded version of the image, expanded to fill the
requested aspect ratio and size.
@ -231,10 +230,11 @@ def pad(image, size, method=Image.NEAREST, color=None, centering=(0.5, 0.5)):
:param size: The requested output size in pixels, given as a
(width, height) tuple.
:param method: What resampling method to use. Default is
:py:attr:`PIL.Image.NEAREST`.
:py:attr:`PIL.Image.BICUBIC`. See :ref:`concept-filters`.
:param color: The background color of the padded image.
:param centering: Control the position of the original image within the
padded version.
(0.5, 0.5) will keep the image centered
(0, 0) will keep the image aligned to the top left
(1, 1) will keep the image aligned to the bottom
@ -243,7 +243,7 @@ def pad(image, size, method=Image.NEAREST, color=None, centering=(0.5, 0.5)):
"""
im_ratio = image.width / image.height
dest_ratio = float(size[0]) / size[1]
dest_ratio = size[0] / size[1]
if im_ratio == dest_ratio:
out = image.resize(size, resample=method)
@ -281,7 +281,7 @@ def crop(image, border=0):
return image.crop((left, top, image.size[0] - right, image.size[1] - bottom))
def scale(image, factor, resample=Image.NEAREST):
def scale(image, factor, resample=Image.BICUBIC):
"""
Returns a rescaled image by a specific factor given in parameter.
A factor greater than 1 expands the image, between 0 and 1 contracts the
@ -289,8 +289,8 @@ def scale(image, factor, resample=Image.NEAREST):
:param image: The image to rescale.
:param factor: The expansion factor, as a float.
:param resample: An optional resampling filter. Same values possible as
in the PIL.Image.resize function.
:param resample: What resampling method to use. Default is
:py:attr:`PIL.Image.BICUBIC`. See :ref:`concept-filters`.
:returns: An :py:class:`~PIL.Image.Image` object.
"""
if factor == 1:
@ -298,7 +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 = (round(factor * image.width), round(factor * image.height))
return image.resize(size, resample)
@ -364,7 +364,7 @@ def expand(image, border=0, fill=0):
return out
def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)):
def fit(image, size, method=Image.BICUBIC, bleed=0.0, centering=(0.5, 0.5)):
"""
Returns a sized and cropped version of the image, cropped to the
requested aspect ratio and size.
@ -375,7 +375,7 @@ def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)):
:param size: The requested output size in pixels, given as a
(width, height) tuple.
:param method: What resampling method to use. Default is
:py:attr:`PIL.Image.NEAREST`.
:py:attr:`PIL.Image.BICUBIC`. See :ref:`concept-filters`.
: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).
@ -420,10 +420,10 @@ def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)):
)
# calculate the aspect ratio of the live_size
live_size_ratio = float(live_size[0]) / live_size[1]
live_size_ratio = live_size[0] / live_size[1]
# calculate the aspect ratio of the output image
output_ratio = float(size[0]) / size[1]
output_ratio = size[0] / size[1]
# figure out if the sides or top/bottom will be cropped off
if live_size_ratio == output_ratio:

View File

@ -21,7 +21,7 @@ import array
from . import GimpGradientFile, GimpPaletteFile, ImageColor, PaletteFile
class ImagePalette(object):
class ImagePalette:
"""
Color palette for palette mapped images
@ -216,6 +216,6 @@ def load(filename):
# traceback.print_exc()
pass
else:
raise IOError("cannot load palette")
raise OSError("cannot load palette")
return lut # data, rawmode

View File

@ -17,18 +17,12 @@
#
import sys
import warnings
from io import BytesIO
from . import Image
from ._util import isPath, py3
from ._util import isPath
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. "
"Please upgrade to PyQt5 or PySide2."
)
qt_versions = [["5", "PyQt5"], ["side2", "PySide2"]]
# 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)
@ -40,16 +34,6 @@ for qt_version, qt_module in qt_versions:
elif qt_module == "PySide2":
from PySide2.QtGui import QImage, qRgba, QPixmap
from PySide2.QtCore import QBuffer, QIODevice
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":
from PySide.QtGui import QImage, qRgba, QPixmap
from PySide.QtCore import QBuffer, QIODevice
warnings.warn(WARNING_TEXT.format(qt_module), DeprecationWarning)
except (ImportError, RuntimeError):
continue
qt_is_installed = True
@ -81,11 +65,7 @@ def fromqimage(im):
im.save(buffer, "ppm")
b = BytesIO()
try:
b.write(buffer.data())
except TypeError:
# workaround for Python 2
b.write(str(buffer.data()))
b.write(buffer.data())
buffer.close()
b.seek(0)
@ -141,10 +121,7 @@ def _toqclass_helper(im):
# handle filename, if given instead of image name
if hasattr(im, "toUtf8"):
# FIXME - is this really the best way to do this?
if py3:
im = str(im.toUtf8(), "utf-8")
else:
im = unicode(im.toUtf8(), "utf-8") # noqa: F821
im = str(im.toUtf8(), "utf-8")
if isPath(im):
im = Image.open(im)
@ -196,8 +173,7 @@ if qt_is_installed:
# 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,
super().__init__(
self.__data,
im_data["im"].size[0],
im_data["im"].size[1],

View File

@ -16,7 +16,7 @@
##
class Iterator(object):
class Iterator:
"""
This class implements an iterator object that can be used to loop
over an image sequence.
@ -52,9 +52,6 @@ class Iterator(object):
except EOFError:
raise StopIteration
def next(self):
return self.__next__()
def all_frames(im, func=None):
"""

View File

@ -11,21 +11,15 @@
#
# See the README file for information on usage and redistribution.
#
from __future__ import print_function
import os
import shutil
import subprocess
import sys
import tempfile
from shlex import quote
from PIL import Image
if sys.version_info.major >= 3:
from shlex import quote
else:
from pipes import quote
_viewers = []
@ -56,7 +50,7 @@ def show(image, title=None, **options):
return 0
class Viewer(object):
class Viewer:
"""Base class for viewers."""
# main api
@ -127,10 +121,8 @@ elif sys.platform == "darwin":
# on darwin open returns immediately resulting in the temp
# file removal while app is opening
command = "open -a Preview.app"
command = "(%s %s; sleep 20; rm -f %s)&" % (
command,
quote(file),
quote(file),
command = "({} {}; sleep 20; rm -f {})&".format(
command, quote(file), quote(file)
)
return command
@ -154,23 +146,13 @@ else:
# unixoids
def which(executable):
path = os.environ.get("PATH")
if not path:
return None
for dirname in path.split(os.pathsep):
filename = os.path.join(dirname, executable)
if os.path.isfile(filename) and os.access(filename, os.X_OK):
return filename
return None
class UnixViewer(Viewer):
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))
return "({} {}; rm -f {})&".format(command, quote(file), quote(file))
def show_file(self, file, **options):
"""Display given file"""
@ -192,7 +174,7 @@ else:
command = executable = "display"
return command, executable
if which("display"):
if shutil.which("display"):
register(DisplayViewer)
class EogViewer(UnixViewer):
@ -200,7 +182,7 @@ else:
command = executable = "eog"
return command, executable
if which("eog"):
if shutil.which("eog"):
register(EogViewer)
class XVViewer(UnixViewer):
@ -212,7 +194,7 @@ else:
command += " -name %s" % quote(title)
return command, executable
if which("xv"):
if shutil.which("xv"):
register(XVViewer)
if __name__ == "__main__":
@ -221,4 +203,5 @@ if __name__ == "__main__":
print("Syntax: python ImageShow.py imagefile [title]")
sys.exit()
print(show(Image.open(sys.argv[1]), *sys.argv[2:]))
with Image.open(sys.argv[1]) as im:
print(show(im, *sys.argv[2:]))

View File

@ -26,7 +26,7 @@ import math
import operator
class Stat(object):
class Stat:
def __init__(self, image_or_list, mask=None):
try:
if mask:

View File

@ -25,17 +25,11 @@
# See the README file for information on usage and redistribution.
#
import sys
import tkinter
from io import BytesIO
from . import Image
if sys.version_info.major > 2:
import tkinter
else:
import Tkinter as tkinter
# --------------------------------------------------------------------
# Check for Tkinter interface hooks
@ -68,7 +62,7 @@ def _get_image_from_kw(kw):
# PhotoImage
class PhotoImage(object):
class PhotoImage:
"""
A Tkinter-compatible photo image. This can be used
everywhere Tkinter expects an image object. If the image is an RGBA
@ -209,7 +203,7 @@ class PhotoImage(object):
# BitmapImage
class BitmapImage(object):
class BitmapImage:
"""
A Tkinter-compatible bitmap image. This can be used everywhere Tkinter
expects an image object.
@ -296,10 +290,10 @@ 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)
super().__init__(master, image=self.image, bg="black", bd=0)
if not tkinter._default_root:
raise IOError("tkinter not initialized")
raise OSError("tkinter not initialized")
top = tkinter.Toplevel()
if title:
top.title(title)

View File

@ -20,7 +20,7 @@
from . import Image
class HDC(object):
class HDC:
"""
Wraps an HDC integer. The resulting object can be passed to the
:py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose`
@ -34,7 +34,7 @@ class HDC(object):
return self.dc
class HWND(object):
class HWND:
"""
Wraps an HWND integer. The resulting object can be passed to the
:py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose`
@ -48,7 +48,7 @@ class HWND(object):
return self.wnd
class Dib(object):
class Dib:
"""
A Windows bitmap with the given mode and size. The mode can be one of "1",
"L", "P", or "RGB".
@ -186,7 +186,7 @@ class Dib(object):
return self.image.tobytes()
class Window(object):
class Window:
"""Create a Window with the given title size."""
def __init__(self, title="PIL", width=None, height=None):
@ -224,7 +224,7 @@ class ImageWindow(Window):
image = Dib(image)
self.image = image
width, height = image.size
Window.__init__(self, title, width=width, height=height)
super().__init__(title, width=width, height=height)
def ui_handle_repair(self, dc, x0, y0, x1, y1):
self.image.draw(dc, (x0, y0, x1, y1))

View File

@ -19,11 +19,6 @@ import re
from . import Image, ImageFile
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.2"
#
# --------------------------------------------------------------------

View File

@ -14,19 +14,12 @@
#
# See the README file for information on usage and redistribution.
#
from __future__ import print_function
import os
import tempfile
from . import Image, ImageFile
from ._binary import i8, i16be as i16, i32be as i32, o8
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.3"
COMPRESSION = {1: "raw", 5: "jpeg"}
PAD = o8(0) * 4
@ -75,7 +68,7 @@ class IptcImageFile(ImageFile.ImageFile):
# field size
size = i8(s[3])
if size > 132:
raise IOError("illegal field length in IPTC/NAA file")
raise OSError("illegal field length in IPTC/NAA file")
elif size == 128:
size = 0
elif size > 128:
@ -126,7 +119,7 @@ class IptcImageFile(ImageFile.ImageFile):
try:
compression = COMPRESSION[self.getint((3, 120))]
except KeyError:
raise IOError("Unknown IPTC image compression")
raise OSError("Unknown IPTC image compression")
# tile
if tag == (8, 10):
@ -165,9 +158,9 @@ class IptcImageFile(ImageFile.ImageFile):
o.close()
try:
_im = Image.open(outfile)
_im.load()
self.im = _im.im
with Image.open(outfile) as _im:
_im.load()
self.im = _im.im
finally:
try:
os.unlink(outfile)
@ -215,7 +208,7 @@ def getiptcinfo(im):
return None # no properties
# create an IptcImagePlugin object without initializing it
class FakeImage(object):
class FakeImage:
pass
im = FakeImage()

View File

@ -18,10 +18,6 @@ import struct
from . import Image, ImageFile
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.1"
def _parse_codestream(fp):
"""Parse the JPEG 2000 codestream to extract the size and component
@ -180,7 +176,7 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
if self.size is None or self.mode is None:
raise SyntaxError("unable to determine size/mode")
self.reduce = 0
self._reduce = 0
self.layers = 0
fd = -1
@ -204,23 +200,33 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
"jpeg2k",
(0, 0) + self.size,
0,
(self.codec, self.reduce, self.layers, fd, length),
(self.codec, self._reduce, self.layers, fd, length),
)
]
@property
def reduce(self):
# https://github.com/python-pillow/Pillow/issues/4343 found that the
# new Image 'reduce' method was shadowed by this plugin's 'reduce'
# property. This attempts to allow for both scenarios
return self._reduce or super().reduce
@reduce.setter
def reduce(self, value):
self._reduce = value
def load(self):
if self.reduce:
power = 1 << self.reduce
if self.tile and self._reduce:
power = 1 << self._reduce
adjust = power >> 1
self._size = (
int((self.size[0] + adjust) / power),
int((self.size[1] + adjust) / power),
)
if self.tile:
# Update the reduce and layers settings
t = self.tile[0]
t3 = (t[3][0], self.reduce, self.layers, t[3][3], t[3][4])
t3 = (t[3][0], self._reduce, self.layers, t[3][3], t[3][4])
self.tile = [(t[0], (0, 0) + self.size, t[2], t3)]
return ImageFile.ImageFile.load(self)

View File

@ -31,24 +31,18 @@
#
# See the README file for information on usage and redistribution.
#
from __future__ import print_function
import array
import io
import os
import struct
import subprocess
import tempfile
import warnings
from . import Image, ImageFile, TiffImagePlugin
from ._binary import i8, i16be as i16, i32be as i32, o8
from ._util import isStringType
from .JpegPresets import presets
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.6"
#
# Parser
@ -106,27 +100,25 @@ def APP(self, marker):
# reassemble the profile, rather than assuming that the APP2
# markers appear in the correct sequence.
self.icclist.append(s)
elif marker == 0xFFED:
if s[:14] == b"Photoshop 3.0\x00":
blocks = s[14:]
# parse the image resource block
offset = 0
photoshop = {}
while blocks[offset : offset + 4] == b"8BIM":
elif marker == 0xFFED and s[:14] == b"Photoshop 3.0\x00":
# parse the image resource block
offset = 14
photoshop = self.info.setdefault("photoshop", {})
while s[offset : offset + 4] == b"8BIM":
try:
offset += 4
# resource code
code = i16(blocks, offset)
code = i16(s, offset)
offset += 2
# resource name (usually empty)
name_len = i8(blocks[offset])
# name = blocks[offset+1:offset+1+name_len]
offset = 1 + offset + name_len
if offset & 1:
offset += 1
name_len = i8(s[offset])
# name = s[offset+1:offset+1+name_len]
offset += 1 + name_len
offset += offset & 1 # align
# resource data block
size = i32(blocks, offset)
size = i32(s, offset)
offset += 4
data = blocks[offset : offset + size]
data = s[offset : offset + size]
if code == 0x03ED: # ResolutionInfo
data = {
"XResolution": i32(data[:4]) / 65536,
@ -135,10 +127,11 @@ def APP(self, marker):
"DisplayedUnitsY": i16(data[12:]),
}
photoshop[code] = data
offset = offset + size
if offset & 1:
offset += 1
self.info["photoshop"] = photoshop
offset += size
offset += offset & 1 # align
except struct.error:
break # insufficient data
elif marker == 0xFFEE and s[:5] == b"Adobe":
self.info["adobe"] = i16(s, 5)
# extract Adobe custom properties
@ -169,10 +162,11 @@ def APP(self, marker):
# 1 dpcm = 2.54 dpi
dpi *= 2.54
self.info["dpi"] = int(dpi + 0.5), int(dpi + 0.5)
except (KeyError, SyntaxError, ZeroDivisionError):
except (KeyError, SyntaxError, ValueError, ZeroDivisionError):
# SyntaxError for invalid/unreadable EXIF
# KeyError for dpi not included
# ZeroDivisionError for invalid dpi rational value
# ValueError for x_resolution[0] being an invalid float
self.info["dpi"] = 72, 72
@ -182,6 +176,7 @@ def COM(self, marker):
n = i16(self.fp.read(2)) - 2
s = ImageFile._safe_read(self.fp, n)
self.info["comment"] = s
self.app["COM"] = s # compatibility
self.applist.append(("COM", s))
@ -415,7 +410,8 @@ class JpegImageFile(ImageFile.ImageFile):
return
d, e, o, a = self.tile[0]
scale = 0
scale = 1
original_size = self.size
if a[0] == "RGB" and mode in ["L", "YCbCr"]:
self.mode = mode
@ -438,16 +434,13 @@ class JpegImageFile(ImageFile.ImageFile):
self.tile = [(d, e, o, a)]
self.decoderconfig = (scale, 0)
return self
box = (0, 0, original_size[0] / scale, original_size[1] / scale)
return (self.mode, box)
def load_djpeg(self):
# ALTERNATIVE: handle JPEGs via the IJG command line utilities
import subprocess
import tempfile
import os
f, path = tempfile.mkstemp()
os.close(f)
if os.path.exists(self.filename):
@ -456,9 +449,9 @@ class JpegImageFile(ImageFile.ImageFile):
raise ValueError("Invalid Filename")
try:
_im = Image.open(path)
_im.load()
self.im = _im.im
with Image.open(path) as _im:
_im.load()
self.im = _im.im
finally:
try:
os.unlink(path)
@ -618,23 +611,23 @@ def _save(im, fp, filename):
try:
rawmode = RAWMODE[im.mode]
except KeyError:
raise IOError("cannot write mode %s as JPEG" % im.mode)
raise OSError("cannot write mode %s as JPEG" % im.mode)
info = im.encoderinfo
dpi = [int(round(x)) for x in info.get("dpi", (0, 0))]
dpi = [round(x) for x in info.get("dpi", (0, 0))]
quality = info.get("quality", 0)
quality = info.get("quality", -1)
subsampling = info.get("subsampling", -1)
qtables = info.get("qtables")
if quality == "keep":
quality = 0
quality = -1
subsampling = "keep"
qtables = "keep"
elif quality in presets:
preset = presets[quality]
quality = 0
quality = -1
subsampling = preset.get("subsampling", -1)
qtables = preset.get("quantization")
elif not isinstance(quality, int):
@ -642,7 +635,7 @@ def _save(im, fp, filename):
else:
if subsampling in presets:
subsampling = presets[subsampling].get("subsampling", -1)
if isStringType(qtables) and qtables in presets:
if isinstance(qtables, str) and qtables in presets:
qtables = presets[qtables].get("quantization")
if subsampling == "4:4:4":
@ -663,7 +656,7 @@ def _save(im, fp, filename):
def validate_qtables(qtables):
if qtables is None:
return qtables
if isStringType(qtables):
if isinstance(qtables, str):
try:
lines = [
int(num)
@ -757,8 +750,8 @@ def _save(im, fp, filename):
# CMYK can be bigger
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:
# keep sets quality to -1, but the actual value may be high.
elif quality >= 95 or quality == -1:
bufsize = 2 * im.size[0] * im.size[1]
else:
bufsize = im.size[0] * im.size[1]
@ -772,9 +765,6 @@ def _save(im, fp, filename):
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:

View File

@ -33,7 +33,10 @@ Possible subsampling values are 0, 1 and 2 that correspond to 4:4:4, 4:2:2 and
4:2:0.
You can get the subsampling of a JPEG with the
`JpegImagePlugin.get_subsampling(im)` function.
`JpegImagePlugin.get_sampling(im)` function.
In JPEG compressed data a JPEG marker is used instead of an EXIF tag.
(ref.: https://www.exiv2.org/tags.html)
Quantization tables

View File

@ -20,10 +20,6 @@ import struct
from . import Image, ImageFile
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.2"
def _accept(s):
return s[:8] == b"\x00\x00\x00\x00\x00\x00\x00\x04"

View File

@ -21,11 +21,6 @@ import olefile
from . import Image, TiffImagePlugin
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.1"
#
# --------------------------------------------------------------------
@ -51,7 +46,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
try:
self.ole = olefile.OleFileIO(self.fp)
except IOError:
except OSError:
raise SyntaxError("not an MIC file; invalid OLE file")
# find ACI subfiles with Image members (maybe not the

View File

@ -17,16 +17,11 @@
from . import Image, ImageFile
from ._binary import i8
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.1"
#
# Bitstream parser
class BitStream(object):
class BitStream:
def __init__(self, fp):
self.fp = fp
self.bits = 0

View File

@ -21,10 +21,6 @@
from . import Image, ImageFile, JpegImagePlugin
from ._binary import i16be as i16
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.1"
def _accept(prefix):
return JpegImagePlugin._accept(prefix)
@ -86,7 +82,10 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
self.offset = self.__mpoffsets[frame]
self.fp.seek(self.offset + 2) # skip SOI marker
if i16(self.fp.read(2)) == 0xFFE1: # APP1
segment = self.fp.read(2)
if not segment:
raise ValueError("No data found for frame")
if i16(segment) == 0xFFE1: # APP1
n = i16(self.fp.read(2)) - 2
self.info["exif"] = ImageFile._safe_read(self.fp, n)

View File

@ -29,11 +29,6 @@ import struct
from . import Image, ImageFile
from ._binary import i8, i16le as i16, o16le as o16
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.1"
#
# read MSP files
@ -122,7 +117,7 @@ class MspDecoder(ImageFile.PyDecoder):
"<%dH" % (self.state.ysize), self.fd.read(self.state.ysize * 2)
)
except struct.error:
raise IOError("Truncated MSP file in row map")
raise OSError("Truncated MSP file in row map")
for x, rowlen in enumerate(rowmap):
try:
@ -131,7 +126,7 @@ class MspDecoder(ImageFile.PyDecoder):
continue
row = self.fd.read(rowlen)
if len(row) != rowlen:
raise IOError(
raise OSError(
"Truncated MSP file, expected %d bytes on row %s", (rowlen, x)
)
idx = 0
@ -148,7 +143,7 @@ class MspDecoder(ImageFile.PyDecoder):
idx += runcount
except struct.error:
raise IOError("Corrupted MSP file in row %d" % x)
raise OSError("Corrupted MSP file in row %d" % x)
self.set_as_raw(img.getvalue(), ("1", 0, 1))
@ -165,7 +160,7 @@ Image.register_decoder("MSP", MspDecoder)
def _save(im, fp, filename):
if im.mode != "1":
raise IOError("cannot write mode %s as MSP" % im.mode)
raise OSError("cannot write mode %s as MSP" % im.mode)
# create MSP header
header = [0] * 16

View File

@ -18,13 +18,12 @@
import sys
from . import EpsImagePlugin
from ._util import py3
##
# Simple Postscript graphics interface.
class PSDraw(object):
class PSDraw:
"""
Sets up printing to the given file. If **fp** is omitted,
:py:attr:`sys.stdout` is assumed.
@ -36,7 +35,7 @@ class PSDraw(object):
self.fp = fp
def _fp_write(self, to_write):
if not py3 or self.fp == sys.stdout:
if self.fp == sys.stdout:
self.fp.write(to_write)
else:
self.fp.write(bytes(to_write, "UTF-8"))
@ -72,7 +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-{} ISOLatin1Encoding /{} E\n".format(font, font))
self.isofont[font] = 1
# rough
self._fp_write("/F0 %d /PSDraw-%s F\n" % (size, font))
@ -120,8 +119,8 @@ class PSDraw(object):
else:
dpi = 100 # greyscale
# image size (on paper)
x = float(im.size[0] * 72) / dpi
y = float(im.size[1] * 72) / dpi
x = im.size[0] * 72 / dpi
y = im.size[1] * 72 / dpi
# max allowed size
xmax = float(box[2] - box[0])
ymax = float(box[3] - box[1])
@ -133,12 +132,12 @@ class PSDraw(object):
y = ymax
dx = (xmax - x) / 2 + box[0]
dy = (ymax - y) / 2 + box[1]
self._fp_write("gsave\n%f %f translate\n" % (dx, dy))
self._fp_write("gsave\n{:f} {:f} translate\n".format(dx, dy))
if (x, y) != im.size:
# EpsImagePlugin._save prints the image at (0,0,xsize,ysize)
sx = x / im.size[0]
sy = y / im.size[1]
self._fp_write("%f %f scale\n" % (sx, sy))
self._fp_write("{:f} {:f} scale\n".format(sx, sy))
EpsImagePlugin._save(im, self.fp, None, 0)
self._fp_write("\ngrestore\n")

View File

@ -19,7 +19,7 @@ from ._binary import o8
# File handler for Teragon-style palette files.
class PaletteFile(object):
class PaletteFile:
rawmode = "RGB"

View File

@ -10,10 +10,6 @@
from . import Image, ImageFile
from ._binary import o8, o16be as o16b
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "1.0"
# fmt: off
_Palm8BitColormapValues = ( # noqa: E131
(255, 255, 255), (255, 204, 255), (255, 153, 255), (255, 102, 255),
@ -141,7 +137,7 @@ def _save(im, fp, filename):
bpp = im.info["bpp"]
im = im.point(lambda x, maxval=(1 << bpp) - 1: maxval - (x & maxval))
else:
raise IOError("cannot write mode %s as Palm" % im.mode)
raise OSError("cannot write mode %s as Palm" % im.mode)
# we ignore the palette here
im.mode = "P"
@ -157,7 +153,7 @@ def _save(im, fp, filename):
else:
raise IOError("cannot write mode %s as Palm" % im.mode)
raise OSError("cannot write mode %s as Palm" % im.mode)
#
# make sure image data is available

View File

@ -18,11 +18,6 @@
from . import Image, ImageFile
from ._binary import i8
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.1"
##
# Image plugin for PhotoCD images. This plugin only reads the 768x512
# image from the file; higher resolutions are encoded in a proprietary

View File

@ -56,13 +56,15 @@ class PcfFontFile(FontFile.FontFile):
name = "name"
def __init__(self, fp):
def __init__(self, fp, charset_encoding="iso8859-1"):
self.charset_encoding = charset_encoding
magic = l32(fp.read(4))
if magic != PCF_MAGIC:
raise SyntaxError("not a PCF file")
FontFile.FontFile.__init__(self)
super().__init__()
count = l32(fp.read(4))
self.toc = {}
@ -184,7 +186,7 @@ class PcfFontFile(FontFile.FontFile):
nbitmaps = i32(fp.read(4))
if nbitmaps != len(metrics):
raise IOError("Wrong number of bitmaps")
raise OSError("Wrong number of bitmaps")
offsets = []
for i in range(nbitmaps):
@ -229,12 +231,17 @@ class PcfFontFile(FontFile.FontFile):
nencoding = (lastCol - firstCol + 1) * (lastRow - firstRow + 1)
for i in range(nencoding):
encodingOffset = i16(fp.read(2))
if encodingOffset != 0xFFFF:
try:
encoding[i + firstCol] = encodingOffset
except IndexError:
break # only load ISO-8859-1 glyphs
encodingOffsets = [i16(fp.read(2)) for _ in range(nencoding)]
for i in range(firstCol, len(encoding)):
try:
encodingOffset = encodingOffsets[
ord(bytearray([i]).decode(self.charset_encoding))
]
if encodingOffset != 0xFFFF:
encoding[i] = encodingOffset
except UnicodeDecodeError:
# character is not supported in selected encoding
pass
return encoding

View File

@ -33,10 +33,6 @@ from ._binary import i8, i16le as i16, o8, o16le as o16
logger = logging.getLogger(__name__)
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.6"
def _accept(prefix):
return i8(prefix[0]) == 10 and i8(prefix[1]) in [0, 2, 3, 5]
@ -107,7 +103,7 @@ class PcxImageFile(ImageFile.ImageFile):
rawmode = "RGB;L"
else:
raise IOError("unknown PCX mode")
raise OSError("unknown PCX mode")
self.mode = mode
self._size = bbox[2] - bbox[0], bbox[3] - bbox[1]

View File

@ -24,12 +24,7 @@ import io
import os
import time
from . import Image, ImageFile, ImageSequence, PdfParser
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.5"
from . import Image, ImageFile, ImageSequence, PdfParser, __version__
#
# --------------------------------------------------------------------
@ -82,7 +77,7 @@ def _save(im, fp, filename, save_all=False):
existing_pdf.start_writing()
existing_pdf.write_header()
existing_pdf.write_comment("created by PIL PDF driver " + __version__)
existing_pdf.write_comment("created by Pillow {} PDF driver".format(__version__))
#
# pages
@ -219,9 +214,9 @@ def _save(im, fp, filename, save_all=False):
#
# 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))
page_contents = b"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)

View File

@ -7,25 +7,6 @@ import re
import time
import zlib
from ._util import py3
try:
from UserDict import UserDict # Python 2.x
except ImportError:
UserDict = collections.UserDict # Python 3.x
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
# see 7.9.2.2 Text String Type on page 86 and D.3 PDFDocEncoding Character Set
# on page 656
@ -34,57 +15,55 @@ def encode_text(s):
PDFDocEncoding = {
0x16: u"\u0017",
0x18: u"\u02D8",
0x19: u"\u02C7",
0x1A: u"\u02C6",
0x1B: u"\u02D9",
0x1C: u"\u02DD",
0x1D: u"\u02DB",
0x1E: u"\u02DA",
0x1F: u"\u02DC",
0x80: u"\u2022",
0x81: u"\u2020",
0x82: u"\u2021",
0x83: u"\u2026",
0x84: u"\u2014",
0x85: u"\u2013",
0x86: u"\u0192",
0x87: u"\u2044",
0x88: u"\u2039",
0x89: u"\u203A",
0x8A: u"\u2212",
0x8B: u"\u2030",
0x8C: u"\u201E",
0x8D: u"\u201C",
0x8E: u"\u201D",
0x8F: u"\u2018",
0x90: u"\u2019",
0x91: u"\u201A",
0x92: u"\u2122",
0x93: u"\uFB01",
0x94: u"\uFB02",
0x95: u"\u0141",
0x96: u"\u0152",
0x97: u"\u0160",
0x98: u"\u0178",
0x99: u"\u017D",
0x9A: u"\u0131",
0x9B: u"\u0142",
0x9C: u"\u0153",
0x9D: u"\u0161",
0x9E: u"\u017E",
0xA0: u"\u20AC",
0x16: "\u0017",
0x18: "\u02D8",
0x19: "\u02C7",
0x1A: "\u02C6",
0x1B: "\u02D9",
0x1C: "\u02DD",
0x1D: "\u02DB",
0x1E: "\u02DA",
0x1F: "\u02DC",
0x80: "\u2022",
0x81: "\u2020",
0x82: "\u2021",
0x83: "\u2026",
0x84: "\u2014",
0x85: "\u2013",
0x86: "\u0192",
0x87: "\u2044",
0x88: "\u2039",
0x89: "\u203A",
0x8A: "\u2212",
0x8B: "\u2030",
0x8C: "\u201E",
0x8D: "\u201C",
0x8E: "\u201D",
0x8F: "\u2018",
0x90: "\u2019",
0x91: "\u201A",
0x92: "\u2122",
0x93: "\uFB01",
0x94: "\uFB02",
0x95: "\u0141",
0x96: "\u0152",
0x97: "\u0160",
0x98: "\u0178",
0x99: "\u017D",
0x9A: "\u0131",
0x9B: "\u0142",
0x9C: "\u0153",
0x9D: "\u0161",
0x9E: "\u017E",
0xA0: "\u20AC",
}
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")
elif py3: # Python 3.x
else:
return "".join(PDFDocEncoding.get(byte, chr(byte)) for byte in b)
else: # Python 2.x
return u"".join(PDFDocEncoding.get(ord(byte), byte) for byte in b)
class PdfFormatError(RuntimeError):
@ -196,10 +175,10 @@ class XrefTable:
else:
contiguous_keys = keys
keys = None
f.write(make_bytes("%d %d\n" % (contiguous_keys[0], len(contiguous_keys))))
f.write(b"%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(b"%010d %05d n \n" % self.new_entries[object_id])
else:
this_deleted_object_id = deleted_keys.pop(0)
check_format_condition(
@ -212,10 +191,8 @@ class XrefTable:
except IndexError:
next_in_linked_list = 0
f.write(
make_bytes(
"%010d %05d f \n"
% (next_in_linked_list, self.deleted_entries[object_id])
)
b"%010d %05d f \n"
% (next_in_linked_list, self.deleted_entries[object_id])
)
return startxref
@ -247,40 +224,27 @@ class PdfName:
def from_pdf_stream(cls, data):
return cls(PdfParser.interpret_name(data))
allowed_chars = set(range(33, 127)) - set(ord(c) for c in "#%/()<>[]{}")
allowed_chars = set(range(33, 127)) - {ord(c) for c in "#%/()<>[]{}"}
def __bytes__(self):
result = bytearray(b"/")
for b in self.name:
if py3: # Python 3.x
if b in self.allowed_chars:
result.append(b)
else:
result.extend(make_bytes("#%02X" % b))
else: # Python 2.x
if ord(b) in self.allowed_chars:
result.append(b)
else:
result.extend(b"#%02X" % ord(b))
if b in self.allowed_chars:
result.append(b)
else:
result.extend(b"#%02X" % b)
return bytes(result)
__str__ = __bytes__
class PdfArray(list):
def __bytes__(self):
return b"[ " + b" ".join(pdf_repr(x) for x in self) + b" ]"
__str__ = __bytes__
class PdfDict(UserDict):
class PdfDict(collections.UserDict):
def __setattr__(self, key, value):
if key == "data":
if hasattr(UserDict, "__setattr__"):
UserDict.__setattr__(self, key, value)
else:
self.__dict__[key] = value
collections.UserDict.__setattr__(self, key, value)
else:
self[key.encode("us-ascii")] = value
@ -324,23 +288,13 @@ class PdfDict(UserDict):
out.extend(b"\n>>")
return bytes(out)
if not py3:
__str__ = __bytes__
class PdfBinary:
def __init__(self, data):
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)
def __bytes__(self):
return b"<%s>" % b"".join(b"%02X" % b for b in self.data)
class PdfStream:
@ -382,9 +336,7 @@ def pdf_repr(x):
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 isinstance(x, str):
return pdf_repr(encode_text(x))
elif isinstance(x, bytes):
# XXX escape more chars? handle binary garbage
@ -471,7 +423,7 @@ class PdfParser:
self.f.write(b"%PDF-1.4\n")
def write_comment(self, s):
self.f.write(("%% %s\n" % (s,)).encode("utf-8"))
self.f.write(("% {}\n".format(s)).encode("utf-8"))
def write_catalog(self):
self.del_root()
@ -533,7 +485,7 @@ class PdfParser:
self.f.write(
b"trailer\n"
+ bytes(PdfDict(trailer_dict))
+ make_bytes("\nstartxref\n%d\n%%%%EOF" % start_xref)
+ b"\nstartxref\n%d\n%%%%EOF" % start_xref
)
def write_page(self, ref, *objs, **dict_obj):

View File

@ -22,11 +22,6 @@
from . import Image, ImageFile
from ._binary import i16le as i16
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.1"
#
# helpers

View File

@ -31,18 +31,15 @@
# See the README file for information on usage and redistribution.
#
import itertools
import logging
import re
import struct
import warnings
import zlib
from . import Image, ImageFile, ImagePalette
from ._binary import i8, i16be as i16, i32be as i32, o16be as o16, o32be as o32
from ._util import py3
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.9"
from . import Image, ImageChops, ImageFile, ImagePalette, ImageSequence
from ._binary import i8, i16be as i16, i32be as i32, o8, o16be as o16, o32be as o32
logger = logging.getLogger(__name__)
@ -86,6 +83,16 @@ MAX_TEXT_CHUNK = ImageFile.SAFEBLOCK
MAX_TEXT_MEMORY = 64 * MAX_TEXT_CHUNK
# APNG frame disposal modes
APNG_DISPOSE_OP_NONE = 0
APNG_DISPOSE_OP_BACKGROUND = 1
APNG_DISPOSE_OP_PREVIOUS = 2
# APNG frame blend modes
APNG_BLEND_OP_SOURCE = 0
APNG_BLEND_OP_OVER = 1
def _safe_zlib_decompress(s):
dobj = zlib.decompressobj()
plaintext = dobj.decompress(s, MAX_TEXT_CHUNK)
@ -102,7 +109,7 @@ def _crc32(data, seed=0):
# Support classes. Suitable for PNG and related formats like MNG etc.
class ChunkStream(object):
class ChunkStream:
def __init__(self, fp):
self.fp = fp
@ -180,7 +187,7 @@ class ChunkStream(object):
try:
cid, pos, length = self.read()
except struct.error:
raise IOError("truncated PNG file")
raise OSError("truncated PNG file")
if cid == endchunk:
break
@ -212,7 +219,7 @@ class iTXt(str):
return self
class PngInfo(object):
class PngInfo:
"""
PNG chunk container (for use with save(pnginfo=))
@ -293,8 +300,7 @@ class PngInfo(object):
class PngStream(ChunkStream):
def __init__(self, fp):
ChunkStream.__init__(self, fp)
super().__init__(fp)
# local copies of Image attributes
self.im_info = {}
@ -304,6 +310,9 @@ class PngStream(ChunkStream):
self.im_tile = None
self.im_palette = None
self.im_custom_mimetype = None
self.im_n_frames = None
self._seq_num = None
self.rewind_state = None
self.text_memory = 0
@ -315,6 +324,18 @@ class PngStream(ChunkStream):
% self.text_memory
)
def save_rewind(self):
self.rewind_state = {
"info": self.im_info.copy(),
"tile": self.im_tile,
"seq_num": self._seq_num,
}
def rewind(self):
self.im_info = self.rewind_state["info"]
self.im_tile = self.rewind_state["tile"]
self._seq_num = self.rewind_state["seq_num"]
def chunk_iCCP(self, pos, length):
# ICC profile
@ -362,7 +383,13 @@ class PngStream(ChunkStream):
def chunk_IDAT(self, pos, length):
# image data
self.im_tile = [("zip", (0, 0) + self.im_size, pos, self.im_rawmode)]
if "bbox" in self.im_info:
tile = [("zip", self.im_info["bbox"], pos, self.im_rawmode)]
else:
if self.im_n_frames is not None:
self.im_info["default_image"] = True
tile = [("zip", (0, 0) + self.im_size, pos, self.im_rawmode)]
self.im_tile = tile
self.im_idat = length
raise EOFError
@ -450,9 +477,8 @@ class PngStream(ChunkStream):
k = s
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))
@ -487,9 +513,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))
@ -524,14 +549,13 @@ class PngStream(ChunkStream):
return s
else:
return s
if py3:
try:
k = k.decode("latin-1", "strict")
lang = lang.decode("utf-8", "strict")
tk = tk.decode("utf-8", "strict")
v = v.decode("utf-8", "strict")
except UnicodeError:
return s
try:
k = k.decode("latin-1", "strict")
lang = lang.decode("utf-8", "strict")
tk = tk.decode("utf-8", "strict")
v = v.decode("utf-8", "strict")
except UnicodeError:
return s
self.im_info[k] = self.im_text[k] = iTXt(v, lang, tk)
self.check_text_memory(len(v))
@ -546,9 +570,49 @@ class PngStream(ChunkStream):
# APNG chunks
def chunk_acTL(self, pos, length):
s = ImageFile._safe_read(self.fp, length)
if self.im_n_frames is not None:
self.im_n_frames = None
warnings.warn("Invalid APNG, will use default PNG image if possible")
return s
n_frames = i32(s)
if n_frames == 0 or n_frames > 0x80000000:
warnings.warn("Invalid APNG, will use default PNG image if possible")
return s
self.im_n_frames = n_frames
self.im_info["loop"] = i32(s[4:])
self.im_custom_mimetype = "image/apng"
return s
def chunk_fcTL(self, pos, length):
s = ImageFile._safe_read(self.fp, length)
seq = i32(s)
if (self._seq_num is None and seq != 0) or (
self._seq_num is not None and self._seq_num != seq - 1
):
raise SyntaxError("APNG contains frame sequence errors")
self._seq_num = seq
width, height = i32(s[4:]), i32(s[8:])
px, py = i32(s[12:]), i32(s[16:])
im_w, im_h = self.im_size
if px + width > im_w or py + height > im_h:
raise SyntaxError("APNG contains invalid frames")
self.im_info["bbox"] = (px, py, px + width, py + height)
delay_num, delay_den = i16(s[20:]), i16(s[22:])
if delay_den == 0:
delay_den = 100
self.im_info["duration"] = float(delay_num) / float(delay_den) * 1000
self.im_info["disposal"] = i8(s[24])
self.im_info["blend"] = i8(s[25])
return s
def chunk_fdAT(self, pos, length):
s = ImageFile._safe_read(self.fp, 4)
seq = i32(s)
if self._seq_num != seq - 1:
raise SyntaxError("APNG contains frame sequence errors")
self._seq_num = seq
return self.chunk_IDAT(pos + 4, length - 4)
# --------------------------------------------------------------------
# PNG reader
@ -571,9 +635,11 @@ class PngImageFile(ImageFile.ImageFile):
if self.fp.read(8) != _MAGIC:
raise SyntaxError("not a PNG file")
self.__fp = self.fp
self.__frame = 0
#
# Parse headers up to the first IDAT chunk
# Parse headers up to the first IDAT or fDAT chunk
self.png = PngStream(self.fp)
@ -607,12 +673,28 @@ class PngImageFile(ImageFile.ImageFile):
self._text = None
self.tile = self.png.im_tile
self.custom_mimetype = self.png.im_custom_mimetype
self.n_frames = self.png.im_n_frames or 1
self.default_image = self.info.get("default_image", False)
if self.png.im_palette:
rawmode, data = self.png.im_palette
self.palette = ImagePalette.raw(rawmode, data)
self.__prepare_idat = length # used by load_prepare()
if cid == b"fdAT":
self.__prepare_idat = length - 4
else:
self.__prepare_idat = length # used by load_prepare()
if self.png.im_n_frames is not None:
self._close_exclusive_fp_after_loading = False
self.png.save_rewind()
self.__rewind_idat = self.__prepare_idat
self.__rewind = self.__fp.tell()
if self.default_image:
# IDAT chunk contains default image and not first animation frame
self.n_frames += 1
self._seek(0)
self.is_animated = self.n_frames > 1
@property
def text(self):
@ -620,7 +702,13 @@ class PngImageFile(ImageFile.ImageFile):
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
if self.is_animated:
frame = self.__frame
# for APNG, seek to the final frame before loading
self.seek(self.n_frames - 1)
self.load()
if self.is_animated:
self.seek(frame)
return self._text
def verify(self):
@ -639,6 +727,97 @@ class PngImageFile(ImageFile.ImageFile):
self.fp.close()
self.fp = None
def seek(self, frame):
if not self._seek_check(frame):
return
if frame < self.__frame:
self._seek(0, True)
last_frame = self.__frame
for f in range(self.__frame + 1, frame + 1):
try:
self._seek(f)
except EOFError:
self.seek(last_frame)
raise EOFError("no more images in APNG file")
def _seek(self, frame, rewind=False):
if frame == 0:
if rewind:
self.__fp.seek(self.__rewind)
self.png.rewind()
self.__prepare_idat = self.__rewind_idat
self.im = None
if self.pyaccess:
self.pyaccess = None
self.info = self.png.im_info
self.tile = self.png.im_tile
self.fp = self.__fp
self._prev_im = None
self.dispose = None
self.default_image = self.info.get("default_image", False)
self.dispose_op = self.info.get("disposal")
self.blend_op = self.info.get("blend")
self.dispose_extent = self.info.get("bbox")
self.__frame = 0
return
else:
if frame != self.__frame + 1:
raise ValueError("cannot seek to frame %d" % frame)
# ensure previous frame was loaded
self.load()
self.fp = self.__fp
# advance to the next frame
if self.__prepare_idat:
ImageFile._safe_read(self.fp, self.__prepare_idat)
self.__prepare_idat = 0
frame_start = False
while True:
self.fp.read(4) # CRC
try:
cid, pos, length = self.png.read()
except (struct.error, SyntaxError):
break
if cid == b"IEND":
raise EOFError("No more images in APNG file")
if cid == b"fcTL":
if frame_start:
# there must be at least one fdAT chunk between fcTL chunks
raise SyntaxError("APNG missing frame data")
frame_start = True
try:
self.png.call(cid, pos, length)
except UnicodeDecodeError:
break
except EOFError:
if cid == b"fdAT":
length -= 4
if frame_start:
self.__prepare_idat = length
break
ImageFile._safe_read(self.fp, length)
except AttributeError:
logger.debug("%r %s %s (unknown)", cid, pos, length)
ImageFile._safe_read(self.fp, length)
self.__frame = frame
self.tile = self.png.im_tile
self.dispose_op = self.info.get("disposal")
self.blend_op = self.info.get("blend")
self.dispose_extent = self.info.get("bbox")
if not self.tile:
raise EOFError
def tell(self):
return self.__frame
def load_prepare(self):
"""internal: prepare to read PNG file"""
@ -658,11 +837,18 @@ class PngImageFile(ImageFile.ImageFile):
cid, pos, length = self.png.read()
if cid not in [b"IDAT", b"DDAT"]:
if cid not in [b"IDAT", b"DDAT", b"fdAT"]:
self.png.push(cid, pos, length)
return b""
self.__idat = length # empty chunks are allowed
if cid == b"fdAT":
try:
self.png.call(cid, pos, length)
except EOFError:
pass
self.__idat = length - 4 # sequence_num has already been read
else:
self.__idat = length # empty chunks are allowed
# read more data from this chunk
if read_bytes <= 0:
@ -686,31 +872,84 @@ class PngImageFile(ImageFile.ImageFile):
if cid == b"IEND":
break
elif cid == b"fcTL" and self.is_animated:
# start of the next frame, stop reading
self.__prepare_idat = 0
self.png.push(cid, pos, length)
break
try:
self.png.call(cid, pos, length)
except UnicodeDecodeError:
break
except EOFError:
if cid == b"fdAT":
length -= 4
ImageFile._safe_read(self.fp, length)
except AttributeError:
logger.debug("%r %s %s (unknown)", cid, pos, length)
ImageFile._safe_read(self.fp, length)
self._text = self.png.im_text
self.png.close()
self.png = None
if not self.is_animated:
self.png.close()
self.png = None
else:
# setup frame disposal (actual disposal done when needed in _seek())
if self._prev_im is None and self.dispose_op == APNG_DISPOSE_OP_PREVIOUS:
self.dispose_op = APNG_DISPOSE_OP_BACKGROUND
if self.dispose_op == APNG_DISPOSE_OP_PREVIOUS:
dispose = self._prev_im.copy()
dispose = self._crop(dispose, self.dispose_extent)
elif self.dispose_op == APNG_DISPOSE_OP_BACKGROUND:
dispose = Image.core.fill("RGBA", self.size, (0, 0, 0, 0))
dispose = self._crop(dispose, self.dispose_extent)
else:
dispose = None
if self._prev_im and self.blend_op == APNG_BLEND_OP_OVER:
updated = self._crop(self.im, self.dispose_extent)
self._prev_im.paste(
updated, self.dispose_extent, updated.convert("RGBA")
)
self.im = self._prev_im
if self.pyaccess:
self.pyaccess = None
self._prev_im = self.im.copy()
if dispose:
self._prev_im.paste(dispose, self.dispose_extent)
def _getexif(self):
if "exif" not in self.info:
self.load()
if "exif" not in self.info:
if "exif" not in self.info and "Raw profile type exif" not in self.info:
return None
return dict(self.getexif())
def getexif(self):
if "exif" not in self.info:
self.load()
return ImageFile.ImageFile.getexif(self)
if self._exif is None:
self._exif = Image.Exif()
exif_info = self.info.get("exif")
if exif_info is None and "Raw profile type exif" in self.info:
exif_info = bytes.fromhex(
"".join(self.info["Raw profile type exif"].split("\n")[3:])
)
self._exif.load(exif_info)
return self._exif
def _close__fp(self):
try:
if self.__fp != self.fp:
self.__fp.close()
except AttributeError:
pass
finally:
self.__fp = None
# --------------------------------------------------------------------
@ -746,7 +985,7 @@ def putchunk(fp, cid, *data):
fp.write(o32(crc))
class _idat(object):
class _idat:
# wrap output from the encoder in IDAT chunks
def __init__(self, fp, chunk):
@ -757,7 +996,147 @@ class _idat(object):
self.chunk(self.fp, b"IDAT", data)
def _save(im, fp, filename, chunk=putchunk):
class _fdat:
# wrap encoder output in fdAT chunks
def __init__(self, fp, chunk, seq_num):
self.fp = fp
self.chunk = chunk
self.seq_num = seq_num
def write(self, data):
self.chunk(self.fp, b"fdAT", o32(self.seq_num), data)
self.seq_num += 1
def _write_multiple_frames(im, fp, chunk, rawmode):
default_image = im.encoderinfo.get("default_image", im.info.get("default_image"))
duration = im.encoderinfo.get("duration", im.info.get("duration", 0))
loop = im.encoderinfo.get("loop", im.info.get("loop", 0))
disposal = im.encoderinfo.get("disposal", im.info.get("disposal"))
blend = im.encoderinfo.get("blend", im.info.get("blend"))
if default_image:
chain = itertools.chain(im.encoderinfo.get("append_images", []))
else:
chain = itertools.chain([im], im.encoderinfo.get("append_images", []))
im_frames = []
frame_count = 0
for im_seq in chain:
for im_frame in ImageSequence.Iterator(im_seq):
im_frame = im_frame.copy()
if im_frame.mode != im.mode:
if im.mode == "P":
im_frame = im_frame.convert(im.mode, palette=im.palette)
else:
im_frame = im_frame.convert(im.mode)
encoderinfo = im.encoderinfo.copy()
if isinstance(duration, (list, tuple)):
encoderinfo["duration"] = duration[frame_count]
if isinstance(disposal, (list, tuple)):
encoderinfo["disposal"] = disposal[frame_count]
if isinstance(blend, (list, tuple)):
encoderinfo["blend"] = blend[frame_count]
frame_count += 1
if im_frames:
previous = im_frames[-1]
prev_disposal = previous["encoderinfo"].get("disposal")
prev_blend = previous["encoderinfo"].get("blend")
if prev_disposal == APNG_DISPOSE_OP_PREVIOUS and len(im_frames) < 2:
prev_disposal == APNG_DISPOSE_OP_BACKGROUND
if prev_disposal == APNG_DISPOSE_OP_BACKGROUND:
base_im = previous["im"]
dispose = Image.core.fill("RGBA", im.size, (0, 0, 0, 0))
bbox = previous["bbox"]
if bbox:
dispose = dispose.crop(bbox)
else:
bbox = (0, 0) + im.size
base_im.paste(dispose, bbox)
elif prev_disposal == APNG_DISPOSE_OP_PREVIOUS:
base_im = im_frames[-2]["im"]
else:
base_im = previous["im"]
delta = ImageChops.subtract_modulo(
im_frame.convert("RGB"), base_im.convert("RGB")
)
bbox = delta.getbbox()
if (
not bbox
and prev_disposal == encoderinfo.get("disposal")
and prev_blend == encoderinfo.get("blend")
):
duration = encoderinfo.get("duration", 0)
if duration:
if "duration" in previous["encoderinfo"]:
previous["encoderinfo"]["duration"] += duration
else:
previous["encoderinfo"]["duration"] = duration
continue
else:
bbox = None
im_frames.append({"im": im_frame, "bbox": bbox, "encoderinfo": encoderinfo})
# animation control
chunk(
fp, b"acTL", o32(len(im_frames)), o32(loop), # 0: num_frames # 4: num_plays
)
# default image IDAT (if it exists)
if default_image:
ImageFile._save(im, _idat(fp, chunk), [("zip", (0, 0) + im.size, 0, rawmode)])
seq_num = 0
for frame, frame_data in enumerate(im_frames):
im_frame = frame_data["im"]
if not frame_data["bbox"]:
bbox = (0, 0) + im_frame.size
else:
bbox = frame_data["bbox"]
im_frame = im_frame.crop(bbox)
size = im_frame.size
duration = int(round(frame_data["encoderinfo"].get("duration", 0)))
disposal = frame_data["encoderinfo"].get("disposal", APNG_DISPOSE_OP_NONE)
blend = frame_data["encoderinfo"].get("blend", APNG_BLEND_OP_SOURCE)
# frame control
chunk(
fp,
b"fcTL",
o32(seq_num), # sequence_number
o32(size[0]), # width
o32(size[1]), # height
o32(bbox[0]), # x_offset
o32(bbox[1]), # y_offset
o16(duration), # delay_numerator
o16(1000), # delay_denominator
o8(disposal), # dispose_op
o8(blend), # blend_op
)
seq_num += 1
# frame data
if frame == 0 and not default_image:
# first frame must be in IDAT chunks for backwards compatibility
ImageFile._save(
im_frame,
_idat(fp, chunk),
[("zip", (0, 0) + im_frame.size, 0, rawmode)],
)
else:
fdat_chunks = _fdat(fp, chunk, seq_num)
ImageFile._save(
im_frame, fdat_chunks, [("zip", (0, 0) + im_frame.size, 0, rawmode)],
)
seq_num = fdat_chunks.seq_num
def _save_all(im, fp, filename):
_save(im, fp, filename, save_all=True)
def _save(im, fp, filename, chunk=putchunk, save_all=False):
# save an image to disk (called by the save method)
mode = im.mode
@ -799,7 +1178,7 @@ def _save(im, fp, filename, chunk=putchunk):
try:
rawmode, mode = _OUTMODES[mode]
except KeyError:
raise IOError("cannot write mode %s as PNG" % mode)
raise OSError("cannot write mode %s as PNG" % mode)
#
# write minimal PNG file
@ -874,7 +1253,7 @@ def _save(im, fp, filename, chunk=putchunk):
if "transparency" in im.encoderinfo:
# don't bother with transparency if it's an RGBA
# and it's in the info dict. It's probably just stale.
raise IOError("cannot use transparency for this mode")
raise OSError("cannot use transparency for this mode")
else:
if im.mode == "P" and im.im.getpalettemode() == "RGBA":
alpha = im.im.getpalette("RGBA", "A")
@ -891,7 +1270,6 @@ def _save(im, fp, filename, chunk=putchunk):
b"\x01",
)
info = im.encoderinfo.get("pnginfo")
if info:
chunks = [b"bKGD", b"hIST"]
for cid, data in info.chunks:
@ -907,7 +1285,10 @@ 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)])
if save_all:
_write_multiple_frames(im, fp, chunk, rawmode)
else:
ImageFile._save(im, _idat(fp, chunk), [("zip", (0, 0) + im.size, 0, rawmode)])
chunk(fp, b"IEND", b"")
@ -922,7 +1303,7 @@ def _save(im, fp, filename, chunk=putchunk):
def getchunks(im, **params):
"""Return a list of PNG chunks representing this image."""
class collector(object):
class collector:
data = []
def write(self, data):
@ -952,6 +1333,7 @@ def getchunks(im, **params):
Image.register_open(PngImageFile.format, PngImageFile, _accept)
Image.register_save(PngImageFile.format, _save)
Image.register_save_all(PngImageFile.format, _save_all)
Image.register_extensions(PngImageFile.format, [".png", ".apng"])

View File

@ -17,10 +17,6 @@
from . import Image, ImageFile
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.2"
#
# --------------------------------------------------------------------
@ -139,7 +135,7 @@ def _save(im, fp, filename):
elif im.mode == "RGBA":
rawmode, head = "RGB", b"P6"
else:
raise IOError("cannot write mode %s as PPM" % im.mode)
raise OSError("cannot write mode %s as PPM" % im.mode)
fp.write(head + ("\n%d %d\n" % im.size).encode("ascii"))
if head == b"P6":
fp.write(b"255\n")

View File

@ -16,10 +16,6 @@
# See the README file for information on usage and redistribution.
#
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.4"
import io
from . import Image, ImageFile, ImagePalette
@ -75,7 +71,7 @@ class PsdImageFile(ImageFile.ImageFile):
mode, channels = MODES[(psd_mode, psd_bits)]
if channels > psd_channels:
raise IOError("not enough channels")
raise OSError("not enough channels")
self.mode = mode
self._size = i32(s[18:]), i32(s[14:])

View File

@ -40,7 +40,7 @@ ffi = FFI()
ffi.cdef(defs)
class PyAccess(object):
class PyAccess:
def __init__(self, img, readonly=False):
vals = dict(img.im.unsafe_ptrs)
self.readonly = readonly

View File

@ -27,11 +27,6 @@ import struct
from . import Image, ImageFile
from ._binary import i8, i16be as i16, o8
from ._util import py3
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.3"
def _accept(prefix):
@ -164,7 +159,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()))
"incorrect number of bands in SGI write: {} vs {}".format(
z, len(im.getbands())
)
)
# Minimum Byte value
@ -173,8 +170,7 @@ def _save(im, fp, filename):
pinmax = 255
# 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))

View File

@ -32,9 +32,6 @@
# Details about the Spider image format:
# https://spider.wadsworth.org/spider_doc/spider/docs/image_doc.html
#
from __future__ import print_function
import os
import struct
import sys
@ -219,7 +216,8 @@ def loadImageSeries(filelist=None):
print("unable to find %s" % img)
continue
try:
im = Image.open(img).convert2byte()
with Image.open(img) as im:
im = im.convert2byte()
except Exception:
if not isSpiderImage(img):
print(img + " is not a Spider image file")
@ -273,7 +271,7 @@ def _save(im, fp, filename):
hdr = makeSpiderHeader(im)
if len(hdr) < 256:
raise IOError("Error creating Spider header")
raise OSError("Error creating Spider header")
# write the SPIDER header
fp.writelines(hdr)
@ -306,21 +304,21 @@ if __name__ == "__main__":
print("input image must be in Spider format")
sys.exit()
im = Image.open(filename)
print("image: " + str(im))
print("format: " + str(im.format))
print("size: " + str(im.size))
print("mode: " + str(im.mode))
print("max, min: ", end=" ")
print(im.getextrema())
with Image.open(filename) as im:
print("image: " + str(im))
print("format: " + str(im.format))
print("size: " + str(im.size))
print("mode: " + str(im.mode))
print("max, min: ", end=" ")
print(im.getextrema())
if len(sys.argv) > 2:
outfile = sys.argv[2]
if len(sys.argv) > 2:
outfile = sys.argv[2]
# 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)
)
im.save(outfile, SpiderImageFile.format)
# 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)
)
im.save(outfile, SpiderImageFile.format)

View File

@ -20,10 +20,6 @@
from . import Image, ImageFile, ImagePalette
from ._binary import i32be as i32
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.3"
def _accept(prefix):
return len(prefix) >= 4 and i32(prefix) == 0x59A66A95

View File

@ -15,7 +15,6 @@
#
import io
import sys
from . import ContainerIO
@ -38,12 +37,12 @@ class TarIO(ContainerIO.ContainerIO):
s = self.fh.read(512)
if len(s) != 512:
raise IOError("unexpected end of tar file")
raise OSError("unexpected end of tar file")
name = s[:100].decode("utf-8")
i = name.find("\0")
if i == 0:
raise IOError("cannot find subfile")
raise OSError("cannot find subfile")
if i > 0:
name = name[:i]
@ -55,7 +54,7 @@ class TarIO(ContainerIO.ContainerIO):
self.fh.seek((size + 511) & (~511), io.SEEK_CUR)
# Open region
ContainerIO.ContainerIO.__init__(self, self.fh, self.fh.tell(), size)
super().__init__(self.fh, self.fh.tell(), size)
# Context manager support
def __enter__(self):
@ -64,10 +63,5 @@ class TarIO(ContainerIO.ContainerIO):
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

@ -22,11 +22,6 @@ import warnings
from . import Image, ImageFile, ImagePalette
from ._binary import i8, i16le as i16, o8, o16le as o16
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.3"
#
# --------------------------------------------------------------------
# Read RGA file
@ -173,7 +168,7 @@ def _save(im, fp, filename):
try:
rawmode, bits, colormaptype, imagetype = SAVE[im.mode]
except KeyError:
raise IOError("cannot write mode %s as TGA" % im.mode)
raise OSError("cannot write mode %s as TGA" % im.mode)
if "rle" in im.encoderinfo:
rle = im.encoderinfo["rle"]

View File

@ -38,35 +38,19 @@
#
# See the README file for information on usage and redistribution.
#
from __future__ import division, print_function
import distutils.version
import io
import itertools
import os
import struct
import sys
import warnings
from collections.abc import MutableMapping
from fractions import Fraction
from numbers import Number, Rational
from . import Image, ImageFile, ImagePalette, TiffTags
from ._binary import i8, o8
from ._util import py3
from .TiffTags import TYPES
try:
# Python 3
from collections.abc import MutableMapping
except ImportError:
# Python 2.7
from collections import MutableMapping
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "1.3.5"
DEBUG = False # Needs to be merged with the new logging approach.
# Set these to true to force use of libtiff for reading or writing.
@ -279,8 +263,18 @@ 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]
def _limit_signed_rational(val, max_val, min_val):
frac = Fraction(val)
n_d = frac.numerator, frac.denominator
if min(n_d) < min_val:
n_d = _limit_rational(val, abs(min_val))
if max(n_d) > max_val:
val = Fraction(*n_d)
n_d = _limit_rational(val, max_val)
return n_d
##
@ -310,25 +304,21 @@ class IFDRational(Rational):
float/rational/other number, or an IFDRational
:param denominator: Optional integer denominator
"""
self._denominator = denominator
self._numerator = value
self._val = float(1)
if isinstance(value, IFDRational):
self._numerator = value.numerator
self._denominator = value.denominator
self._val = value._val
return
if isinstance(value, Fraction):
self._numerator = value.numerator
self._denominator = value.denominator
self._val = value
if isinstance(value, IFDRational):
self._denominator = value.denominator
self._numerator = value.numerator
self._val = value._val
return
else:
self._numerator = value
self._denominator = denominator
if denominator == 0:
self._val = float("nan")
return
elif denominator == 1:
self._val = Fraction(value)
else:
@ -370,10 +360,10 @@ class IFDRational(Rational):
return delegate
""" a = ['add','radd', 'sub', 'rsub','div', 'rdiv', 'mul', 'rmul',
'truediv', 'rtruediv', 'floordiv',
'rfloordiv','mod','rmod', 'pow','rpow', 'pos', 'neg',
'abs', 'trunc', 'lt', 'gt', 'le', 'ge', 'nonzero',
""" a = ['add','radd', 'sub', 'rsub', 'mul', 'rmul',
'truediv', 'rtruediv', 'floordiv', 'rfloordiv',
'mod','rmod', 'pow','rpow', 'pos', 'neg',
'abs', 'trunc', 'lt', 'gt', 'le', 'ge', 'bool',
'ceil', 'floor', 'round']
print("\n".join("__%s__ = _delegate('__%s__')" % (s,s) for s in a))
"""
@ -382,8 +372,6 @@ class IFDRational(Rational):
__radd__ = _delegate("__radd__")
__sub__ = _delegate("__sub__")
__rsub__ = _delegate("__rsub__")
__div__ = _delegate("__div__")
__rdiv__ = _delegate("__rdiv__")
__mul__ = _delegate("__mul__")
__rmul__ = _delegate("__rmul__")
__truediv__ = _delegate("__truediv__")
@ -402,7 +390,7 @@ class IFDRational(Rational):
__gt__ = _delegate("__gt__")
__le__ = _delegate("__le__")
__ge__ = _delegate("__ge__")
__nonzero__ = _delegate("__nonzero__")
__bool__ = _delegate("__bool__")
__ceil__ = _delegate("__ceil__")
__floor__ = _delegate("__floor__")
__round__ = _delegate("__round__")
@ -484,7 +472,7 @@ class ImageFileDirectory_v2(MutableMapping):
else:
raise SyntaxError("not a TIFF IFD")
self.reset()
self.next, = self._unpack("L", ifh[4:])
(self.next,) = self._unpack("L", ifh[4:])
self._legacy_api = False
prefix = property(lambda self: self._prefix)
@ -531,18 +519,11 @@ class ImageFileDirectory_v2(MutableMapping):
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
def __setitem__(self, tag, value):
self._setitem(tag, value, self.legacy_api)
def _setitem(self, tag, value, legacy_api):
basetypes = (Number, bytes, str)
if not py3:
basetypes += (unicode,) # noqa: F821
info = TiffTags.lookup(tag)
values = [value] if isinstance(value, basetypes) else value
@ -553,23 +534,29 @@ class ImageFileDirectory_v2(MutableMapping):
else:
self.tagtype[tag] = TiffTags.UNDEFINED
if all(isinstance(v, IFDRational) for v in values):
self.tagtype[tag] = TiffTags.RATIONAL
self.tagtype[tag] = (
TiffTags.RATIONAL
if all(v >= 0 for v in values)
else TiffTags.SIGNED_RATIONAL
)
elif all(isinstance(v, int) for v in values):
if all(v < 2 ** 16 for v in values):
if all(0 <= v < 2 ** 16 for v in values):
self.tagtype[tag] = TiffTags.SHORT
elif all(-(2 ** 15) < v < 2 ** 15 for v in values):
self.tagtype[tag] = TiffTags.SIGNED_SHORT
else:
self.tagtype[tag] = TiffTags.LONG
self.tagtype[tag] = (
TiffTags.LONG
if all(v >= 0 for v in values)
else TiffTags.SIGNED_LONG
)
elif all(isinstance(v, float) for v in values):
self.tagtype[tag] = TiffTags.DOUBLE
else:
if py3:
if all(isinstance(v, str) for v in values):
self.tagtype[tag] = TiffTags.ASCII
else:
# Never treat data as binary by default on Python 2.
if all(isinstance(v, str) for v in values):
self.tagtype[tag] = TiffTags.ASCII
if self.tagtype[tag] == TiffTags.UNDEFINED and py3:
if self.tagtype[tag] == TiffTags.UNDEFINED:
values = [
value.encode("ascii", "replace") if isinstance(value, str) else value
]
@ -595,7 +582,7 @@ class ImageFileDirectory_v2(MutableMapping):
]: # rationals
values = (values,)
try:
dest[tag], = values
(dest[tag],) = values
except ValueError:
# We've got a builtin tag with 1 expected entry
warnings.warn(
@ -689,8 +676,6 @@ class ImageFileDirectory_v2(MutableMapping):
@_register_writer(2)
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"
@_register_loader(5, 8)
@ -705,7 +690,7 @@ class ImageFileDirectory_v2(MutableMapping):
@_register_writer(5)
def write_rational(self, *values):
return b"".join(
self._pack("2L", *_limit_rational(frac, 2 ** 31)) for frac in values
self._pack("2L", *_limit_rational(frac, 2 ** 32 - 1)) for frac in values
)
@_register_loader(7, 1)
@ -728,13 +713,14 @@ class ImageFileDirectory_v2(MutableMapping):
@_register_writer(10)
def write_signed_rational(self, *values):
return b"".join(
self._pack("2L", *_limit_rational(frac, 2 ** 30)) for frac in values
self._pack("2l", *_limit_signed_rational(frac, 2 ** 31 - 1, -(2 ** 31)))
for frac in values
)
def _ensure_read(self, fp, size):
ret = fp.read(size)
if len(ret) != size:
raise IOError(
raise OSError(
"Corrupt EXIF data. "
+ "Expecting to read %d bytes but only got %d. " % (size, len(ret))
)
@ -765,10 +751,10 @@ class ImageFileDirectory_v2(MutableMapping):
size = count * unit_size
if size > 4:
here = fp.tell()
offset, = self._unpack("L", data)
(offset,) = self._unpack("L", data)
if DEBUG:
print(
"Tag Location: %s - Data Location: %s" % (here, offset),
"Tag Location: {} - Data Location: {}".format(here, offset),
end=" ",
)
fp.seek(offset)
@ -797,8 +783,8 @@ class ImageFileDirectory_v2(MutableMapping):
else:
print("- value:", self[tag])
self.next, = self._unpack("L", self._ensure_read(fp, 4))
except IOError as msg:
(self.next,) = self._unpack("L", self._ensure_read(fp, 4))
except OSError as msg:
warnings.warn(str(msg))
return
@ -817,7 +803,7 @@ class ImageFileDirectory_v2(MutableMapping):
stripoffsets = len(entries)
typ = self.tagtype.get(tag)
if DEBUG:
print("Tag %s, Type: %s, Value: %s" % (tag, typ, value))
print("Tag {}, Type: {}, Value: {}".format(tag, typ, value))
values = value if isinstance(value, tuple) else (value,)
data = self._write_dispatch[typ](self, *values)
if DEBUG:
@ -854,7 +840,7 @@ class ImageFileDirectory_v2(MutableMapping):
# pass 2: write entries to file
for tag, typ, count, value, data in entries:
if DEBUG > 1:
if DEBUG:
print(tag, typ, count, repr(value), repr(data))
result += self._pack("HHL4s", tag, typ, count, value)
@ -911,7 +897,7 @@ class ImageFileDirectory_v1(ImageFileDirectory_v2):
"""
def __init__(self, *args, **kwargs):
ImageFileDirectory_v2.__init__(self, *args, **kwargs)
super().__init__(*args, **kwargs)
self._legacy_api = True
tags = property(lambda self: self._tags_v1)
@ -1054,7 +1040,7 @@ class TiffImageFile(ImageFile.ImageFile):
"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
# reset buffered io handle in case fp
# was passed to libtiff, invalidating the buffer
self.fp.tell()
self.fp.seek(self.__next)
@ -1079,23 +1065,10 @@ class TiffImageFile(ImageFile.ImageFile):
"""Return the current frame number"""
return self.__frame
@property
def size(self):
return self._size
@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,
)
self._size = value
def load(self):
if self.use_load_libtiff:
return self._load_libtiff()
return super(TiffImageFile, self).load()
return super().load()
def load_end(self):
if self._tile_orientation:
@ -1124,14 +1097,14 @@ class TiffImageFile(ImageFile.ImageFile):
pixel = Image.Image.load(self)
if self.tile is None:
raise IOError("cannot load this image")
raise OSError("cannot load this image")
if not self.tile:
return pixel
self.load_prepare()
if not len(self.tile) == 1:
raise IOError("Not exactly one tile")
raise OSError("Not exactly one tile")
# (self._compression, (extents tuple),
# 0, (rawmode, self._compression, fp))
@ -1145,11 +1118,11 @@ class TiffImageFile(ImageFile.ImageFile):
try:
fp = hasattr(self.fp, "fileno") and os.dup(self.fp.fileno())
# flush the file descriptor, prevents error on pypy 2.4+
# should also eliminate the need for fp.tell for py3
# should also eliminate the need for fp.tell
# in _seek
if hasattr(self.fp, "flush"):
self.fp.flush()
except IOError:
except OSError:
# io.BytesIO have a fileno, but returns an IOError if
# it doesn't use a file descriptor.
fp = False
@ -1163,7 +1136,7 @@ class TiffImageFile(ImageFile.ImageFile):
try:
decoder.setimage(self.im, extents)
except ValueError:
raise IOError("Couldn't set the image")
raise OSError("Couldn't set the image")
close_self_fp = self._exclusive_fp and not self._is_animated
if hasattr(self.fp, "getvalue"):
@ -1206,7 +1179,7 @@ class TiffImageFile(ImageFile.ImageFile):
self.fp = None # might be shared
if err < 0:
raise IOError(err)
raise OSError(err)
return Image.Image.load(self)
@ -1214,7 +1187,7 @@ class TiffImageFile(ImageFile.ImageFile):
"""Setup this image object based on current tags"""
if 0xBC01 in self.tag_v2:
raise IOError("Windows Media Photo files not yet supported")
raise OSError("Windows Media Photo files not yet supported")
# extract relevant tags
self._compression = COMPRESSION_INFO[self.tag_v2.get(COMPRESSION, 1)]
@ -1456,7 +1429,7 @@ def _save(im, fp, filename):
try:
rawmode, prefix, photo, format, bits, extra = SAVE_INFO[im.mode]
except KeyError:
raise IOError("cannot write mode %s as TIFF" % im.mode)
raise OSError("cannot write mode %s as TIFF" % im.mode)
ifd = ImageFileDirectory_v2(prefix=prefix)
@ -1606,22 +1579,18 @@ def _save(im, fp, filename):
# Custom items are supported for int, float, unicode, string and byte
# values. Other types and tuples require a tagtype.
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"):
if (
TiffTags.lookup(tag).type == TiffTags.UNDEFINED
or not Image.core.libtiff_support_custom_tags
):
continue
if tag in ifd.tagtype:
types[tag] = ifd.tagtype[tag]
elif not (
isinstance(value, (int, float, str, bytes))
or (not py3 and isinstance(value, unicode)) # noqa: F821
):
elif not (isinstance(value, (int, float, str, bytes))):
continue
if tag not in atts and tag not in blocklist:
if isinstance(value, str if py3 else unicode): # noqa: F821
if isinstance(value, str):
atts[tag] = value.encode("ascii", "replace") + b"\0"
elif isinstance(value, IFDRational):
atts[tag] = float(value)
@ -1654,7 +1623,7 @@ def _save(im, fp, filename):
if s:
break
if s < 0:
raise IOError("encoder error %d when writing image file" % s)
raise OSError("encoder error %d when writing image file" % s)
else:
offset = ifd.save(fp)
@ -1702,9 +1671,9 @@ class AppendingTiffWriter:
self.name = fn
self.close_fp = True
try:
self.f = io.open(fn, "w+b" if new else "r+b")
except IOError:
self.f = io.open(fn, "w+b")
self.f = open(fn, "w+b" if new else "r+b")
except OSError:
self.f = open(fn, "w+b")
self.beginning = self.f.tell()
self.setup()
@ -1785,7 +1754,7 @@ class AppendingTiffWriter:
# pad to 16 byte boundary
padBytes = 16 - pos % 16
if 0 < padBytes < 16:
self.f.write(bytes(bytearray(padBytes)))
self.f.write(bytes(padBytes))
self.offsetOfNewPage = self.f.tell()
def setEndian(self, endian):
@ -1809,11 +1778,11 @@ class AppendingTiffWriter:
return self.f.write(data)
def readShort(self):
value, = struct.unpack(self.shortFmt, self.f.read(2))
(value,) = struct.unpack(self.shortFmt, self.f.read(2))
return value
def readLong(self):
value, = struct.unpack(self.longFmt, self.f.read(4))
(value,) = struct.unpack(self.longFmt, self.f.read(4))
return value
def rewriteLastShortToLong(self, value):

View File

@ -24,7 +24,7 @@ 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 {})
return super().__new__(cls, value, name, type, length, enum or {})
def cvt_enum(self, value):
# Using get will call hash(value), which can be expensive
@ -120,7 +120,7 @@ TAGS_V2 = {
277: ("SamplesPerPixel", SHORT, 1),
278: ("RowsPerStrip", LONG, 1),
279: ("StripByteCounts", LONG, 0),
280: ("MinSampleValue", LONG, 0),
280: ("MinSampleValue", SHORT, 0),
281: ("MaxSampleValue", SHORT, 0),
282: ("XResolution", RATIONAL, 1),
283: ("YResolution", RATIONAL, 1),
@ -182,7 +182,7 @@ TAGS_V2 = {
# FIXME add more tags here
34665: ("ExifIFD", LONG, 1),
34675: ("ICCProfile", UNDEFINED, 1),
34853: ("GPSInfoIFD", BYTE, 1),
34853: ("GPSInfoIFD", LONG, 1),
# MPInfo
45056: ("MPFVersion", UNDEFINED, 1),
45057: ("NumberOfImages", LONG, 1),

View File

@ -1,4 +1,3 @@
# encoding: utf-8
#
# The Python Imaging Library.
# $Id$
@ -21,16 +20,11 @@
# https://www.flipcode.com/archives/Quake_2_BSP_File_Format.shtml
# and has been tested with a few sample files found using google.
import builtins
from . import Image
from ._binary import i32le as i32
try:
import builtins
except ImportError:
import __builtin__
builtins = __builtin__
def open(filename):
"""

View File

@ -105,7 +105,7 @@ class WebPImageFile(ImageFile.ImageFile):
def seek(self, frame):
if not _webp.HAVE_WEBPANIM:
return super(WebPImageFile, self).seek(frame)
return super().seek(frame)
# Perform some simple checks first
if frame >= self._n_frames:
@ -168,11 +168,11 @@ class WebPImageFile(ImageFile.ImageFile):
self.fp = BytesIO(data)
self.tile = [("raw", (0, 0) + self.size, 0, self.rawmode)]
return super(WebPImageFile, self).load()
return super().load()
def tell(self):
if not _webp.HAVE_WEBPANIM:
return super(WebPImageFile, self).tell()
return super().tell()
return self.__logical_frame
@ -233,7 +233,7 @@ def _save_all(im, fp, filename):
or len(background) != 4
or not all(v >= 0 and v < 256 for v in background)
):
raise IOError(
raise OSError(
"Background color is not an RGBA tuple clamped to (0-255): %s"
% str(background)
)
@ -312,7 +312,7 @@ def _save_all(im, fp, filename):
# Get the final output from the encoder
data = enc.assemble(icc_profile, exif, xmp)
if data is None:
raise IOError("cannot write file as WebP (encoder returned None)")
raise OSError("cannot write file as WebP (encoder returned None)")
fp.write(data)
@ -346,7 +346,7 @@ def _save(im, fp, filename):
xmp,
)
if data is None:
raise IOError("cannot write file as WebP (encoder returned None)")
raise OSError("cannot write file as WebP (encoder returned None)")
fp.write(data)

View File

@ -19,21 +19,11 @@
# http://wvware.sourceforge.net/caolan/index.html
# http://wvware.sourceforge.net/caolan/ora-wmf.html
from __future__ import print_function
from . import Image, ImageFile
from ._binary import i16le as word, i32le as dword, si16le as short, si32le as _long
from ._util import py3
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.2"
_handler = None
if py3:
long = int
def register_handler(handler):
"""
@ -48,7 +38,7 @@ def register_handler(handler):
if hasattr(Image.core, "drawwmf"):
# install default handler (windows only)
class WmfHandler(object):
class WmfHandler:
def open(self, im):
im.mode = "RGB"
self.bbox = im.info["wmf_bbox"]
@ -88,6 +78,7 @@ class WmfStubImageFile(ImageFile.StubImageFile):
format_description = "Windows Metafile"
def _open(self):
self._inch = None
# check placable header
s = self.fp.read(80)
@ -97,7 +88,7 @@ class WmfStubImageFile(ImageFile.StubImageFile):
# placeable windows metafile
# get units per inch
inch = word(s, 14)
self._inch = word(s, 14)
# get bounding box
x0 = short(s, 6)
@ -106,12 +97,14 @@ class WmfStubImageFile(ImageFile.StubImageFile):
y1 = short(s, 12)
# normalize size to 72 dots per inch
size = (x1 - x0) * 72 // inch, (y1 - y0) * 72 // inch
self.info["dpi"] = 72
size = (
(x1 - x0) * self.info["dpi"] // self._inch,
(y1 - y0) * self.info["dpi"] // self._inch,
)
self.info["wmf_bbox"] = x0, y0, x1, y1
self.info["dpi"] = 72
# sanity check (standard metafile header)
if s[22:26] != b"\x01\x00\t\x00":
raise SyntaxError("Unsupported WMF file format")
@ -128,7 +121,6 @@ class WmfStubImageFile(ImageFile.StubImageFile):
# get frame (in 0.01 millimeter units)
frame = _long(s, 24), _long(s, 28), _long(s, 32), _long(s, 36)
# normalize size to 72 dots per inch
size = x1 - x0, y1 - y0
# calculate dots per inch from bbox and frame
@ -155,10 +147,20 @@ class WmfStubImageFile(ImageFile.StubImageFile):
def _load(self):
return _handler
def load(self, dpi=None):
if dpi is not None and self._inch is not None:
self.info["dpi"] = int(dpi + 0.5)
x0, y0, x1, y1 = self.info["wmf_bbox"]
self._size = (
(x1 - x0) * self.info["dpi"] // self._inch,
(y1 - y0) * self.info["dpi"] // self._inch,
)
super().load()
def _save(im, fp, filename):
if _handler is None or not hasattr(_handler, "save"):
raise IOError("WMF save handler not installed")
raise OSError("WMF save handler not installed")
_handler.save(im, fp, filename)

View File

@ -20,10 +20,6 @@
from . import Image, ImageFile, ImagePalette
from ._binary import i8, o8
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.1"
_MAGIC = b"P7 332"
# standard color palette for thumbnails (RGB332)

View File

@ -23,10 +23,6 @@ import re
from . import Image, ImageFile
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.6"
# XBM header
xbm_head = re.compile(
br"\s*#define[ \t]+.*_width[ \t]+(?P<width>[0-9]+)[\r\n]+"
@ -73,7 +69,7 @@ class XbmImageFile(ImageFile.ImageFile):
def _save(im, fp, filename):
if im.mode != "1":
raise IOError("cannot write mode %s as XBM" % im.mode)
raise OSError("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"))

View File

@ -20,10 +20,6 @@ import re
from . import Image, ImageFile, ImagePalette
from ._binary import i8, o8
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
__version__ = "0.2"
# XPM header
xpm_head = re.compile(b'"([0-9]*) ([0-9]*) ([0-9]*) ([0-9]*)')

View File

@ -9,17 +9,75 @@ PIL is the Python Imaging Library by Fredrik Lundh and Contributors.
Copyright (c) 1999 by Secret Labs AB.
Use PIL.__version__ for this Pillow version.
PIL.VERSION is the old PIL version and will be removed in the future.
;-)
"""
import sys
import warnings
from . import _version
# VERSION was removed in Pillow 6.0.0.
# PILLOW_VERSION is deprecated and will be removed in Pillow 7.0.0.
__version__ = _version.__version__
# PILLOW_VERSION is deprecated and will be removed in a future release.
# Use __version__ instead.
PILLOW_VERSION = __version__ = _version.__version__
def _raise_version_warning():
warnings.warn(
"PILLOW_VERSION is deprecated and will be removed in a future release. "
"Use __version__ instead.",
DeprecationWarning,
stacklevel=3,
)
if sys.version_info >= (3, 7):
def __getattr__(name):
if name == "PILLOW_VERSION":
_raise_version_warning()
return __version__
raise AttributeError("module '{}' has no attribute '{}'".format(__name__, name))
else:
class _Deprecated_Version(str):
def __str__(self):
_raise_version_warning()
return super().__str__()
def __getitem__(self, key):
_raise_version_warning()
return super().__getitem__(key)
def __eq__(self, other):
_raise_version_warning()
return super().__eq__(other)
def __ne__(self, other):
_raise_version_warning()
return super().__ne__(other)
def __gt__(self, other):
_raise_version_warning()
return super().__gt__(other)
def __lt__(self, other):
_raise_version_warning()
return super().__lt__(other)
def __ge__(self, other):
_raise_version_warning()
return super().__gt__(other)
def __le__(self, other):
_raise_version_warning()
return super().__lt__(other)
PILLOW_VERSION = _Deprecated_Version(__version__)
del _version
@ -71,3 +129,7 @@ _plugins = [
"XpmImagePlugin",
"XVThumbImagePlugin",
]
class UnidentifiedImageError(IOError):
pass

View File

@ -13,24 +13,13 @@
from struct import pack, unpack_from
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,))
def i8(c):
return c if c.__class__ is int else c[0]
else:
def i8(c):
return ord(c)
def o8(i):
return chr(i & 255)
def o8(i):
return bytes((i & 255,))
# Input, le = little endian, be = big endian

View File

@ -1,11 +1,7 @@
""" Find compiled module linking to Tcl / Tk libraries
"""
import sys
if sys.version_info.major > 2:
from tkinter import _tkinter as tk
else:
from Tkinter import tkinter as tk
from tkinter import _tkinter as tk
if hasattr(sys, "pypy_find_executable"):
# Tested with packages at https://bitbucket.org/pypy/pypy/downloads.

View File

@ -1,33 +1,20 @@
import os
import sys
py3 = sys.version_info.major >= 3
py36 = sys.version_info[0:2] >= (3, 6)
if py3:
def isStringType(t):
return isinstance(t, str)
if py36:
from pathlib import Path
if py36:
from pathlib import Path
def isPath(f):
return isinstance(f, (bytes, str, Path))
else:
def isPath(f):
return isinstance(f, (bytes, str))
def isPath(f):
return isinstance(f, (bytes, str, Path))
else:
def isStringType(t):
return isinstance(t, basestring) # noqa: F821
def isPath(f):
return isinstance(f, basestring) # noqa: F821
return isinstance(f, (bytes, str))
# Checks if an object is a string, and that it points to a directory.
@ -35,7 +22,7 @@ def isDirectory(f):
return isPath(f) and os.path.isdir(f)
class deferred_error(object):
class deferred_error:
def __init__(self, ex):
self.ex = ex

View File

@ -1,2 +1,2 @@
# Master version for Pillow
__version__ = "6.2.1"
__version__ = "7.1.2"

View File

@ -1,8 +1,7 @@
from __future__ import print_function, unicode_literals
import collections
import os
import sys
import warnings
import PIL
@ -56,6 +55,8 @@ features = {
"transp_webp": ("PIL._webp", "HAVE_TRANSPARENCY"),
"raqm": ("PIL._imagingft", "HAVE_RAQM"),
"libjpeg_turbo": ("PIL._imaging", "HAVE_LIBJPEGTURBO"),
"libimagequant": ("PIL._imaging", "HAVE_LIBIMAGEQUANT"),
"xcb": ("PIL._imaging", "HAVE_XCB"),
}
@ -77,14 +78,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)
)
if feature in modules:
return check_module(feature)
if feature in codecs:
return check_codec(feature)
if feature in features:
return check_feature(feature)
warnings.warn("Unknown feature '%s'." % feature, stacklevel=2)
return False
def get_supported():
@ -94,7 +95,7 @@ def get_supported():
return ret
def pilinfo(out=None):
def pilinfo(out=None, supported_formats=True):
if out is None:
out = sys.stdout
@ -102,6 +103,10 @@ def pilinfo(out=None):
print("-" * 68, file=out)
print("Pillow {}".format(PIL.__version__), file=out)
py_version = sys.version.splitlines()
print("Python {}".format(py_version[0].strip()), file=out)
for py_version in py_version[1:]:
print(" {}".format(py_version.strip()), file=out)
print("-" * 68, file=out)
print(
"Python modules loaded from {}".format(os.path.dirname(Image.__file__)),
@ -113,12 +118,6 @@ def pilinfo(out=None):
)
print("-" * 68, file=out)
v = sys.version.splitlines()
print("Python {}".format(v[0].strip()), file=out)
for v in v[1:]:
print(" {}".format(v.strip()), file=out)
print("-" * 68, file=out)
for name, feature in [
("pil", "PIL CORE"),
("tkinter", "TKINTER"),
@ -133,6 +132,8 @@ def pilinfo(out=None):
("zlib", "ZLIB (PNG/ZIP)"),
("libtiff", "LIBTIFF"),
("raqm", "RAQM (Bidirectional Text)"),
("libimagequant", "LIBIMAGEQUANT (Quantization method)"),
("xcb", "XCB (X protocol)"),
]:
if check(name):
print("---", feature, "support ok", file=out)
@ -140,30 +141,33 @@ def pilinfo(out=None):
print("***", feature, "support not installed", file=out)
print("-" * 68, file=out)
extensions = collections.defaultdict(list)
for ext, i in Image.EXTENSION.items():
extensions[i].append(ext)
if supported_formats:
extensions = collections.defaultdict(list)
for ext, i in Image.EXTENSION.items():
extensions[i].append(ext)
for i in sorted(Image.ID):
line = "{}".format(i)
if i in Image.MIME:
line = "{} {}".format(line, Image.MIME[i])
print(line, file=out)
for i in sorted(Image.ID):
line = "{}".format(i)
if i in Image.MIME:
line = "{} {}".format(line, Image.MIME[i])
print(line, file=out)
if i in extensions:
print("Extensions: {}".format(", ".join(sorted(extensions[i]))), file=out)
if i in extensions:
print(
"Extensions: {}".format(", ".join(sorted(extensions[i]))), file=out
)
features = []
if i in Image.OPEN:
features.append("open")
if i in Image.SAVE:
features.append("save")
if i in Image.SAVE_ALL:
features.append("save_all")
if i in Image.DECODERS:
features.append("decode")
if i in Image.ENCODERS:
features.append("encode")
features = []
if i in Image.OPEN:
features.append("open")
if i in Image.SAVE:
features.append("save")
if i in Image.SAVE_ALL:
features.append("save_all")
if i in Image.DECODERS:
features.append("decode")
if i in Image.ENCODERS:
features.append("encode")
print("Features: {}".format(", ".join(features)), file=out)
print("-" * 68, file=out)
print("Features: {}".format(", ".join(features)), file=out)
print("-" * 68, file=out)

Some files were not shown because too many files have changed in this diff Show More