mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-25 09:26:16 +03:00
Merge branch 'master' into gha-win
This commit is contained in:
commit
a0a5601689
|
@ -30,6 +30,10 @@ environment:
|
|||
PIP_DIR: bin
|
||||
TEST_OPTIONS: --processes=0
|
||||
DEPLOY: NO
|
||||
- PYTHON: C:/vp/pypy3
|
||||
EXECUTABLE: bin/pypy.exe
|
||||
PIP_DIR: bin
|
||||
VENV: YES
|
||||
|
||||
|
||||
install:
|
||||
|
@ -43,7 +47,12 @@ install:
|
|||
- ps: |
|
||||
if ($env:PYTHON -eq "c:/vp/pypy2")
|
||||
{
|
||||
c:\pillow\winbuild\appveyor_install_pypy.cmd
|
||||
c:\pillow\winbuild\appveyor_install_pypy2.cmd
|
||||
}
|
||||
- ps: |
|
||||
if ($env:PYTHON -eq "c:/vp/pypy3")
|
||||
{
|
||||
c:\pillow\winbuild\appveyor_install_pypy3.cmd
|
||||
}
|
||||
- ps: |
|
||||
if ($env:PYTHON -eq "c:/msys64/mingw32")
|
||||
|
|
|
@ -22,19 +22,20 @@ matrix:
|
|||
name: "PyPy3 Xenial"
|
||||
- python: '3.7'
|
||||
name: "3.7 Xenial"
|
||||
services: xvfb
|
||||
- python: '2.7'
|
||||
name: "2.7 Xenial"
|
||||
- python: "2.7_with_system_site_packages" # For PyQt4
|
||||
name: "2.7_with_system_site_packages Xenial"
|
||||
services: xvfb
|
||||
- python: '3.6'
|
||||
name: "3.6 Xenial PYTHONOPTIMIZE=1"
|
||||
env: PYTHONOPTIMIZE=1
|
||||
services: xvfb
|
||||
- python: '3.5'
|
||||
name: "3.5 Xenial PYTHONOPTIMIZE=2"
|
||||
env: PYTHONOPTIMIZE=2
|
||||
services: xvfb
|
||||
- python: "3.8-dev"
|
||||
name: "3.8-dev Xenial"
|
||||
services: xvfb
|
||||
- env: DOCKER="alpine" DOCKER_TAG="master"
|
||||
- env: DOCKER="arch" DOCKER_TAG="master" # contains PyQt5
|
||||
- env: DOCKER="ubuntu-16.04-xenial-amd64" DOCKER_TAG="master"
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
set -e
|
||||
|
||||
sudo apt-get update
|
||||
sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-tk python-qt4\
|
||||
sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-tk\
|
||||
ghostscript libffi-dev libjpeg-turbo-progs libopenjp2-7-dev\
|
||||
cmake imagemagick libharfbuzz-dev libfribidi-dev
|
||||
|
||||
|
@ -15,6 +15,10 @@ pip install -U pytest-cov
|
|||
pip install pyroma
|
||||
pip install test-image-results
|
||||
pip install numpy
|
||||
if [[ $TRAVIS_PYTHON_VERSION == 3.* ]]; then
|
||||
sudo apt-get -qq install pyqt5-dev-tools
|
||||
pip install pyqt5
|
||||
fi
|
||||
|
||||
# docs only on Python 2.7
|
||||
if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ]; then pip install -r requirements.txt ; fi
|
||||
|
|
41
CHANGES.rst
41
CHANGES.rst
|
@ -2,11 +2,50 @@
|
|||
Changelog (Pillow)
|
||||
==================
|
||||
|
||||
6.2.0 (unreleased)
|
||||
7.0.0 (unreleased)
|
||||
------------------
|
||||
|
||||
- Drop support for EOL PyQt4 and PySide #4108
|
||||
[hugovk, radarhere]
|
||||
|
||||
- Removed deprecated setting of TIFF image sizes #4114
|
||||
[radarhere]
|
||||
|
||||
- Removed deprecated PILLOW_VERSION #4107
|
||||
[hugovk]
|
||||
|
||||
- Changed default frombuffer raw decoder args #1730
|
||||
[radarhere]
|
||||
|
||||
6.2.0 (2019-10-01)
|
||||
------------------
|
||||
|
||||
- This is the last Pillow release to support Python 2.7 #3642
|
||||
|
||||
- Catch buffer overruns #4104
|
||||
[radarhere]
|
||||
|
||||
- Initialize rows_per_strip when RowsPerStrip tag is missing #4034
|
||||
[cgohlke, radarhere]
|
||||
|
||||
- Raise error if TIFF dimension is a string #4103
|
||||
[radarhere]
|
||||
|
||||
- Added decompression bomb checks #4102
|
||||
[radarhere]
|
||||
|
||||
- Fix ImageGrab.grab DPI scaling on Windows 10 version 1607+ #4000
|
||||
[nulano, radarhere]
|
||||
|
||||
- Corrected negative seeks #4101
|
||||
[radarhere]
|
||||
|
||||
- Added argument to capture all screens on Windows #3950
|
||||
[nulano, radarhere]
|
||||
|
||||
- Updated warning to specify when Image.frombuffer defaults will change #4086
|
||||
[radarhere]
|
||||
|
||||
- Changed WindowsViewer format to PNG #4080
|
||||
[radarhere]
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ Released as needed privately to individual vendors for critical security-related
|
|||
```
|
||||
* [ ] Download distributions from the [Pillow Wheel Builder container](http://a365fff413fe338398b6-1c8a9b3114517dc5fe17b7c3f8c63a43.r19.cf2.rackcdn.com/).
|
||||
```bash
|
||||
wget -m -A 'Pillow-<VERSION>*' \
|
||||
wget -m -A 'Pillow-<VERSION>-*' \
|
||||
http://a365fff413fe338398b6-1c8a9b3114517dc5fe17b7c3f8c63a43.r19.cf2.rackcdn.com
|
||||
```
|
||||
|
||||
|
|
|
@ -4,9 +4,5 @@
|
|||
from io import BytesIO
|
||||
|
||||
from PIL import Image
|
||||
from PIL._util import py3
|
||||
|
||||
if py3:
|
||||
Image.open(BytesIO(bytes("icns\x00\x00\x00\x10hang\x00\x00\x00\x00", "latin-1")))
|
||||
else:
|
||||
Image.open(BytesIO(bytes("icns\x00\x00\x00\x10hang\x00\x00\x00\x00")))
|
||||
Image.open(BytesIO(b"icns\x00\x00\x00\x10hang\x00\x00\x00\x00"))
|
||||
|
|
|
@ -4,19 +4,5 @@
|
|||
from io import BytesIO
|
||||
|
||||
from PIL import Image
|
||||
from PIL._util import py3
|
||||
|
||||
if py3:
|
||||
Image.open(
|
||||
BytesIO(
|
||||
bytes(
|
||||
"\x00\x00\x00\x0cjP\x20\x20\x0d\x0a\x87\x0a\x00\x00\x00\x00hang",
|
||||
"latin-1",
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
else:
|
||||
Image.open(
|
||||
BytesIO(bytes("\x00\x00\x00\x0cjP\x20\x20\x0d\x0a\x87\x0a\x00\x00\x00\x00hang"))
|
||||
)
|
||||
Image.open(BytesIO(b"\x00\x00\x00\x0cjP\x20\x20\x0d\x0a\x87\x0a\x00\x00\x00\x00hang"))
|
||||
|
|
|
@ -5,9 +5,11 @@ from __future__ import print_function
|
|||
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from io import BytesIO
|
||||
|
||||
from PIL import Image, ImageMath
|
||||
from PIL._util import py3
|
||||
|
@ -284,14 +286,10 @@ if not py3:
|
|||
|
||||
|
||||
def fromstring(data):
|
||||
from io import BytesIO
|
||||
|
||||
return Image.open(BytesIO(data))
|
||||
|
||||
|
||||
def tostring(im, string_format, **options):
|
||||
from io import BytesIO
|
||||
|
||||
out = BytesIO()
|
||||
im.save(out, string_format, **options)
|
||||
return out.getvalue()
|
||||
|
@ -323,8 +321,6 @@ def command_succeeds(cmd):
|
|||
Runs the command, which must be a list of strings. Returns True if the
|
||||
command succeeds, or False if an OSError was raised by subprocess.Popen.
|
||||
"""
|
||||
import subprocess
|
||||
|
||||
with open(os.devnull, "wb") as f:
|
||||
try:
|
||||
subprocess.call(cmd, stdout=f, stderr=subprocess.STDOUT)
|
||||
|
|
BIN
Tests/images/combined_larger_than_size.psd
Normal file
BIN
Tests/images/combined_larger_than_size.psd
Normal file
Binary file not shown.
BIN
Tests/images/decompression_bomb.gif
Normal file
BIN
Tests/images/decompression_bomb.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 44 B |
BIN
Tests/images/decompression_bomb.ico
Normal file
BIN
Tests/images/decompression_bomb.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 58 B |
BIN
Tests/images/fli_overrun.bin
Normal file
BIN
Tests/images/fli_overrun.bin
Normal file
Binary file not shown.
BIN
Tests/images/no_rows_per_strip.tif
Normal file
BIN
Tests/images/no_rows_per_strip.tif
Normal file
Binary file not shown.
BIN
Tests/images/pcx_overrun.bin
Normal file
BIN
Tests/images/pcx_overrun.bin
Normal file
Binary file not shown.
BIN
Tests/images/raw_negative_stride.bin
Normal file
BIN
Tests/images/raw_negative_stride.bin
Normal file
Binary file not shown.
BIN
Tests/images/sgi_overrun.bin
Normal file
BIN
Tests/images/sgi_overrun.bin
Normal file
Binary file not shown.
BIN
Tests/images/string_dimension.tiff
Normal file
BIN
Tests/images/string_dimension.tiff
Normal file
Binary file not shown.
|
@ -41,6 +41,14 @@ class TestDecompressionBomb(PillowTestCase):
|
|||
|
||||
self.assertRaises(Image.DecompressionBombError, lambda: Image.open(TEST_FILE))
|
||||
|
||||
def test_exception_ico(self):
|
||||
with self.assertRaises(Image.DecompressionBombError):
|
||||
Image.open("Tests/images/decompression_bomb.ico")
|
||||
|
||||
def test_exception_gif(self):
|
||||
with self.assertRaises(Image.DecompressionBombError):
|
||||
Image.open("Tests/images/decompression_bomb.gif")
|
||||
|
||||
|
||||
class TestDecompressionCrop(PillowTestCase):
|
||||
def setUp(self):
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from __future__ import print_function
|
||||
|
||||
import base64
|
||||
import distutils.version
|
||||
import io
|
||||
import itertools
|
||||
|
@ -832,6 +833,13 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
im, "Tests/images/old-style-jpeg-compression.png"
|
||||
)
|
||||
|
||||
def test_no_rows_per_strip(self):
|
||||
# This image does not have a RowsPerStrip TIFF tag
|
||||
infile = "Tests/images/no_rows_per_strip.tif"
|
||||
im = Image.open(infile)
|
||||
im.load()
|
||||
self.assertEqual(im.size, (950, 975))
|
||||
|
||||
def test_orientation(self):
|
||||
base_im = Image.open("Tests/images/g4_orientation_1.tif")
|
||||
|
||||
|
@ -840,3 +848,24 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
im.load()
|
||||
|
||||
self.assert_image_similar(base_im, im, 0.7)
|
||||
|
||||
def test_sampleformat_not_corrupted(self):
|
||||
# Assert that a TIFF image with SampleFormat=UINT tag is not corrupted
|
||||
# when saving to a new file.
|
||||
# Pillow 6.0 fails with "OSError: cannot identify image file".
|
||||
tiff = io.BytesIO(
|
||||
base64.b64decode(
|
||||
b"SUkqAAgAAAAPAP4ABAABAAAAAAAAAAABBAABAAAAAQAAAAEBBAABAAAAAQAA"
|
||||
b"AAIBAwADAAAAwgAAAAMBAwABAAAACAAAAAYBAwABAAAAAgAAABEBBAABAAAA"
|
||||
b"4AAAABUBAwABAAAAAwAAABYBBAABAAAAAQAAABcBBAABAAAACwAAABoBBQAB"
|
||||
b"AAAAyAAAABsBBQABAAAA0AAAABwBAwABAAAAAQAAACgBAwABAAAAAQAAAFMB"
|
||||
b"AwADAAAA2AAAAAAAAAAIAAgACAABAAAAAQAAAAEAAAABAAAAAQABAAEAAAB4"
|
||||
b"nGNgYAAAAAMAAQ=="
|
||||
)
|
||||
)
|
||||
out = io.BytesIO()
|
||||
with Image.open(tiff) as im:
|
||||
im.save(out, format="tiff")
|
||||
out.seek(0)
|
||||
with Image.open(out) as im:
|
||||
im.load()
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
from io import BytesIO
|
||||
|
||||
from PIL import Image
|
||||
|
||||
from .test_file_libtiff import LibTiffTestCase
|
||||
|
@ -25,8 +27,6 @@ class TestFileLibTiffSmall(LibTiffTestCase):
|
|||
|
||||
def test_g4_hopper_bytesio(self):
|
||||
"""Testing the bytesio loading code path"""
|
||||
from io import BytesIO
|
||||
|
||||
test_file = "Tests/images/hopper_g4.tif"
|
||||
s = BytesIO()
|
||||
with open(test_file, "rb") as f:
|
||||
|
|
|
@ -87,3 +87,12 @@ class TestImagePsd(PillowTestCase):
|
|||
im = Image.open("Tests/images/hopper_merged.psd")
|
||||
|
||||
self.assertNotIn("icc_profile", im.info)
|
||||
|
||||
def test_combined_larger_than_size(self):
|
||||
# The 'combined' sizes of the individual parts is larger than the
|
||||
# declared 'size' of the extra data field, resulting in a backwards seek.
|
||||
|
||||
# If we instead take the 'size' of the extra data field as the source of truth,
|
||||
# then the seek can't be negative
|
||||
with self.assertRaises(IOError):
|
||||
Image.open("Tests/images/combined_larger_than_size.psd")
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import tempfile
|
||||
from io import BytesIO
|
||||
|
||||
from PIL import Image, ImageSequence, SpiderImagePlugin
|
||||
|
||||
|
@ -117,3 +118,14 @@ class TestImageSpider(PillowTestCase):
|
|||
for i, frame in enumerate(ImageSequence.Iterator(im)):
|
||||
if i > 1:
|
||||
self.fail("Non-stack DOS file test failed")
|
||||
|
||||
# for issue #4093
|
||||
def test_odd_size(self):
|
||||
data = BytesIO()
|
||||
width = 100
|
||||
im = Image.new("F", (width, 64))
|
||||
im.save(data, format="SPIDER")
|
||||
|
||||
data.seek(0)
|
||||
im2 = Image.open(data)
|
||||
self.assert_image_equal(im, im2)
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import logging
|
||||
import os
|
||||
from io import BytesIO
|
||||
|
||||
from PIL import Image, TiffImagePlugin, features
|
||||
from PIL import Image, TiffImagePlugin
|
||||
from PIL._util import py3
|
||||
from PIL.TiffImagePlugin import RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION
|
||||
|
||||
|
@ -72,15 +73,6 @@ class TestFileTiff(PillowTestCase):
|
|||
ifd.legacy_api = None
|
||||
self.assertEqual(str(e.exception), "Not allowing setting of legacy api")
|
||||
|
||||
def test_size(self):
|
||||
filename = "Tests/images/pil168.tif"
|
||||
im = Image.open(filename)
|
||||
|
||||
def set_size():
|
||||
im.size = (256, 256)
|
||||
|
||||
self.assert_warning(DeprecationWarning, set_size)
|
||||
|
||||
def test_xyres_tiff(self):
|
||||
filename = "Tests/images/pil168.tif"
|
||||
im = Image.open(filename)
|
||||
|
@ -512,10 +504,7 @@ class TestFileTiff(PillowTestCase):
|
|||
self.assert_image_equal(im.convert("RGB"), reloaded.convert("RGB"))
|
||||
|
||||
def test_tiff_save_all(self):
|
||||
import io
|
||||
import os
|
||||
|
||||
mp = io.BytesIO()
|
||||
mp = BytesIO()
|
||||
with Image.open("Tests/images/multipage.tiff") as im:
|
||||
im.save(mp, format="tiff", save_all=True)
|
||||
|
||||
|
@ -524,7 +513,7 @@ class TestFileTiff(PillowTestCase):
|
|||
self.assertEqual(im.n_frames, 3)
|
||||
|
||||
# Test appending images
|
||||
mp = io.BytesIO()
|
||||
mp = BytesIO()
|
||||
im = Image.new("RGB", (100, 100), "#f00")
|
||||
ims = [Image.new("RGB", (100, 100), color) for color in ["#0f0", "#00f"]]
|
||||
im.copy().save(mp, format="TIFF", save_all=True, append_images=ims)
|
||||
|
@ -538,7 +527,7 @@ class TestFileTiff(PillowTestCase):
|
|||
for im in ims:
|
||||
yield im
|
||||
|
||||
mp = io.BytesIO()
|
||||
mp = BytesIO()
|
||||
im.save(mp, format="TIFF", save_all=True, append_images=imGenerator(ims))
|
||||
|
||||
mp.seek(0, os.SEEK_SET)
|
||||
|
@ -586,36 +575,16 @@ class TestFileTiff(PillowTestCase):
|
|||
im.load()
|
||||
self.assertFalse(fp.closed)
|
||||
|
||||
@unittest.skipUnless(features.check("libtiff"), "libtiff not installed")
|
||||
def test_sampleformat_not_corrupted(self):
|
||||
# Assert that a TIFF image with SampleFormat=UINT tag is not corrupted
|
||||
# when saving to a new file.
|
||||
# Pillow 6.0 fails with "OSError: cannot identify image file".
|
||||
import base64
|
||||
|
||||
tiff = BytesIO(
|
||||
base64.b64decode(
|
||||
b"SUkqAAgAAAAPAP4ABAABAAAAAAAAAAABBAABAAAAAQAAAAEBBAABAAAAAQAA"
|
||||
b"AAIBAwADAAAAwgAAAAMBAwABAAAACAAAAAYBAwABAAAAAgAAABEBBAABAAAA"
|
||||
b"4AAAABUBAwABAAAAAwAAABYBBAABAAAAAQAAABcBBAABAAAACwAAABoBBQAB"
|
||||
b"AAAAyAAAABsBBQABAAAA0AAAABwBAwABAAAAAQAAACgBAwABAAAAAQAAAFMB"
|
||||
b"AwADAAAA2AAAAAAAAAAIAAgACAABAAAAAQAAAAEAAAABAAAAAQABAAEAAAB4"
|
||||
b"nGNgYAAAAAMAAQ=="
|
||||
)
|
||||
)
|
||||
out = BytesIO()
|
||||
with Image.open(tiff) as im:
|
||||
im.save(out, format="tiff")
|
||||
out.seek(0)
|
||||
with Image.open(out) as im:
|
||||
im.load()
|
||||
def test_string_dimension(self):
|
||||
# Assert that an error is raised if one of the dimensions is a string
|
||||
with self.assertRaises(ValueError):
|
||||
Image.open("Tests/images/string_dimension.tiff")
|
||||
|
||||
|
||||
@unittest.skipUnless(is_win32(), "Windows only")
|
||||
class TestFileTiffW32(PillowTestCase):
|
||||
def test_fd_leak(self):
|
||||
tmpfile = self.tempfile("temp.tif")
|
||||
import os
|
||||
|
||||
# this is an mmaped file.
|
||||
with Image.open("Tests/images/uint16_1_4660.tif") as im:
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
from io import BytesIO
|
||||
|
||||
from PIL import Image
|
||||
|
||||
from .helper import PillowTestCase
|
||||
|
@ -39,8 +41,6 @@ class TestFileWebpMetadata(PillowTestCase):
|
|||
self.assertEqual(exif_data, expected_exif)
|
||||
|
||||
def test_write_exif_metadata(self):
|
||||
from io import BytesIO
|
||||
|
||||
file_path = "Tests/images/flower.jpg"
|
||||
image = Image.open(file_path)
|
||||
expected_exif = image.info["exif"]
|
||||
|
@ -73,8 +73,6 @@ class TestFileWebpMetadata(PillowTestCase):
|
|||
self.assertEqual(icc, expected_icc)
|
||||
|
||||
def test_write_icc_metadata(self):
|
||||
from io import BytesIO
|
||||
|
||||
file_path = "Tests/images/flower2.jpg"
|
||||
image = Image.open(file_path)
|
||||
expected_icc_profile = image.info["icc_profile"]
|
||||
|
@ -95,8 +93,6 @@ class TestFileWebpMetadata(PillowTestCase):
|
|||
)
|
||||
|
||||
def test_read_no_exif(self):
|
||||
from io import BytesIO
|
||||
|
||||
file_path = "Tests/images/flower.jpg"
|
||||
image = Image.open(file_path)
|
||||
self.assertIn("exif", image.info)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
from io import BytesIO
|
||||
|
||||
from PIL import Image
|
||||
|
||||
from .helper import PillowTestCase
|
||||
|
@ -28,8 +30,6 @@ static char basic_bits[] = {
|
|||
|
||||
class TestFileXbm(PillowTestCase):
|
||||
def test_pil151(self):
|
||||
from io import BytesIO
|
||||
|
||||
im = Image.open(BytesIO(PIL151))
|
||||
|
||||
im.load()
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
from PIL import Image
|
||||
from PIL._util import py3
|
||||
|
@ -125,8 +126,6 @@ class TestImage(PillowTestCase):
|
|||
def test_tempfile(self):
|
||||
# see #1460, pathlib support breaks tempfile.TemporaryFile on py27
|
||||
# Will error out on save on 3.0.0
|
||||
import tempfile
|
||||
|
||||
im = hopper()
|
||||
with tempfile.TemporaryFile() as fp:
|
||||
im.save(fp, "JPEG")
|
||||
|
@ -586,6 +585,15 @@ class TestImage(PillowTestCase):
|
|||
|
||||
self.assertFalse(fp.closed)
|
||||
|
||||
def test_overrun(self):
|
||||
for file in ["fli_overrun.bin", "sgi_overrun.bin", "pcx_overrun.bin"]:
|
||||
im = Image.open(os.path.join("Tests/images", file))
|
||||
try:
|
||||
im.load()
|
||||
self.assertFail()
|
||||
except IOError as e:
|
||||
self.assertEqual(str(e), "buffer overrun when reading image file")
|
||||
|
||||
|
||||
class MockEncoder(object):
|
||||
pass
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
import ctypes
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from distutils import ccompiler, sysconfig
|
||||
|
||||
from PIL import Image
|
||||
|
||||
|
@ -337,10 +340,6 @@ class TestEmbeddable(unittest.TestCase):
|
|||
"Failing on AppVeyor / GitHub Actions when run from subprocess, not from shell",
|
||||
)
|
||||
def test_embeddable(self):
|
||||
import subprocess
|
||||
import ctypes
|
||||
from distutils import ccompiler, sysconfig
|
||||
|
||||
with open("embed_pil.c", "w") as fh:
|
||||
fh.write(
|
||||
"""
|
||||
|
|
|
@ -103,6 +103,14 @@ class TestImageFile(PillowTestCase):
|
|||
parser = ImageFile.Parser()
|
||||
parser.feed(1)
|
||||
|
||||
def test_negative_stride(self):
|
||||
with open("Tests/images/raw_negative_stride.bin", "rb") as f:
|
||||
input = f.read()
|
||||
p = ImageFile.Parser()
|
||||
p.feed(input)
|
||||
with self.assertRaises(IOError):
|
||||
p.close()
|
||||
|
||||
def test_truncated_with_errors(self):
|
||||
if "zip_encoder" not in codecs:
|
||||
self.skipTest("PNG (zlib) encoder not available")
|
||||
|
|
|
@ -8,7 +8,11 @@ try:
|
|||
|
||||
class TestImageGrab(PillowTestCase):
|
||||
def test_grab(self):
|
||||
for im in [ImageGrab.grab(), ImageGrab.grab(include_layered_windows=True)]:
|
||||
for im in [
|
||||
ImageGrab.grab(),
|
||||
ImageGrab.grab(include_layered_windows=True),
|
||||
ImageGrab.grab(all_screens=True),
|
||||
]:
|
||||
self.assert_image(im, im.mode, im.size)
|
||||
|
||||
def test_grabclipboard(self):
|
||||
|
|
|
@ -81,6 +81,16 @@ class TestImageOps(PillowTestCase):
|
|||
newimg = ImageOps.fit(hopper("RGB").resize((100, 1)), (35, 35))
|
||||
self.assertEqual(newimg.size, (35, 35))
|
||||
|
||||
def test_fit_same_ratio(self):
|
||||
# The ratio for this image is 1000.0 / 755 = 1.3245033112582782
|
||||
# If the ratios are not acknowledged to be the same,
|
||||
# and Pillow attempts to adjust the width to
|
||||
# 1.3245033112582782 * 755 = 1000.0000000000001
|
||||
# then centering this greater width causes a negative x offset when cropping
|
||||
with Image.new("RGB", (1000, 755)) as im:
|
||||
new_im = ImageOps.fit(im, (1000, 755))
|
||||
self.assertEqual(new_im.size, (1000, 755))
|
||||
|
||||
def test_pad(self):
|
||||
# Same ratio
|
||||
im = hopper()
|
||||
|
|
|
@ -1,13 +1,7 @@
|
|||
import sys
|
||||
import warnings
|
||||
|
||||
from PIL import ImageQt
|
||||
|
||||
from .helper import PillowTestCase, hopper
|
||||
|
||||
if sys.version_info.major >= 3:
|
||||
from importlib import reload
|
||||
|
||||
if ImageQt.qt_is_installed:
|
||||
from PIL.ImageQt import qRgba
|
||||
|
||||
|
@ -35,10 +29,6 @@ class PillowQPixmapTestCase(PillowQtTestCase):
|
|||
try:
|
||||
if ImageQt.qt_version == "5":
|
||||
from PyQt5.QtGui import QGuiApplication
|
||||
elif ImageQt.qt_version == "4":
|
||||
from PyQt4.QtGui import QGuiApplication
|
||||
elif ImageQt.qt_version == "side":
|
||||
from PySide.QtGui import QGuiApplication
|
||||
elif ImageQt.qt_version == "side2":
|
||||
from PySide2.QtGui import QGuiApplication
|
||||
except ImportError:
|
||||
|
@ -59,10 +49,6 @@ class TestImageQt(PillowQtTestCase, PillowTestCase):
|
|||
# equivalent to an unsigned int.
|
||||
if ImageQt.qt_version == "5":
|
||||
from PyQt5.QtGui import qRgb
|
||||
elif ImageQt.qt_version == "4":
|
||||
from PyQt4.QtGui import qRgb
|
||||
elif ImageQt.qt_version == "side":
|
||||
from PySide.QtGui import qRgb
|
||||
elif ImageQt.qt_version == "side2":
|
||||
from PySide2.QtGui import qRgb
|
||||
|
||||
|
@ -83,13 +69,3 @@ class TestImageQt(PillowQtTestCase, PillowTestCase):
|
|||
def test_image(self):
|
||||
for mode in ("1", "RGB", "RGBA", "L", "P"):
|
||||
ImageQt.ImageQt(hopper(mode))
|
||||
|
||||
def test_deprecated(self):
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
reload(ImageQt)
|
||||
if ImageQt.qt_version in ["4", "side"]:
|
||||
self.assertEqual(len(w), 1)
|
||||
self.assertTrue(issubclass(w[-1].category, DeprecationWarning))
|
||||
else:
|
||||
# No warning.
|
||||
self.assertEqual(w, [])
|
||||
|
|
|
@ -31,4 +31,8 @@ class TestLocale(PillowTestCase):
|
|||
locale.setlocale(locale.LC_ALL, "polish")
|
||||
except locale.Error:
|
||||
unittest.skip("Polish locale not available")
|
||||
|
||||
try:
|
||||
Image.open(path)
|
||||
finally:
|
||||
locale.setlocale(locale.LC_ALL, (None, None))
|
||||
|
|
|
@ -9,26 +9,10 @@ if ImageQt.qt_is_installed:
|
|||
try:
|
||||
from PyQt5 import QtGui
|
||||
from PyQt5.QtWidgets import QWidget, QHBoxLayout, QLabel, QApplication
|
||||
|
||||
QT_VERSION = 5
|
||||
except (ImportError, RuntimeError):
|
||||
try:
|
||||
from PySide2 import QtGui
|
||||
from PySide2.QtWidgets import QWidget, QHBoxLayout, QLabel, QApplication
|
||||
|
||||
QT_VERSION = 5
|
||||
except (ImportError, RuntimeError):
|
||||
try:
|
||||
from PyQt4 import QtGui
|
||||
from PyQt4.QtGui import QWidget, QHBoxLayout, QLabel, QApplication
|
||||
|
||||
QT_VERSION = 4
|
||||
except (ImportError, RuntimeError):
|
||||
from PySide import QtGui
|
||||
from PySide.QtGui import QWidget, QHBoxLayout, QLabel, QApplication
|
||||
|
||||
QT_VERSION = 4
|
||||
|
||||
|
||||
class TestToQImage(PillowQtTestCase, PillowTestCase):
|
||||
def test_sanity(self):
|
||||
|
@ -60,10 +44,6 @@ class TestToQImage(PillowQtTestCase, PillowTestCase):
|
|||
|
||||
# Check that it actually worked.
|
||||
reloaded = Image.open(tempfile)
|
||||
# Gray images appear to come back in palette mode.
|
||||
# They're roughly equivalent
|
||||
if QT_VERSION == 4 and mode == "L":
|
||||
src = src.convert("P")
|
||||
self.assert_image_equal(reloaded, src)
|
||||
|
||||
def test_segfault(self):
|
||||
|
|
|
@ -45,17 +45,6 @@ Python 2.7 reaches end-of-life on 2020-01-01.
|
|||
Pillow 7.0.0 will be released on 2020-01-01 and will drop support for Python 2.7, making
|
||||
Pillow 6.x the last series to support Python 2.
|
||||
|
||||
PyQt4 and PySide
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 6.0.0
|
||||
|
||||
Qt 4 reached end-of-life on 2015-12-19. Its Python bindings are also EOL: PyQt4 since
|
||||
2018-08-31 and PySide since 2015-10-14.
|
||||
|
||||
Support for PyQt4 and PySide has been deprecated from ``ImageQt`` and will be removed in
|
||||
a future version. Please upgrade to PyQt5 or PySide2.
|
||||
|
||||
PIL.*ImagePlugin.__version__ attributes
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -81,27 +70,6 @@ Deprecated Deprecated Deprecated
|
|||
``IptcImagePlugin.__version__`` ``PixarImagePlugin.__version__``
|
||||
=============================== ================================= ==================================
|
||||
|
||||
Setting the size of TIFF images
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 5.3.0
|
||||
|
||||
Setting the image size of a TIFF image (eg. ``im.size = (256, 256)``) issues
|
||||
a ``DeprecationWarning``:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
Setting the size of a TIFF image directly is deprecated, and will
|
||||
be removed in a future version. Use the resize method instead.
|
||||
|
||||
PILLOW_VERSION constant
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 5.2.0
|
||||
|
||||
``PILLOW_VERSION`` has been deprecated and will be removed in 7.0.0. Use ``__version__``
|
||||
instead.
|
||||
|
||||
ImageCms.CmsProfile attributes
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -128,6 +96,32 @@ Removed features
|
|||
Deprecated features are only removed in major releases after an appropriate
|
||||
period of deprecation has passed.
|
||||
|
||||
PILLOW_VERSION constant
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
*Removed in version 7.0.0.*
|
||||
|
||||
``PILLOW_VERSION`` has been removed. Use ``__version__`` instead.
|
||||
|
||||
PyQt4 and PySide
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
*Removed in version 7.0.0.*
|
||||
|
||||
Qt 4 reached end-of-life on 2015-12-19. Its Python bindings are also EOL: PyQt4 since
|
||||
2018-08-31 and PySide since 2015-10-14.
|
||||
|
||||
Support for PyQt4 and PySide has been removed from ``ImageQt``. Please upgrade to PyQt5
|
||||
or PySide2.
|
||||
|
||||
Setting the size of TIFF images
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
*Removed in version 7.0.0.*
|
||||
|
||||
Setting the size of a TIFF image directly (eg. ``im.size = (256, 256)``) throws
|
||||
an error. Use ``Image.resize`` instead.
|
||||
|
||||
VERSION constant
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -113,8 +113,8 @@ to seek to the next frame (``im.seek(im.tell() + 1)``).
|
|||
Saving
|
||||
~~~~~~
|
||||
|
||||
When calling :py:meth:`~PIL.Image.Image.save`, the following options
|
||||
are available::
|
||||
When calling :py:meth:`~PIL.Image.Image.save` to write a GIF file, the
|
||||
following options are available::
|
||||
|
||||
im.save(out, save_all=True, append_images=[im1, im2, ...])
|
||||
|
||||
|
@ -813,8 +813,9 @@ Saving sequences
|
|||
library is v0.5.0 or later. You can check webp animation support at
|
||||
runtime by calling ``features.check("webp_anim")``.
|
||||
|
||||
When calling :py:meth:`~PIL.Image.Image.save`, the following options
|
||||
are available when the ``save_all`` argument is present and true.
|
||||
When calling :py:meth:`~PIL.Image.Image.save` to write a WebP file, the
|
||||
following options are available when the ``save_all`` argument is present and
|
||||
true.
|
||||
|
||||
**append_images**
|
||||
A list of images to append as additional frames. Each of the
|
||||
|
|
|
@ -11,7 +11,7 @@ or the clipboard to a PIL image memory.
|
|||
|
||||
.. versionadded:: 1.1.3
|
||||
|
||||
.. py:function:: PIL.ImageGrab.grab(bbox=None, include_layered_windows=False)
|
||||
.. py:function:: PIL.ImageGrab.grab(bbox=None, include_layered_windows=False, all_screens=False)
|
||||
|
||||
Take a snapshot of the screen. The pixels inside the bounding box are
|
||||
returned as an "RGB" image on Windows or "RGBA" on macOS.
|
||||
|
@ -20,7 +20,13 @@ or the clipboard to a PIL image memory.
|
|||
.. versionadded:: 1.1.3 (Windows), 3.0.0 (macOS)
|
||||
|
||||
:param bbox: What region to copy. Default is the entire screen.
|
||||
Note that on Windows OS, the top-left point may be negative if ``all_screens=True`` is used.
|
||||
:param include_layered_windows: Includes layered windows. Windows OS only.
|
||||
|
||||
.. versionadded:: 6.1.0
|
||||
:param all_screens: Capture all monitors. Windows OS only.
|
||||
|
||||
.. versionadded:: 6.2.0
|
||||
:return: An image
|
||||
|
||||
.. py:function:: PIL.ImageGrab.grabclipboard()
|
||||
|
|
|
@ -8,13 +8,13 @@ The :py:mod:`ImageOps` module contains a number of ‘ready-made’ image
|
|||
processing operations. This module is somewhat experimental, and most operators
|
||||
only work on L and RGB images.
|
||||
|
||||
Only bug fixes have been added since the Pillow fork.
|
||||
|
||||
.. versionadded:: 1.1.3
|
||||
|
||||
.. autofunction:: autocontrast
|
||||
.. autofunction:: colorize
|
||||
.. autofunction:: pad
|
||||
.. autofunction:: crop
|
||||
.. autofunction:: scale
|
||||
.. autofunction:: deform
|
||||
.. autofunction:: equalize
|
||||
.. autofunction:: expand
|
||||
|
@ -25,3 +25,4 @@ Only bug fixes have been added since the Pillow fork.
|
|||
.. autofunction:: mirror
|
||||
.. autofunction:: posterize
|
||||
.. autofunction:: solarize
|
||||
.. autofunction:: exif_transpose
|
||||
|
|
|
@ -4,14 +4,8 @@
|
|||
:py:mod:`ImageQt` Module
|
||||
========================
|
||||
|
||||
The :py:mod:`ImageQt` module contains support for creating PyQt4, PyQt5, PySide or
|
||||
PySide2 QImage objects from PIL images.
|
||||
|
||||
Qt 4 reached end-of-life on 2015-12-19. Its Python bindings are also EOL: PyQt4 since
|
||||
2018-08-31 and PySide since 2015-10-14.
|
||||
|
||||
Support for PyQt4 and PySide is deprecated since Pillow 6.0.0 and will be removed in a
|
||||
future version. Please upgrade to PyQt5 or PySide2.
|
||||
The :py:mod:`ImageQt` module contains support for creating PyQt5 or PySide2 QImage
|
||||
objects from PIL images.
|
||||
|
||||
.. versionadded:: 1.1.6
|
||||
|
||||
|
@ -20,7 +14,7 @@ future version. Please upgrade to PyQt5 or PySide2.
|
|||
Creates an :py:class:`~PIL.ImageQt.ImageQt` object from a PIL
|
||||
:py:class:`~PIL.Image.Image` object. This class is a subclass of
|
||||
QtGui.QImage, which means that you can pass the resulting objects directly
|
||||
to PyQt4/PyQt5/PySide API functions and methods.
|
||||
to PyQt5/PySide2 API functions and methods.
|
||||
|
||||
This operation is currently supported for mode 1, L, P, RGB, and RGBA
|
||||
images. To handle other modes, you need to convert the image first.
|
||||
|
|
|
@ -44,6 +44,12 @@ creates the following image:
|
|||
|
||||
.. image:: ../../Tests/images/imagedraw_stroke_different.png
|
||||
|
||||
ImageGrab on multi-monitor Windows
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
An ``all_screens`` argument has been added to ``ImageGrab.grab``. If ``True``,
|
||||
all monitors will be included in the created image.
|
||||
|
||||
API Changes
|
||||
===========
|
||||
|
||||
|
@ -64,6 +70,13 @@ Python 2.7 reaches end-of-life on 2020-01-01.
|
|||
Pillow 7.0.0 will be released on 2020-01-01 and will drop support for Python
|
||||
2.7, making Pillow 6.2.x the last release series to support Python 2.
|
||||
|
||||
Image.frombuffer
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
There has been a longstanding warning that the defaults of ``Image.frombuffer``
|
||||
may change in the future for the "raw" decoder. The change will now take place
|
||||
in Pillow 7.0.
|
||||
|
||||
Other Changes
|
||||
=============
|
||||
|
||||
|
|
|
@ -23,7 +23,9 @@
|
|||
import io
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
from . import Image, ImageFile
|
||||
from ._binary import i32le as i32
|
||||
|
@ -61,8 +63,6 @@ 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)
|
||||
|
@ -91,9 +91,6 @@ def Ghostscript(tile, size, fp, scale=1):
|
|||
float((72.0 * size[1]) / (bbox[3] - bbox[1])),
|
||||
)
|
||||
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
out_fd, outfile = tempfile.mkstemp()
|
||||
os.close(out_fd)
|
||||
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
#
|
||||
|
||||
import itertools
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from . import Image, ImageChops, ImageFile, ImagePalette, ImageSequence
|
||||
from ._binary import i8, i16le as i16, o8, o16le as o16
|
||||
|
@ -265,6 +267,7 @@ class GifImageFile(ImageFile.ImageFile):
|
|||
self.dispose = None
|
||||
elif self.disposal_method == 2:
|
||||
# replace with background colour
|
||||
Image._decompression_bomb_check(self.size)
|
||||
self.dispose = Image.core.fill("P", self.size, self.info["background"])
|
||||
else:
|
||||
# replace with previous contents
|
||||
|
@ -616,24 +619,22 @@ 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)
|
||||
subprocess.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(
|
||||
quant_proc = subprocess.Popen(
|
||||
quant_cmd, stdout=subprocess.PIPE, stderr=devnull
|
||||
)
|
||||
togif_proc = subprocess.Popen(
|
||||
togif_cmd, stdin=quant_proc.stdout, stdout=f, stderr=devnull
|
||||
)
|
||||
|
||||
|
@ -642,11 +643,11 @@ def _save_netpbm(im, fp, filename):
|
|||
|
||||
retcode = quant_proc.wait()
|
||||
if retcode:
|
||||
raise CalledProcessError(retcode, quant_cmd)
|
||||
raise subprocess.CalledProcessError(retcode, quant_cmd)
|
||||
|
||||
retcode = togif_proc.wait()
|
||||
if retcode:
|
||||
raise CalledProcessError(retcode, togif_cmd)
|
||||
raise subprocess.CalledProcessError(retcode, togif_cmd)
|
||||
|
||||
try:
|
||||
os.unlink(tempfile)
|
||||
|
|
|
@ -19,6 +19,7 @@ import io
|
|||
import os
|
||||
import shutil
|
||||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
|
@ -333,11 +334,12 @@ def _save(im, fp, filename):
|
|||
last_w = w * 2
|
||||
|
||||
# iconutil -c icns -o {} {}
|
||||
from subprocess import Popen, PIPE, CalledProcessError
|
||||
|
||||
convert_cmd = ["iconutil", "-c", "icns", "-o", filename, iconset]
|
||||
with open(os.devnull, "wb") as devnull:
|
||||
convert_proc = Popen(convert_cmd, stdout=PIPE, stderr=devnull)
|
||||
convert_proc = subprocess.Popen(
|
||||
convert_cmd, stdout=subprocess.PIPE, stderr=devnull
|
||||
)
|
||||
|
||||
convert_proc.stdout.close()
|
||||
|
||||
|
@ -347,7 +349,7 @@ def _save(im, fp, filename):
|
|||
shutil.rmtree(iconset)
|
||||
|
||||
if retcode:
|
||||
raise CalledProcessError(retcode, convert_cmd)
|
||||
raise subprocess.CalledProcessError(retcode, convert_cmd)
|
||||
|
||||
|
||||
Image.register_open(IcnsImageFile.format, IcnsImageFile, lambda x: x[:4] == b"icns")
|
||||
|
|
|
@ -180,6 +180,7 @@ class IcoFile(object):
|
|||
else:
|
||||
# XOR + AND mask bmp frame
|
||||
im = BmpImagePlugin.DibImageFile(self.buf)
|
||||
Image._decompression_bomb_check(im.size)
|
||||
|
||||
# change tile dimension to only encompass XOR image
|
||||
im._size = (im.size[0], int(im.size[1] / 2))
|
||||
|
|
|
@ -32,12 +32,13 @@ import numbers
|
|||
import os
|
||||
import struct
|
||||
import sys
|
||||
import tempfile
|
||||
import warnings
|
||||
|
||||
# VERSION was removed in Pillow 6.0.0.
|
||||
# PILLOW_VERSION is deprecated and will be removed in Pillow 7.0.0.
|
||||
# PILLOW_VERSION was removed in Pillow 7.0.0.
|
||||
# Use __version__ instead.
|
||||
from . import PILLOW_VERSION, ImageMode, TiffTags, __version__, _plugins
|
||||
from . import ImageMode, TiffTags, __version__, _plugins
|
||||
from ._binary import i8, i32le
|
||||
from ._util import deferred_error, isPath, isStringType, py3
|
||||
|
||||
|
@ -57,9 +58,6 @@ except ImportError:
|
|||
from collections import Callable, MutableMapping
|
||||
|
||||
|
||||
# Silence warning
|
||||
assert PILLOW_VERSION
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
@ -642,8 +640,6 @@ class Image(object):
|
|||
self.load()
|
||||
|
||||
def _dump(self, file=None, format=None, **options):
|
||||
import tempfile
|
||||
|
||||
suffix = ""
|
||||
if format:
|
||||
suffix = "." + format
|
||||
|
@ -2592,14 +2588,7 @@ def frombuffer(mode, size, data, decoder_name="raw", *args):
|
|||
|
||||
if decoder_name == "raw":
|
||||
if args == ():
|
||||
warnings.warn(
|
||||
"the frombuffer defaults may change in a future release; "
|
||||
"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))
|
||||
|
|
|
@ -25,8 +25,10 @@
|
|||
# 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
|
||||
|
@ -713,9 +715,6 @@ def load_default():
|
|||
|
||||
:return: A font object.
|
||||
"""
|
||||
from io import BytesIO
|
||||
import base64
|
||||
|
||||
f = ImageFont()
|
||||
f._load_pilfont_data(
|
||||
# courB08
|
||||
|
|
|
@ -15,21 +15,18 @@
|
|||
# See the README file for information on usage and redistribution.
|
||||
#
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
from . import Image
|
||||
|
||||
if sys.platform == "win32":
|
||||
grabber = Image.core.grabscreen
|
||||
elif sys.platform == "darwin":
|
||||
import os
|
||||
import tempfile
|
||||
import subprocess
|
||||
else:
|
||||
if sys.platform not in ["win32", "darwin"]:
|
||||
raise ImportError("ImageGrab is macOS and Windows only")
|
||||
|
||||
|
||||
def grab(bbox=None, include_layered_windows=False):
|
||||
def grab(bbox=None, include_layered_windows=False, all_screens=False):
|
||||
if sys.platform == "darwin":
|
||||
fh, filepath = tempfile.mkstemp(".png")
|
||||
os.close(fh)
|
||||
|
@ -37,8 +34,10 @@ def grab(bbox=None, include_layered_windows=False):
|
|||
im = Image.open(filepath)
|
||||
im.load()
|
||||
os.unlink(filepath)
|
||||
if bbox:
|
||||
im = im.crop(bbox)
|
||||
else:
|
||||
size, data = grabber(include_layered_windows)
|
||||
offset, size, data = Image.core.grabscreen(include_layered_windows, all_screens)
|
||||
im = Image.frombytes(
|
||||
"RGB",
|
||||
size,
|
||||
|
@ -50,7 +49,9 @@ def grab(bbox=None, include_layered_windows=False):
|
|||
-1,
|
||||
)
|
||||
if bbox:
|
||||
im = im.crop(bbox)
|
||||
x0, y0 = offset
|
||||
left, top, right, bottom = bbox
|
||||
im = im.crop((left - x0, top - y0, right - x0, bottom - y0))
|
||||
return im
|
||||
|
||||
|
||||
|
|
|
@ -426,7 +426,11 @@ def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)):
|
|||
output_ratio = float(size[0]) / size[1]
|
||||
|
||||
# figure out if the sides or top/bottom will be cropped off
|
||||
if live_size_ratio >= output_ratio:
|
||||
if live_size_ratio == output_ratio:
|
||||
# live_size is already the needed ratio
|
||||
crop_width = live_size[0]
|
||||
crop_height = live_size[1]
|
||||
elif live_size_ratio >= output_ratio:
|
||||
# live_size is wider than what's needed, crop the sides
|
||||
crop_width = output_ratio * live_size[1]
|
||||
crop_height = live_size[1]
|
||||
|
|
|
@ -17,18 +17,12 @@
|
|||
#
|
||||
|
||||
import sys
|
||||
import warnings
|
||||
from io import BytesIO
|
||||
|
||||
from . import Image
|
||||
from ._util import isPath, py3
|
||||
|
||||
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
|
||||
|
|
|
@ -36,7 +36,10 @@ from __future__ import print_function
|
|||
|
||||
import array
|
||||
import io
|
||||
import os
|
||||
import struct
|
||||
import subprocess
|
||||
import tempfile
|
||||
import warnings
|
||||
|
||||
from . import Image, ImageFile, TiffImagePlugin
|
||||
|
@ -444,10 +447,6 @@ class JpegImageFile(ImageFile.ImageFile):
|
|||
|
||||
# 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):
|
||||
|
@ -772,9 +771,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:
|
||||
|
|
|
@ -224,9 +224,11 @@ def _layerinfo(file):
|
|||
# skip over blend flags and extra information
|
||||
read(12) # filler
|
||||
name = ""
|
||||
size = i32(read(4))
|
||||
size = i32(read(4)) # length of the extra data field
|
||||
combined = 0
|
||||
if size:
|
||||
data_end = file.tell() + size
|
||||
|
||||
length = i32(read(4))
|
||||
if length:
|
||||
file.seek(length - 16, io.SEEK_CUR)
|
||||
|
@ -244,7 +246,7 @@ def _layerinfo(file):
|
|||
name = read(length).decode("latin-1", "replace")
|
||||
combined += length + 1
|
||||
|
||||
file.seek(size - combined, io.SEEK_CUR)
|
||||
file.seek(data_end)
|
||||
layers.append((name, mode, (x0, y0, x1, y1)))
|
||||
|
||||
# get tiles
|
||||
|
|
|
@ -236,7 +236,7 @@ def loadImageSeries(filelist=None):
|
|||
def makeSpiderHeader(im):
|
||||
nsam, nrow = im.size
|
||||
lenbyt = nsam * 4 # There are labrec records in the header
|
||||
labrec = 1024 / lenbyt
|
||||
labrec = int(1024 / lenbyt)
|
||||
if 1024 % lenbyt != 0:
|
||||
labrec += 1
|
||||
labbyt = labrec * lenbyt
|
||||
|
|
|
@ -854,7 +854,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)
|
||||
|
||||
|
@ -1079,19 +1079,6 @@ 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()
|
||||
|
@ -1239,8 +1226,8 @@ class TiffImageFile(ImageFile.ImageFile):
|
|||
print("- YCbCr subsampling:", self.tag.get(530))
|
||||
|
||||
# size
|
||||
xsize = self.tag_v2.get(IMAGEWIDTH)
|
||||
ysize = self.tag_v2.get(IMAGELENGTH)
|
||||
xsize = int(self.tag_v2.get(IMAGEWIDTH))
|
||||
ysize = int(self.tag_v2.get(IMAGELENGTH))
|
||||
self._size = xsize, ysize
|
||||
|
||||
if DEBUG:
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -9,7 +9,6 @@ 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.
|
||||
|
||||
;-)
|
||||
"""
|
||||
|
@ -17,9 +16,9 @@ PIL.VERSION is the old PIL version and will be removed in the future.
|
|||
from . import _version
|
||||
|
||||
# VERSION was removed in Pillow 6.0.0.
|
||||
# PILLOW_VERSION is deprecated and will be removed in Pillow 7.0.0.
|
||||
# PILLOW_VERSION was removed in Pillow 7.0.0.
|
||||
# Use __version__ instead.
|
||||
PILLOW_VERSION = __version__ = _version.__version__
|
||||
__version__ = _version.__version__
|
||||
|
||||
del _version
|
||||
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
# Master version for Pillow
|
||||
__version__ = "6.2.0.dev0"
|
||||
__version__ = "7.0.0.dev0"
|
||||
|
|
|
@ -319,18 +319,23 @@ PyImaging_DisplayModeWin32(PyObject* self, PyObject* args)
|
|||
/* -------------------------------------------------------------------- */
|
||||
/* Windows screen grabber */
|
||||
|
||||
typedef HANDLE(__stdcall* Func_SetThreadDpiAwarenessContext)(HANDLE);
|
||||
|
||||
PyObject*
|
||||
PyImaging_GrabScreenWin32(PyObject* self, PyObject* args)
|
||||
{
|
||||
int width, height;
|
||||
int includeLayeredWindows = 0;
|
||||
int x = 0, y = 0, width, height;
|
||||
int includeLayeredWindows = 0, all_screens = 0;
|
||||
HBITMAP bitmap;
|
||||
BITMAPCOREHEADER core;
|
||||
HDC screen, screen_copy;
|
||||
DWORD rop;
|
||||
PyObject* buffer;
|
||||
HANDLE dpiAwareness;
|
||||
HMODULE user32;
|
||||
Func_SetThreadDpiAwarenessContext SetThreadDpiAwarenessContext_function;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "|i", &includeLayeredWindows))
|
||||
if (!PyArg_ParseTuple(args, "|ii", &includeLayeredWindows, &all_screens))
|
||||
return NULL;
|
||||
|
||||
/* step 1: create a memory DC large enough to hold the
|
||||
|
@ -339,8 +344,32 @@ PyImaging_GrabScreenWin32(PyObject* self, PyObject* args)
|
|||
screen = CreateDC("DISPLAY", NULL, NULL, NULL);
|
||||
screen_copy = CreateCompatibleDC(screen);
|
||||
|
||||
// added in Windows 10 (1607)
|
||||
// loaded dynamically to avoid link errors
|
||||
user32 = LoadLibraryA("User32.dll");
|
||||
SetThreadDpiAwarenessContext_function =
|
||||
(Func_SetThreadDpiAwarenessContext)
|
||||
GetProcAddress(user32, "SetThreadDpiAwarenessContext");
|
||||
if (SetThreadDpiAwarenessContext_function != NULL) {
|
||||
// DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = ((DPI_CONTEXT_HANDLE)-3)
|
||||
dpiAwareness = SetThreadDpiAwarenessContext_function((HANDLE) -3);
|
||||
}
|
||||
|
||||
if (all_screens) {
|
||||
x = GetSystemMetrics(SM_XVIRTUALSCREEN);
|
||||
y = GetSystemMetrics(SM_YVIRTUALSCREEN);
|
||||
width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
|
||||
height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
|
||||
} else {
|
||||
width = GetDeviceCaps(screen, HORZRES);
|
||||
height = GetDeviceCaps(screen, VERTRES);
|
||||
}
|
||||
|
||||
if (SetThreadDpiAwarenessContext_function != NULL) {
|
||||
SetThreadDpiAwarenessContext_function(dpiAwareness);
|
||||
}
|
||||
|
||||
FreeLibrary(user32);
|
||||
|
||||
bitmap = CreateCompatibleBitmap(screen, width, height);
|
||||
if (!bitmap)
|
||||
|
@ -354,7 +383,7 @@ PyImaging_GrabScreenWin32(PyObject* self, PyObject* args)
|
|||
rop = SRCCOPY;
|
||||
if (includeLayeredWindows)
|
||||
rop |= CAPTUREBLT;
|
||||
if (!BitBlt(screen_copy, 0, 0, width, height, screen, 0, 0, rop))
|
||||
if (!BitBlt(screen_copy, 0, 0, width, height, screen, x, y, rop))
|
||||
goto error;
|
||||
|
||||
/* step 3: extract bits from bitmap */
|
||||
|
@ -376,7 +405,7 @@ PyImaging_GrabScreenWin32(PyObject* self, PyObject* args)
|
|||
DeleteDC(screen_copy);
|
||||
DeleteDC(screen);
|
||||
|
||||
return Py_BuildValue("(ii)N", width, height, buffer);
|
||||
return Py_BuildValue("(ii)(ii)N", x, y, width, height, buffer);
|
||||
|
||||
error:
|
||||
PyErr_SetString(PyExc_IOError, "screen grab failed");
|
||||
|
|
|
@ -30,7 +30,7 @@ ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt
|
|||
{
|
||||
UINT8* ptr;
|
||||
int framesize;
|
||||
int c, chunks;
|
||||
int c, chunks, advance;
|
||||
int l, lines;
|
||||
int i, j, x = 0, y, ymax;
|
||||
|
||||
|
@ -59,10 +59,16 @@ ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt
|
|||
|
||||
chunks = I16(ptr+6);
|
||||
ptr += 16;
|
||||
bytes -= 16;
|
||||
|
||||
/* Process subchunks */
|
||||
for (c = 0; c < chunks; c++) {
|
||||
UINT8 *data = ptr + 6;
|
||||
UINT8* data;
|
||||
if (bytes < 10) {
|
||||
state->errcode = IMAGING_CODEC_OVERRUN;
|
||||
return -1;
|
||||
}
|
||||
data = ptr + 6;
|
||||
switch (I16(ptr+4)) {
|
||||
case 4: case 11:
|
||||
/* FLI COLOR chunk */
|
||||
|
@ -198,7 +204,9 @@ ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt
|
|||
state->errcode = IMAGING_CODEC_UNKNOWN;
|
||||
return -1;
|
||||
}
|
||||
ptr += I32(ptr);
|
||||
advance = I32(ptr);
|
||||
ptr += advance;
|
||||
bytes -= advance;
|
||||
}
|
||||
|
||||
return -1; /* end of frame */
|
||||
|
|
|
@ -22,6 +22,11 @@ ImagingPcxDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt
|
|||
UINT8 n;
|
||||
UINT8* ptr;
|
||||
|
||||
if (strcmp(im->mode, "1") == 0 && state->xsize > state->bytes * 8) {
|
||||
state->errcode = IMAGING_CODEC_OVERRUN;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ptr = buf;
|
||||
|
||||
for (;;) {
|
||||
|
|
|
@ -33,8 +33,15 @@ ImagingRawDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt
|
|||
|
||||
/* get size of image data and padding */
|
||||
state->bytes = (state->xsize * state->bits + 7) / 8;
|
||||
rawstate->skip = (rawstate->stride) ?
|
||||
rawstate->stride - state->bytes : 0;
|
||||
if (rawstate->stride) {
|
||||
rawstate->skip = rawstate->stride - state->bytes;
|
||||
if (rawstate->skip < 0) {
|
||||
state->errcode = IMAGING_CODEC_CONFIG;
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
rawstate->skip = 0;
|
||||
}
|
||||
|
||||
/* check image orientation */
|
||||
if (state->ystep < 0) {
|
||||
|
|
|
@ -157,6 +157,11 @@ ImagingSgiRleDecode(Imaging im, ImagingCodecState state,
|
|||
c->rlelength = c->lengthtab[c->rowno + c->channo * im->ysize];
|
||||
c->rleoffset -= SGI_HEADER_SIZE;
|
||||
|
||||
if (c->rleoffset + c->rlelength > c->bufsize) {
|
||||
state->errcode = IMAGING_CODEC_OVERRUN;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* row decompression */
|
||||
if (c->bpc ==1) {
|
||||
if(expandrow(&state->buffer[c->channo], &ptr[c->rleoffset], c->rlelength, im->bands))
|
||||
|
|
|
@ -405,8 +405,12 @@ int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, Py_
|
|||
UINT32 strip_row, row_byte_size;
|
||||
UINT8 *new_data;
|
||||
UINT32 rows_per_strip;
|
||||
int ret;
|
||||
|
||||
TIFFGetField(tiff, TIFFTAG_ROWSPERSTRIP, &rows_per_strip);
|
||||
ret = TIFFGetField(tiff, TIFFTAG_ROWSPERSTRIP, &rows_per_strip);
|
||||
if (ret != 1) {
|
||||
rows_per_strip = state->ysize;
|
||||
}
|
||||
TRACE(("RowsPerStrip: %u \n", rows_per_strip));
|
||||
|
||||
// We could use TIFFStripSize, but for YCbCr data it returns subsampled data size
|
||||
|
|
3
winbuild/appveyor_install_pypy3.cmd
Normal file
3
winbuild/appveyor_install_pypy3.cmd
Normal file
|
@ -0,0 +1,3 @@
|
|||
curl -fsSL -o pypy3.zip http://buildbot.pypy.org/nightly/py3.6/pypy-c-jit-97588-7392d01b93d0-win32.zip
|
||||
7z x pypy3.zip -oc:\
|
||||
c:\Python37\Scripts\virtualenv.exe -p c:\pypy-c-jit-97588-7392d01b93d0-win32\pypy3.exe c:\vp\pypy3
|
|
@ -9,6 +9,7 @@ pythons = {
|
|||
"pypy2": {"compiler": 7, "vc": 2010},
|
||||
"35": {"compiler": 7.1, "vc": 2015},
|
||||
"36": {"compiler": 7.1, "vc": 2015},
|
||||
"pypy3": {"compiler": 7.1, "vc": 2015},
|
||||
"37": {"compiler": 7.1, "vc": 2015},
|
||||
# for GitHub Actions
|
||||
"3.5": {"compiler": 7.1, "vc": 2015},
|
||||
|
|
Loading…
Reference in New Issue
Block a user