Merge branch 'master' into pr_2687

This commit is contained in:
Andrew Murray 2019-03-07 23:11:02 +11:00 committed by GitHub
commit fdebb6f2f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 402 additions and 109 deletions

View File

@ -0,0 +1,28 @@
parameters:
name: '' # defaults for any parameters that aren't specified
vmImage: ''
jobs:
- job: ${{ parameters.name }}
pool:
vmImage: ${{ parameters.vmImage }}
strategy:
matrix:
Python37:
python.version: '3.7'
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '$(python.version)'
architecture: 'x64'
- script: |
python -m pip install --upgrade tox
displayName: 'Install dependencies'
- script: |
tox -e lint
displayName: 'Lint'

View File

@ -0,0 +1,22 @@
parameters:
docker: '' # defaults for any parameters that aren't specified
dockerTag: 'master'
name: ''
vmImage: 'Ubuntu-16.04'
jobs:
- job: ${{ parameters.name }}
pool:
vmImage: ${{ parameters.vmImage }}
steps:
- script: |
docker pull pythonpillow/${{ parameters.docker }}:${{ parameters.dockerTag }}
displayName: 'Docker pull'
- script: |
# The Pillow user in the docker container is UID 1000
sudo chown -R 1000 $(Build.SourcesDirectory)
docker run -v $(Build.SourcesDirectory):/Pillow pythonpillow/${{ parameters.docker }}:${{ parameters.dockerTag }}
displayName: 'Docker build'

View File

@ -5,6 +5,39 @@ Changelog (Pillow)
6.0.0 (unreleased)
------------------
- Python 2.7 support will be removed in Pillow 7.0.0 #3682
[hugovk]
- Removed deprecated VERSION #3624
[hugovk]
- Fix 'BytesWarning: Comparison between bytes and string' in PdfDict #3580
[jdufresne]
- Do not resize in Image.thumbnail if already the destination size #3632
[radarhere]
- Replace .seek() magic numbers with io.SEEK_* constants #3572
[jdufresne]
- Make ContainerIO.isatty() return a bool, not int #3568
[jdufresne]
- Add support for I;16 modes for more transpose operations #3563
[radarhere]
- Deprecate support for PyQt4 and PySide #3655
[hugovk, radarhere]
- Add TIFF compression codecs: LZMA, Zstd, WebP #3555
[cgohlke]
- Fixed pickling of iTXt class with protocol > 1 #3537
[radarhere]
- _util.isPath returns True for pathlib.Path objects #3616
[wbadart]
- Remove unnecessary unittest.main() boilerplate from test files #3631
[jdufresne]

View File

@ -23,9 +23,10 @@ exclude .codecov.yml
exclude .editorconfig
exclude .landscape.yaml
exclude .readthedocs.yml
exclude .travis
exclude .travis/*
exclude azure-pipelines.yml
exclude tox.ini
global-exclude .git*
global-exclude *.pyc
global-exclude *.so
prune .azure-pipelines
prune .travis

View File

@ -88,14 +88,7 @@ Released as needed privately to individual vendors for critical security-related
```bash
git clone https://github.com/python-pillow/pillow-wheels
cd pillow-wheels
git submodule init
git submodule update Pillow
cd Pillow
git fetch --all
git checkout [[release tag]]
cd ..
git commit -m "Pillow -> 5.2.0" Pillow
git push
./update-pillow-tag.sh [[release tag]]
```
* [ ] Download distributions from the [Pillow Wheel Builder container](http://a365fff413fe338398b6-1c8a9b3114517dc5fe17b7c3f8c63a43.r19.cf2.rackcdn.com/).
```bash

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View File

@ -11,8 +11,6 @@ class TestSanity(PillowTestCase):
# Make sure we have the binary extension
PIL.Image.core.new("L", (100, 100))
self.assertEqual(PIL.Image.VERSION[:3], '1.1')
# Create an image and do stuff with it.
im = PIL.Image.new("1", (100, 100))
self.assertEqual((im.mode, im.size), ('1', (100, 100)))

View File

@ -16,7 +16,7 @@ class TestFileContainer(PillowTestCase):
im = hopper()
container = ContainerIO.ContainerIO(im, 0, 0)
self.assertEqual(container.isatty(), 0)
self.assertFalse(container.isatty())
def test_seek_mode_0(self):
# Arrange

View File

@ -12,11 +12,11 @@ YA_EXTRA_DIR = "Tests/images/msp"
class TestFileMsp(PillowTestCase):
def test_sanity(self):
file = self.tempfile("temp.msp")
test_file = self.tempfile("temp.msp")
hopper("1").save(file)
hopper("1").save(test_file)
im = Image.open(file)
im = Image.open(test_file)
im.load()
self.assertEqual(im.mode, "1")
self.assertEqual(im.size, (128, 128))

View File

@ -14,12 +14,14 @@ class TestFilePpm(PillowTestCase):
self.assertEqual(im.mode, "RGB")
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "PPM")
self.assertEqual(im.get_format_mimetype(), "image/x-portable-pixmap")
def test_16bit_pgm(self):
im = Image.open('Tests/images/16_bit_binary.pgm')
im.load()
self.assertEqual(im.mode, 'I')
self.assertEqual(im.size, (20, 100))
self.assertEqual(im.get_format_mimetype(), "image/x-portable-graymap")
tgt = Image.open('Tests/images/16_bit_binary_pgm.png')
self.assert_image_equal(im, tgt)
@ -49,3 +51,16 @@ class TestFilePpm(PillowTestCase):
with self.assertRaises(IOError):
Image.open('Tests/images/negative_size.ppm')
def test_mimetypes(self):
path = self.tempfile('temp.pgm')
with open(path, 'w') as f:
f.write("P4\n128 128\n255")
im = Image.open(path)
self.assertEqual(im.get_format_mimetype(), "image/x-portable-bitmap")
with open(path, 'w') as f:
f.write("PyCMYK\n128 128\n255")
im = Image.open(path)
self.assertEqual(im.get_format_mimetype(), "image/x-portable-anymap")

View File

@ -1,4 +1,5 @@
from .helper import PillowTestCase, hopper
from PIL import Image
class TestImageThumbnail(PillowTestCase):
@ -35,3 +36,14 @@ class TestImageThumbnail(PillowTestCase):
im = hopper().resize((128, 128))
im.thumbnail((100, 100))
self.assert_image(im, im.mode, (100, 100))
def test_no_resize(self):
# Check that draft() can resize the image to the destination size
im = Image.open("Tests/images/hopper.jpg")
im.draft(None, (64, 64))
self.assertEqual(im.size, (64, 64))
# Test thumbnail(), where only draft() is necessary to resize the image
im = Image.open("Tests/images/hopper.jpg")
im.thumbnail((64, 64))
self.assert_image(im, im.mode, (64, 64))

View File

@ -7,10 +7,9 @@ from PIL.Image import (FLIP_LEFT_RIGHT, FLIP_TOP_BOTTOM, ROTATE_90, ROTATE_180,
class TestImageTranspose(PillowTestCase):
hopper = {
'L': helper.hopper('L').crop((0, 0, 121, 127)).copy(),
'RGB': helper.hopper('RGB').crop((0, 0, 121, 127)).copy(),
}
hopper = {mode: helper.hopper(mode).crop((0, 0, 121, 127)).copy() for mode in [
'L', 'RGB', 'I;16', 'I;16L', 'I;16B'
]}
def test_flip_left_right(self):
def transpose(mode):
@ -25,7 +24,7 @@ class TestImageTranspose(PillowTestCase):
self.assertEqual(im.getpixel((1, y-2)), out.getpixel((x-2, y-2)))
self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((1, y-2)))
for mode in ("L", "RGB"):
for mode in ("L", "RGB", "I;16", "I;16L", "I;16B"):
transpose(mode)
def test_flip_top_bottom(self):
@ -41,7 +40,7 @@ class TestImageTranspose(PillowTestCase):
self.assertEqual(im.getpixel((1, y-2)), out.getpixel((1, 1)))
self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((x-2, 1)))
for mode in ("L", "RGB"):
for mode in ("L", "RGB", "I;16", "I;16L", "I;16B"):
transpose(mode)
def test_rotate_90(self):
@ -73,7 +72,7 @@ class TestImageTranspose(PillowTestCase):
self.assertEqual(im.getpixel((1, y-2)), out.getpixel((x-2, 1)))
self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((1, 1)))
for mode in ("L", "RGB"):
for mode in ("L", "RGB", "I;16", "I;16L", "I;16B"):
transpose(mode)
def test_rotate_270(self):

View File

@ -1,7 +1,16 @@
from .helper import PillowTestCase, hopper
from PIL import ImageQt
import warnings
deprecated = False
with warnings.catch_warnings():
warnings.filterwarnings("error", category=DeprecationWarning)
try:
from PIL import ImageQt
except DeprecationWarning:
deprecated = True
warnings.filterwarnings("ignore", category=DeprecationWarning)
from PIL import ImageQt
if ImageQt.qt_is_installed:
from PIL.ImageQt import qRgba
@ -78,3 +87,6 @@ class TestImageQt(PillowQtTestCase, PillowTestCase):
def test_image(self):
for mode in ('1', 'RGB', 'RGBA', 'L', 'P'):
ImageQt.ImageQt(hopper(mode))
def test_deprecated(self):
self.assertEqual(ImageQt.qt_version in ["4", "side"], deprecated)

View File

@ -67,9 +67,13 @@ class TestPickle(PillowTestCase):
"Tests/images/non_zero_bb.png",
"Tests/images/non_zero_bb_scale2.png",
"Tests/images/p_trns_single.png",
"Tests/images/pil123p.png"
"Tests/images/pil123p.png",
"Tests/images/itxt_chunks.png"
]:
self.helper_pickle_string(pickle, test_file=test_file)
for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1):
self.helper_pickle_string(pickle,
protocol=protocol,
test_file=test_file)
def test_pickle_l_mode(self):
# Arrange

View File

@ -1,4 +1,4 @@
from .helper import PillowTestCase
from .helper import unittest, PillowTestCase
from PIL import _util
@ -35,6 +35,18 @@ class TestUtil(PillowTestCase):
# Assert
self.assertTrue(it_is)
@unittest.skipIf(not _util.py36, 'os.path support for Paths added in 3.6')
def test_path_obj_is_path(self):
# Arrange
from pathlib import Path
test_path = Path('filename.ext')
# Act
it_is = _util.isPath(test_path)
# Assert
self.assertTrue(it_is)
def test_is_not_path(self):
# Arrange
filename = self.tempfile("temp.ext")

67
azure-pipelines.yml Normal file
View File

@ -0,0 +1,67 @@
# Python package
# Create and test a Python package on multiple Python versions.
# Add steps that analyze code, save the dist with the build record,
# publish to a PyPI-compatible index, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/python
jobs:
- template: .azure-pipelines/jobs/lint.yml
parameters:
name: Lint
vmImage: 'Ubuntu-16.04'
- template: .azure-pipelines/jobs/test-docker.yml
parameters:
docker: 'alpine'
name: 'alpine'
- template: .azure-pipelines/jobs/test-docker.yml
parameters:
docker: 'arch'
name: 'arch'
- template: .azure-pipelines/jobs/test-docker.yml
parameters:
docker: 'ubuntu-trusty-x86'
name: 'ubuntu_trusty_x86'
- template: .azure-pipelines/jobs/test-docker.yml
parameters:
docker: 'ubuntu-xenial-amd64'
name: 'ubuntu_xenial_amd64'
- template: .azure-pipelines/jobs/test-docker.yml
parameters:
docker: 'debian-stretch-x86'
name: 'debian_stretch_x86'
- template: .azure-pipelines/jobs/test-docker.yml
parameters:
docker: 'centos-6-amd64'
name: 'centos_6_amd64'
- template: .azure-pipelines/jobs/test-docker.yml
parameters:
docker: 'centos-7-amd64'
name: 'centos_7_amd64'
- template: .azure-pipelines/jobs/test-docker.yml
parameters:
docker: 'amazon-1-amd64'
name: 'amazon_1_amd64'
- template: .azure-pipelines/jobs/test-docker.yml
parameters:
docker: 'amazon-2-amd64'
name: 'amazon_2_amd64'
- template: .azure-pipelines/jobs/test-docker.yml
parameters:
docker: 'fedora-28-amd64'
name: 'fedora_28_amd64'
- template: .azure-pipelines/jobs/test-docker.yml
parameters:
docker: 'fedora-29-amd64'
name: 'fedora_29_amd64'

View File

@ -12,6 +12,27 @@ Deprecated features
Below are features which are considered deprecated. Where appropriate,
a ``DeprecationWarning`` is issued.
Python 2.7
~~~~~~~~~~
.. deprecated:: 6.0.0
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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -50,13 +71,12 @@ a ``DeprecationWarning``:
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 and VERSION constants
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PILLOW_VERSION constant
~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 5.2.0
Two version constants ``VERSION`` (the old PIL version, always 1.1.7) and
``PILLOW_VERSION`` have been deprecated and will be removed in the next
``PILLOW_VERSION`` has been deprecated and will be removed in the next
major release. Use ``__version__`` instead.
Removed features
@ -65,6 +85,14 @@ Removed features
Deprecated features are only removed in major releases after an appropriate
period of deprecation has passed.
VERSION constant
~~~~~~~~~~~~~~~~
*Removed in version 6.0.0.*
``VERSION`` (the old PIL version, always 1.1.7) has been removed. Use
``__version__`` instead.
Undocumented ImageOps functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -15,21 +15,23 @@ Notes
.. note:: Pillow is supported on the following Python versions
+--------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
+---------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
|**Python** |**2.4**|**2.5**|**2.6**|**2.7**|**3.2**|**3.3**|**3.4**|**3.5**|**3.6**|**3.7**|
+--------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
+---------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
|Pillow < 2.0.0 | Yes | Yes | Yes | Yes | | | | | | |
+--------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
+---------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
|Pillow 2.x - 3.x | | | Yes | Yes | Yes | Yes | Yes | Yes | | |
+--------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
+---------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
|Pillow 4.x | | | | Yes | | Yes | Yes | Yes | Yes | |
+--------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
+---------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
|Pillow 5.0.x - 5.1.x | | | | Yes | | | Yes | Yes | Yes | |
+--------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
+---------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
|Pillow 5.2.x - 5.4.x | | | | Yes | | | Yes | Yes | Yes | Yes |
+--------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
|Pillow >= 6.0.0 | | | | Yes | | | | Yes | Yes | Yes |
+--------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
+---------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
|Pillow 6.x | | | | Yes | | | | Yes | Yes | Yes |
+---------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
|Pillow >= 7.0.0 | | | | | | | | Yes | Yes | Yes |
+---------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
Basic Installation
------------------

View File

@ -7,6 +7,12 @@
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.
.. versionadded:: 1.1.6
.. py:class:: ImageQt.ImageQt(image)

View File

@ -26,12 +26,38 @@ Several undocumented functions in ``ImageOps`` were deprecated in Pillow 4.3.0 (
and have now been removed: ``gaussian_blur``, ``gblur``, ``unsharp_mask``, ``usm`` and
``box_blur``. Use the equivalent operations in ``ImageFilter`` instead.
Removed deprecated VERSION
^^^^^^^^^^^^^^^^^^^^^^^^^^
``VERSION`` (the old PIL version, always 1.1.7) has been removed. Use ``__version__``
instead.
API Changes
===========
Deprecations
^^^^^^^^^^^^
Python 2.7
~~~~~~~~~~
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
~~~~~~~~~~~~~~~~
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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
These version constants have been deprecated and will be removed in a future
version.

View File

@ -18,6 +18,8 @@
# A file object that provides read access to a part of an existing
# file (for example a TAR file).
import io
class ContainerIO(object):
@ -39,9 +41,9 @@ class ContainerIO(object):
# Always false.
def isatty(self):
return 0
return False
def seek(self, offset, mode=0):
def seek(self, offset, mode=io.SEEK_SET):
"""
Move file pointer.

View File

@ -104,7 +104,7 @@ def Ghostscript(tile, size, fp, scale=1):
# Copy whole file to read in Ghostscript
with open(infile_temp, 'wb') as f:
# fetch length of fp
fp.seek(0, 2)
fp.seek(0, io.SEEK_END)
fsize = fp.tell()
# ensure start position
# go back
@ -169,7 +169,7 @@ class PSFile(object):
self.fp = fp
self.char = None
def seek(self, offset, whence=0):
def seek(self, offset, whence=io.SEEK_SET):
self.char = None
self.fp.seek(offset, whence)
@ -312,7 +312,7 @@ class EpsImageFile(ImageFile.ImageFile):
if s[:4] == b"%!PS":
# for HEAD without binary preview
fp.seek(0, 2)
fp.seek(0, io.SEEK_END)
length = fp.tell()
offset = 0
elif i32(s[0:4]) == 0xC6D3D0C5:

View File

@ -604,16 +604,16 @@ def _save_netpbm(im, fp, filename):
import os
from subprocess import Popen, check_call, PIPE, CalledProcessError
file = im._dump()
tempfile = im._dump()
with open(filename, 'wb') as f:
if im.mode != "RGB":
with open(os.devnull, 'wb') as devnull:
check_call(["ppmtogif", file], stdout=f, stderr=devnull)
check_call(["ppmtogif", tempfile], stdout=f, stderr=devnull)
else:
# Pipe ppmquant output into ppmtogif
# "ppmquant 256 %s | ppmtogif > %s" % (file, filename)
quant_cmd = ["ppmquant", "256", file]
# "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)
@ -632,7 +632,7 @@ def _save_netpbm(im, fp, filename):
raise CalledProcessError(retcode, togif_cmd)
try:
os.unlink(file)
os.unlink(tempfile)
except OSError:
pass

View File

@ -195,7 +195,7 @@ class IcnsFile(object):
i += HEADERSIZE
blocksize -= HEADERSIZE
dct[sig] = (i, blocksize)
fobj.seek(blocksize, 1)
fobj.seek(blocksize, io.SEEK_CUR)
i += blocksize
def itersizes(self):

View File

@ -40,10 +40,10 @@ if False:
from PyQt5.QtGui import QImage, QPixmap
# VERSION is deprecated and will be removed in Pillow 6.0.0.
# PILLOW_VERSION is deprecated and will be removed after that.
# VERSION was removed in Pillow 6.0.0.
# PILLOW_VERSION is deprecated and will be removed in Pillow 7.0.0.
# Use __version__ instead.
from . import VERSION, PILLOW_VERSION, __version__, _plugins
from . import PILLOW_VERSION, __version__, _plugins
from ._util import py3
import logging
@ -76,8 +76,7 @@ except ImportError:
from collections import Callable
# Silence warnings
assert VERSION
# Silence warning
assert PILLOW_VERSION
logger = logging.getLogger(__name__)
@ -680,8 +679,7 @@ class Image(object):
def __eq__(self, other):
# type: (object) -> bool
return (isinstance(other, Image) and
self.__class__.__name__ == other.__class__.__name__ and
return (self.__class__ is other.__class__ and
self.mode == other.mode and
self.size == other.size and
self.info == other.info and
@ -709,8 +707,7 @@ class Image(object):
:returns: png version of the image as bytes
"""
from io import BytesIO
b = BytesIO()
b = io.BytesIO()
self.save(b, 'PNG')
return b.getvalue()
@ -2138,10 +2135,10 @@ class Image(object):
debugging purposes.
On Unix platforms, this method saves the image to a temporary
PPM file, and calls either the **xv** utility or the **display**
PPM file, and calls the **display**, **eog** or **xv**
utility, depending on which one can be found.
On macOS, this method saves the image to a temporary BMP file, and
On macOS, this method saves the image to a temporary PNG file, and
opens it with the native Preview application.
On Windows, it saves the image to a temporary BMP file, and uses
@ -2251,11 +2248,12 @@ class Image(object):
self.draft(None, size)
if self.size != size:
im = self.resize(size, resample)
self.im = im.im
self.mode = im.mode
self._size = size
self.mode = self.im.mode
self.readonly = 0
self.pyaccess = None

View File

@ -950,5 +950,5 @@ def versions():
return (
VERSION, core.littlecms_version,
sys.version.split()[0], Image.VERSION
sys.version.split()[0], Image.__version__
)

View File

@ -525,7 +525,7 @@ def _save(im, fp, tile, bufsize=0):
for e, b, o, a in tile:
e = Image._getencoder(im.mode, e, a, im.encoderconfig)
if o > 0:
fp.seek(o, 0)
fp.seek(o)
e.setimage(im.im, b)
if e.pushes_fd:
e.setfd(fp)
@ -544,7 +544,7 @@ def _save(im, fp, tile, bufsize=0):
for e, b, o, a in tile:
e = Image._getencoder(im.mode, e, a, im.encoderconfig)
if o > 0:
fp.seek(o, 0)
fp.seek(o)
e.setimage(im.im, b)
if e.pushes_fd:
e.setfd(fp)

View File

@ -28,7 +28,7 @@ VERBOSE = 0
def _isconstant(v):
return isinstance(v, int) or isinstance(v, float)
return isinstance(v, (int, float))
class _Operand(object):

View File

@ -20,6 +20,7 @@ from . import Image
from ._util import isPath, py3
from io import BytesIO
import sys
import warnings
qt_versions = [
['5', 'PyQt5'],
@ -27,6 +28,12 @@ qt_versions = [
['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."
)
# 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)
@ -41,9 +48,13 @@ for qt_version, qt_module in qt_versions:
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
@ -67,7 +78,7 @@ def fromqimage(im):
"""
buffer = QBuffer()
buffer.open(QIODevice.ReadWrite)
# preserve alha channel with png
# preserve alpha channel with png
# otherwise ppm is more friendly with Image.open
if im.hasAlphaChannel():
im.save(buffer, 'png')

View File

@ -193,9 +193,9 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
fd = -1
try:
pos = self.fp.tell()
self.fp.seek(0, 2)
self.fp.seek(0, io.SEEK_END)
length = self.fp.tell()
self.fp.seek(pos, 0)
self.fp.seek(pos)
except Exception:
length = -1

View File

@ -16,6 +16,7 @@
# See the README file for information on usage and redistribution.
#
import io
from . import Image, FontFile
from ._binary import i8, i16le as l16, i32le as l32, i16be as b16, i32be as b32
@ -117,7 +118,7 @@ class PcfFontFile(FontFile.FontFile):
for i in range(nprops):
p.append((i32(fp.read(4)), i8(fp.read(1)), i32(fp.read(4))))
if nprops & 3:
fp.seek(4 - (nprops & 3), 1) # pad
fp.seek(4 - (nprops & 3), io.SEEK_CUR) # pad
data = fp.read(i32(fp.read(4)))

View File

@ -25,6 +25,7 @@
# See the README file for information on usage and redistribution.
#
import io
import logging
from . import Image, ImageFile, ImagePalette
from ._binary import i8, i16le as i16, o8, o16le as o16
@ -82,7 +83,7 @@ class PcxImageFile(ImageFile.ImageFile):
elif version == 5 and bits == 8 and planes == 1:
mode = rawmode = "L"
# FIXME: hey, this doesn't work with the incremental loader !!!
self.fp.seek(-769, 2)
self.fp.seek(-769, io.SEEK_END)
s = self.fp.read(769)
if len(s) == 769 and i8(s[0]) == 12:
# check if the palette is linear greyscale

View File

@ -269,14 +269,9 @@ class PdfDict(UserDict):
else:
self.__dict__[key] = value
else:
if isinstance(key, str):
key = key.encode("us-ascii")
self[key] = value
self[key.encode("us-ascii")] = value
def __getattr__(self, key):
try:
value = self[key]
except KeyError:
try:
value = self[key.encode("us-ascii")]
except KeyError:
@ -361,8 +356,7 @@ def pdf_repr(x):
return b"false"
elif x is None:
return b"null"
elif (isinstance(x, PdfName) or isinstance(x, PdfDict) or
isinstance(x, PdfArray) or isinstance(x, PdfBinary)):
elif isinstance(x, (PdfName, PdfDict, PdfArray, PdfBinary)):
return bytes(x)
elif isinstance(x, int):
return str(x).encode("us-ascii")

View File

@ -193,7 +193,7 @@ class iTXt(str):
"""
@staticmethod
def __new__(cls, text, lang, tkey):
def __new__(cls, text, lang=None, tkey=None):
"""
:param cls: the class to use when creating the instance
:param text: value for this key

View File

@ -70,7 +70,14 @@ class PpmImageFile(ImageFile.ImageFile):
s = self.fp.read(1)
if s != b"P":
raise SyntaxError("not a PPM file")
mode = MODES[self._token(s)]
magic_number = self._token(s)
mode = MODES[magic_number]
self.custom_mimetype = {
b"P4": "image/x-portable-bitmap",
b"P5": "image/x-portable-graymap",
b"P6": "image/x-portable-pixmap",
}.get(magic_number)
if mode == "1":
self.mode = "1"
@ -158,3 +165,5 @@ Image.register_open(PpmImageFile.format, PpmImageFile, _accept)
Image.register_save(PpmImageFile.format, _save)
Image.register_extensions(PpmImageFile.format, [".pbm", ".pgm", ".ppm"])
Image.register_mime(PpmImageFile.format, "image/x-portable-anymap")

View File

@ -20,6 +20,7 @@
# PIL.__version__ instead.
__version__ = "0.4"
import io
from . import Image, ImageFile, ImagePalette
from ._binary import i8, i16be as i16, i32be as i32
@ -216,12 +217,12 @@ def _layerinfo(file):
if size:
length = i32(read(4))
if length:
file.seek(length - 16, 1)
file.seek(length - 16, io.SEEK_CUR)
combined += length + 4
length = i32(read(4))
if length:
file.seek(length, 1)
file.seek(length, io.SEEK_CUR)
combined += length + 4
length = i8(read(1))
@ -231,7 +232,7 @@ def _layerinfo(file):
name = read(length).decode('latin-1', 'replace')
combined += length + 1
file.seek(size - combined, 1)
file.seek(size - combined, io.SEEK_CUR)
layers.append((name, mode, (x0, y0, x1, y1)))
# get tiles

View File

@ -14,6 +14,7 @@
# See the README file for information on usage and redistribution.
#
import io
import sys
from . import ContainerIO
@ -51,7 +52,7 @@ class TarIO(ContainerIO.ContainerIO):
if file == name:
break
self.fh.seek((size + 511) & (~511), 1)
self.fh.seek((size + 511) & (~511), io.SEEK_CUR)
# Open region
ContainerIO.ContainerIO.__init__(self, self.fh, self.fh.tell(), size)

View File

@ -135,6 +135,9 @@ COMPRESSION_INFO = {
32946: "tiff_deflate",
34676: "tiff_sgilog",
34677: "tiff_sgilog24",
34925: "lzma",
50000: "zstd",
50001: "webp",
}
COMPRESSION_INFO_REV = {v: k for k, v in COMPRESSION_INFO.items()}
@ -423,7 +426,7 @@ class ImageFileDirectory_v2(MutableMapping):
ifd = ImageFileDirectory_v2()
ifd[key] = 'Some Data'
ifd.tagtype[key] = 2
ifd.tagtype[key] = TiffTags.ASCII
print(ifd[key])
'Some Data'
@ -557,7 +560,7 @@ class ImageFileDirectory_v2(MutableMapping):
if info.type:
self.tagtype[tag] = info.type
else:
self.tagtype[tag] = 7
self.tagtype[tag] = TiffTags.UNDEFINED
if all(isinstance(v, IFDRational) for v in values):
self.tagtype[tag] = TiffTags.RATIONAL
elif all(isinstance(v, int) for v in values):
@ -872,7 +875,7 @@ class ImageFileDirectory_v1(ImageFileDirectory_v2):
ifd = ImageFileDirectory_v1()
ifd[key] = 'Some Data'
ifd.tagtype[key] = 2
ifd.tagtype[key] = TiffTags.ASCII
print(ifd[key])
('Some Data',)
@ -1436,7 +1439,7 @@ def _save(im, fp, filename):
try:
ifd.tagtype[key] = info.tagtype[key]
except Exception:
pass # might not be an IFD, Might not have populated type
pass # might not be an IFD. Might not have populated type
# additions written by Greg Couch, gregc@cgl.ucsf.edu
# inspired by image-sig posting from Kevin Cazabon, kcazabon@home.com
@ -1680,7 +1683,7 @@ class AppendingTiffWriter:
def tell(self):
return self.f.tell() - self.offsetOfNewPage
def seek(self, offset, whence):
def seek(self, offset, whence=io.SEEK_SET):
if whence == os.SEEK_SET:
offset += self.offsetOfNewPage

View File

@ -16,10 +16,9 @@ PIL.VERSION is the old PIL version and will be removed in the future.
from . import _version
# VERSION is deprecated and will be removed in Pillow 6.0.0.
# PILLOW_VERSION is deprecated and will be removed after that.
# VERSION was removed in Pillow 6.0.0.
# PILLOW_VERSION is deprecated and will be removed in Pillow 7.0.0.
# Use __version__ instead.
VERSION = '1.1.7' # PIL Version
PILLOW_VERSION = __version__ = _version.__version__
del _version

View File

@ -2,11 +2,18 @@ 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
def isPath(f):
return isinstance(f, (bytes, str, Path))
else:
def isPath(f):
return isinstance(f, (bytes, str))
else:

View File

@ -39,7 +39,11 @@ ImagingFlipLeftRight(Imaging imOut, Imaging imIn)
ImagingSectionEnter(&cookie);
if (imIn->image8) {
if (strncmp(imIn->mode, "I;16", 4) == 0) {
FLIP_LEFT_RIGHT(UINT16, image8)
} else {
FLIP_LEFT_RIGHT(UINT8, image8)
}
} else {
FLIP_LEFT_RIGHT(INT32, image32)
}
@ -253,7 +257,11 @@ ImagingRotate180(Imaging imOut, Imaging imIn)
yr = imIn->ysize-1;
if (imIn->image8) {
if (strncmp(imIn->mode, "I;16", 4) == 0) {
ROTATE_180(UINT16, image8)
} else {
ROTATE_180(UINT8, image8)
}
} else {
ROTATE_180(INT32, image32)
}