mirror of https://github.com/tp4a/teleport
update Python module for Linux.
parent
bc494cac5c
commit
45ee30b95d
|
@ -32,7 +32,7 @@
|
|||
|
||||
<div class="footer">
|
||||
<div class="container">
|
||||
<p><a href="https://tp4a.com/" target="_blank">TELEPORT</a> | ©2015 - 2019,保留所有权利。</p>
|
||||
<p><a href="https://tp4a.com/" target="_blank">TELEPORT</a> | ©2015 - 2020,保留所有权利。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
|
||||
<div class="footer">
|
||||
<div class="container">
|
||||
<p><a href="https://tp4a.com/" target="_blank">TELEPORT</a> | ©2015 - 2019,保留所有权利。</p>
|
||||
<p><a href="https://tp4a.com/" target="_blank">TELEPORT</a> | ©2015 - 2020,保留所有权利。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
|
||||
<div class="footer">
|
||||
<div class="container">
|
||||
<p><a href="https://tp4a.com/" target="_blank">TELEPORT</a> | ©2015 - 2019,保留所有权利。</p>
|
||||
<p><a href="https://tp4a.com/" target="_blank">TELEPORT</a> | ©2015 - 2020,保留所有权利。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
|
||||
<div class="footer">
|
||||
<div class="container">
|
||||
<p><a href="https://tp4a.com/" target="_blank">TELEPORT</a> | ©2015 - 2019,保留所有权利。</p>
|
||||
<p><a href="https://tp4a.com/" target="_blank">TELEPORT</a> | ©2015 - 2020,保留所有权利。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -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":
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
||||
#
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -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?
|
||||
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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))
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ from ._binary import o8
|
|||
# File handler for GIMP's palette format.
|
||||
|
||||
|
||||
class GimpPaletteFile(object):
|
||||
class GimpPaletteFile:
|
||||
|
||||
rawmode = "RGB"
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"]:
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
from . import Image, ImageFilter, ImageStat
|
||||
|
||||
|
||||
class _Enhance(object):
|
||||
class _Enhance:
|
||||
def enhance(self, factor):
|
||||
"""
|
||||
Returns an enhanced image.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
_modes = None
|
||||
|
||||
|
||||
class ModeDescriptor(object):
|
||||
class ModeDescriptor:
|
||||
"""Wrapper for mode strings."""
|
||||
|
||||
def __init__(self, mode, bands, basemode, basetype):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
@ -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:]))
|
||||
|
|
|
@ -26,7 +26,7 @@ import math
|
|||
import operator
|
||||
|
||||
|
||||
class Stat(object):
|
||||
class Stat:
|
||||
def __init__(self, image_or_list, mask=None):
|
||||
try:
|
||||
if mask:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
||||
#
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ from ._binary import o8
|
|||
# File handler for Teragon-style palette files.
|
||||
|
||||
|
||||
class PaletteFile(object):
|
||||
class PaletteFile:
|
||||
|
||||
rawmode = "RGB"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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"])
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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:])
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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]*)')
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
# Master version for Pillow
|
||||
__version__ = "6.2.1"
|
||||
__version__ = "7.1.2"
|
||||
|
|
Binary file not shown.
|
@ -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)
|
||||
|
|
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue