Merge remote-tracking branch 'remotes/upstream/master' into tiff-old-jpeg

This commit is contained in:
Konstantin Kopachev 2019-03-11 10:38:02 -07:00
commit 28c6f555a2
No known key found for this signature in database
GPG Key ID: CECF757E656F4F62
55 changed files with 722 additions and 234 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

@ -34,6 +34,6 @@ The best reproductions are self-contained scripts with minimal dependencies. If
## Security vulnerabilities
To report sensitive vulnerability information, email security@python-pillow.org.
To report sensitive vulnerability information, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure.
If your organisation/employer is a distributor of Pillow and would like advance notification of security-related bugs, please let us know your preferred contact method.

View File

@ -5,6 +5,54 @@ Changelog (Pillow)
6.0.0 (unreleased)
------------------
- Python 2.7 support will be removed in Pillow 7.0.0 #3682
[hugovk]
- Add EXIF support for PNG #3674
[radarhere]
- Add option to set dither param on quantize #3699
[glasnt]
- Add reading of DDS uncompressed RGB data #3673
[radarhere]
- Correct length of Tiff BYTE tags #3672
[radarhere]
- Add DIB saving and loading through Image open #3691
[radarhere]
- 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

BIN
Tests/images/exif.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
Tests/images/uncompressed_rgb.dds Executable file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

Binary file not shown.

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,6 +16,7 @@ class TestFileBmp(PillowTestCase):
self.assertEqual(im.mode, reloaded.mode)
self.assertEqual(im.size, reloaded.size)
self.assertEqual(reloaded.format, "BMP")
self.assertEqual(reloaded.get_format_mimetype(), "image/bmp")
def test_sanity(self):
self.roundtrip(hopper())
@ -72,6 +73,20 @@ class TestFileBmp(PillowTestCase):
def test_load_dib(self):
# test for #1293, Imagegrab returning Unsupported Bitfields Format
im = BmpImagePlugin.DibImageFile('Tests/images/clipboard.dib')
im = Image.open('Tests/images/clipboard.dib')
self.assertEqual(im.format, "DIB")
self.assertEqual(im.get_format_mimetype(), "image/bmp")
target = Image.open('Tests/images/clipboard_target.png')
self.assert_image_equal(im, target)
def test_save_dib(self):
outfile = self.tempfile("temp.dib")
im = Image.open('Tests/images/clipboard.dib')
im.save(outfile)
reloaded = Image.open(outfile)
self.assertEqual(reloaded.format, "DIB")
self.assertEqual(reloaded.get_format_mimetype(), "image/bmp")
self.assert_image_equal(im, reloaded)

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

@ -7,6 +7,7 @@ TEST_FILE_DXT1 = "Tests/images/dxt1-rgb-4bbp-noalpha_MipMaps-1.dds"
TEST_FILE_DXT3 = "Tests/images/dxt3-argb-8bbp-explicitalpha_MipMaps-1.dds"
TEST_FILE_DXT5 = "Tests/images/dxt5-argb-8bbp-interpolatedalpha_MipMaps-1.dds"
TEST_FILE_DX10_BC7 = "Tests/images/bc7-argb-8bpp_MipMaps-1.dds"
TEST_FILE_UNCOMPRESSED_RGB = "Tests/images/uncompressed_rgb.dds"
class TestFileDds(PillowTestCase):
@ -67,6 +68,24 @@ class TestFileDds(PillowTestCase):
self.assert_image_equal(target, im)
def test_unimplemented_dxgi_format(self):
self.assertRaises(NotImplementedError, Image.open,
"Tests/images/unimplemented_dxgi_format.dds")
def test_uncompressed_rgb(self):
"""Check uncompressed RGB images can be opened"""
target = Image.open(TEST_FILE_UNCOMPRESSED_RGB.replace('.dds', '.png'))
im = Image.open(TEST_FILE_UNCOMPRESSED_RGB)
im.load()
self.assertEqual(im.format, "DDS")
self.assertEqual(im.mode, "RGBA")
self.assertEqual(im.size, (800, 600))
self.assert_image_equal(target, im)
def test__validate_true(self):
"""Check valid prefix"""
# Arrange
@ -110,3 +129,7 @@ class TestFileDds(PillowTestCase):
im.load()
self.assertRaises(IOError, short_file)
def test_unimplemented_pixel_format(self):
self.assertRaises(NotImplementedError, Image.open,
"Tests/images/unimplemented_pixel_format.dds")

View File

@ -234,11 +234,11 @@ class TestFileLibTiff(LibTiffTestCase):
def test_custom_metadata(self):
custom = {
37000: 4,
37001: 4.2,
37002: 'custom tag value',
37003: u'custom tag value',
37004: b'custom tag value'
37000: [4, TiffTags.SHORT],
37001: [4.2, TiffTags.RATIONAL],
37002: ['custom tag value', TiffTags.ASCII],
37003: [u'custom tag value', TiffTags.ASCII],
37004: [b'custom tag value', TiffTags.BYTE]
}
libtiff_version = TiffImagePlugin._libtiff_version()
@ -251,17 +251,33 @@ class TestFileLibTiff(LibTiffTestCase):
for libtiff in libtiffs:
TiffImagePlugin.WRITE_LIBTIFF = libtiff
im = hopper()
def check_tags(tiffinfo):
im = hopper()
out = self.tempfile("temp.tif")
im.save(out, tiffinfo=custom)
TiffImagePlugin.WRITE_LIBTIFF = False
out = self.tempfile("temp.tif")
im.save(out, tiffinfo=tiffinfo)
reloaded = Image.open(out)
for tag, value in custom.items():
if libtiff and isinstance(value, bytes):
value = value.decode()
self.assertEqual(reloaded.tag_v2[tag], value)
reloaded = Image.open(out)
for tag, value in tiffinfo.items():
reloaded_value = reloaded.tag_v2[tag]
if isinstance(reloaded_value, TiffImagePlugin.IFDRational):
reloaded_value = float(reloaded_value)
if libtiff and isinstance(value, bytes):
value = value.decode()
self.assertEqual(reloaded_value, value)
# Test with types
ifd = TiffImagePlugin.ImageFileDirectory_v2()
for tag, tagdata in custom.items():
ifd[tag] = tagdata[0]
ifd.tagtype[tag] = tagdata[1]
check_tags(ifd)
# Test without types
check_tags({tag: tagdata[0] for tag, tagdata in custom.items()})
TiffImagePlugin.WRITE_LIBTIFF = False
def test_int_dpi(self):
# issue #1765

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

@ -590,6 +590,40 @@ class TestFilePng(PillowTestCase):
im = Image.open("Tests/images/hopper_idat_after_image_end.png")
self.assertEqual(im.text, {'TXT': 'VALUE', 'ZIP': 'VALUE'})
def test_exif(self):
im = Image.open("Tests/images/exif.png")
exif = im._getexif()
self.assertEqual(exif[274], 1)
def test_exif_save(self):
im = Image.open("Tests/images/exif.png")
test_file = self.tempfile("temp.png")
im.save(test_file)
reloaded = Image.open(test_file)
exif = reloaded._getexif()
self.assertEqual(exif[274], 1)
def test_exif_from_jpg(self):
im = Image.open("Tests/images/pil_sample_rgb.jpg")
test_file = self.tempfile("temp.png")
im.save(test_file)
reloaded = Image.open(test_file)
exif = reloaded._getexif()
self.assertEqual(exif[305], "Adobe Photoshop CS Macintosh")
def test_exif_argument(self):
im = Image.open(TEST_PNG_FILE)
test_file = self.tempfile("temp.png")
im.save(test_file, exif=b"exifstring")
reloaded = Image.open(test_file)
self.assertEqual(reloaded.info["exif"], b"Exif\x00\x00exifstring")
@unittest.skipUnless(HAVE_WEBP and _webp.HAVE_WEBPANIM,
"WebP support not installed with animation")
def test_apng(self):

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

@ -46,3 +46,19 @@ class TestImageQuantize(PillowTestCase):
converted = image.quantize()
self.assert_image(converted, 'P', converted.size)
self.assert_image_similar(converted.convert('RGB'), image, 1)
def test_quantize_no_dither(self):
image = hopper()
palette = Image.open('Tests/images/caption_6_33_22.png').convert('P')
converted = image.quantize(dither=0, palette=palette)
self.assert_image(converted, 'P', converted.size)
def test_quantize_dither_diff(self):
image = hopper()
palette = Image.open('Tests/images/caption_6_33_22.png').convert('P')
dither = image.quantize(dither=1, palette=palette)
nodither = image.quantize(dither=0, palette=palette)
self.assertNotEqual(dither.tobytes(), nodither.tobytes())

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

@ -21,7 +21,7 @@ Fully supported formats
BMP
^^^
PIL reads and writes Windows and OS/2 BMP files containing ``1``, ``L``, ``P``,
Pillow reads and writes Windows and OS/2 BMP files containing ``1``, ``L``, ``P``,
or ``RGB`` data. 16-colour images are read as ``P`` images. Run-length encoding
is not supported.
@ -31,13 +31,21 @@ The :py:meth:`~PIL.Image.Image.open` method sets the following
**compression**
Set to ``bmp_rle`` if the file is run-length encoded.
DIB
^^^
Pillow reads and writes DIB files. DIB files are similar to BMP files, so see
above for more information.
.. versionadded:: 6.0.0
EPS
^^^
PIL identifies EPS files containing image data, and can read files that contain
embedded raster images (ImageData descriptors). If Ghostscript is available,
other EPS files can be read as well. The EPS driver can also write EPS
images. The EPS driver can read EPS images in ``L``, ``LAB``, ``RGB`` and
Pillow identifies EPS files containing image data, and can read files that
contain embedded raster images (ImageData descriptors). If Ghostscript is
available, other EPS files can be read as well. The EPS driver can also write
EPS images. The EPS driver can read EPS images in ``L``, ``LAB``, ``RGB`` and
``CMYK`` mode, but Ghostscript may convert the images to ``RGB`` mode rather
than leaving them in the original color space. The EPS driver can write images
in ``L``, ``RGB`` and ``CMYK`` modes.
@ -59,8 +67,8 @@ method with the following parameter to affect how Ghostscript renders the EPS
GIF
^^^
PIL reads GIF87a and GIF89a versions of the GIF file format. The library writes
run-length encoded files in GIF87a by default, unless GIF89a features
Pillow reads GIF87a and GIF89a versions of the GIF file format. The library
writes run-length encoded files in GIF87a by default, unless GIF89a features
are used or GIF89a is already in use.
Note that GIF files are always read as grayscale (``L``)
@ -192,7 +200,7 @@ attributes before loading the file::
ICNS
^^^^
PIL reads and (macOS only) writes macOS ``.icns`` files. By default, the
Pillow reads and (macOS only) writes macOS ``.icns`` files. By default, the
largest available icon is read, though you can override this by setting the
:py:attr:`~PIL.Image.Image.size` property before calling
:py:meth:`~PIL.Image.Image.load`. The :py:meth:`~PIL.Image.Image.open` method
@ -237,12 +245,12 @@ IM is a format used by LabEye and other applications based on the IFUNC image
processing library. The library reads and writes most uncompressed interchange
versions of this format.
IM is the only format that can store all internal PIL formats.
IM is the only format that can store all internal Pillow formats.
JPEG
^^^^
PIL reads JPEG, JFIF, and Adobe JPEG files containing ``L``, ``RGB``, or
Pillow reads JPEG, JFIF, and Adobe JPEG files containing ``L``, ``RGB``, or
``CMYK`` data. It writes standard and progressive JFIF files.
Using the :py:meth:`~PIL.Image.Image.draft` method, you can speed things up by
@ -354,15 +362,15 @@ JPEG 2000
.. versionadded:: 2.4.0
PIL reads and writes JPEG 2000 files containing ``L``, ``LA``, ``RGB`` or
Pillow reads and writes JPEG 2000 files containing ``L``, ``LA``, ``RGB`` or
``RGBA`` data. It can also read files containing ``YCbCr`` data, which it
converts on read into ``RGB`` or ``RGBA`` depending on whether or not there is
an alpha channel. PIL supports JPEG 2000 raw codestreams (``.j2k`` files), as
well as boxed JPEG 2000 files (``.j2p`` or ``.jpx`` files). PIL does *not*
support files whose components have different sampling frequencies.
an alpha channel. Pillow supports JPEG 2000 raw codestreams (``.j2k`` files),
as well as boxed JPEG 2000 files (``.j2p`` or ``.jpx`` files). Pillow does
*not* support files whose components have different sampling frequencies.
When loading, if you set the ``mode`` on the image prior to the
:py:meth:`~PIL.Image.Image.load` method being invoked, you can ask PIL to
:py:meth:`~PIL.Image.Image.load` method being invoked, you can ask Pillow to
convert the image to either ``RGB`` or ``RGBA`` rather than choosing for
itself. It is also possible to set ``reduce`` to the number of resolutions to
discard (each one reduces the size of the resulting image by a factor of 2),
@ -433,27 +441,31 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
Library.
Windows users can install the OpenJPEG binaries available on the
OpenJPEG website, but must add them to their PATH in order to use PIL (if
OpenJPEG website, but must add them to their PATH in order to use Pillow (if
you fail to do this, you will get errors about not being able to load the
``_imaging`` DLL).
MSP
^^^
PIL identifies and reads MSP files from Windows 1 and 2. The library writes
Pillow identifies and reads MSP files from Windows 1 and 2. The library writes
uncompressed (Windows 1) versions of this format.
PCX
^^^
PIL reads and writes PCX files containing ``1``, ``L``, ``P``, or ``RGB`` data.
Pillow reads and writes PCX files containing ``1``, ``L``, ``P``, or ``RGB`` data.
PNG
^^^
PIL identifies, reads, and writes PNG files containing ``1``, ``L``, ``P``,
Pillow identifies, reads, and writes PNG files containing ``1``, ``L``, ``P``,
``RGB``, or ``RGBA`` data. Interlaced files are supported as of v1.1.7.
As of Pillow 6.0, EXIF data can be read from PNG images. However, unlike other
image formats, EXIF data is not guaranteed to have been read until
:py:meth:`~PIL.Image.Image.load` has been called.
The :py:meth:`~PIL.Image.Image.open` method sets the following
:py:attr:`~PIL.Image.Image.info` properties, when appropriate:
@ -519,6 +531,11 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
**icc_profile**
The ICC Profile to include in the saved file.
**exif**
The exif data to include in the saved file.
.. versionadded:: 6.0.0
**bits (experimental)**
For ``P`` images, this option controls how many bits to store. If omitted,
the PNG writer uses 8 bits (256 colors).
@ -535,8 +552,8 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
PPM
^^^
PIL reads and writes PBM, PGM and PPM files containing ``1``, ``L`` or ``RGB``
data.
Pillow reads and writes PBM, PGM and PPM files containing ``1``, ``L`` or
``RGB`` data.
SGI
^^^
@ -547,10 +564,10 @@ Pillow reads and writes uncompressed ``L``, ``RGB``, and ``RGBA`` files.
SPIDER
^^^^^^
PIL reads and writes SPIDER image files of 32-bit floating point data
Pillow reads and writes SPIDER image files of 32-bit floating point data
("F;32F").
PIL also reads SPIDER stack files containing sequences of SPIDER images. The
Pillow also reads SPIDER stack files containing sequences of SPIDER images. The
:py:meth:`~file.seek` and :py:meth:`~file.tell` methods are supported, and
random access is allowed.
@ -587,8 +604,8 @@ For more information about the SPIDER image processing package, see the
TGA
^^^
PIL reads and writes TGA images containing ``L``, ``LA``, ``P``,
``RGB``, and ``RGBA`` data. PIL can read and write both uncompressed and
Pillow reads and writes TGA images containing ``L``, ``LA``, ``P``,
``RGB``, and ``RGBA`` data. Pillow can read and write both uncompressed and
run-length encoded TGAs.
TIFF
@ -596,8 +613,8 @@ TIFF
Pillow reads and writes TIFF files. It can read both striped and tiled
images, pixel and plane interleaved multi-band images. If you have
libtiff and its headers installed, PIL can read and write many kinds
of compressed TIFF files. If not, PIL will only read and write
libtiff and its headers installed, Pillow can read and write many kinds
of compressed TIFF files. If not, Pillow will only read and write
uncompressed files.
.. note::
@ -734,8 +751,8 @@ using the general tags available through tiffinfo.
WebP
^^^^
PIL reads and writes WebP files. The specifics of PIL's capabilities with this
format are currently undocumented.
Pillow reads and writes WebP files. The specifics of Pillow's capabilities with
this format are currently undocumented.
The :py:meth:`~PIL.Image.Image.save` method supports the following options:
@ -807,7 +824,7 @@ are available when the `save_all` argument is present and true.
XBM
^^^
PIL reads and writes X bitmap files (mode ``1``).
Pillow reads and writes X bitmap files (mode ``1``).
Read-only formats
-----------------
@ -841,15 +858,15 @@ DDS
DDS is a popular container texture format used in video games and natively
supported by DirectX.
Currently, DXT1, DXT3, and DXT5 pixel formats are supported and only in ``RGBA``
mode.
Currently, uncompressed RGB data and DXT1, DXT3, and DXT5 pixel formats are
supported, and only in ``RGBA`` mode.
.. versionadded:: 3.4.0 DXT3
FLI, FLC
^^^^^^^^
PIL reads Autodesk FLI and FLC animations.
Pillow reads Autodesk FLI and FLC animations.
The :py:meth:`~PIL.Image.Image.open` method sets the following
:py:attr:`~PIL.Image.Image.info` properties:
@ -860,7 +877,7 @@ The :py:meth:`~PIL.Image.Image.open` method sets the following
FPX
^^^
PIL reads Kodak FlashPix files. In the current version, only the highest
Pillow reads Kodak FlashPix files. In the current version, only the highest
resolution image is read from the file, and the viewing transform is not taken
into account.
@ -896,7 +913,7 @@ The :py:meth:`~PIL.Image.Image.open` method sets the following
GD
^^
PIL reads uncompressed GD2 files. Note that you must use
Pillow reads uncompressed GD2 files. Note that you must use
:py:func:`PIL.GdImageFile.open` to read such a file.
The :py:meth:`~PIL.Image.Image.open` method sets the following
@ -909,23 +926,23 @@ The :py:meth:`~PIL.Image.Image.open` method sets the following
IMT
^^^
PIL reads Image Tools images containing ``L`` data.
Pillow reads Image Tools images containing ``L`` data.
IPTC/NAA
^^^^^^^^
PIL provides limited read support for IPTC/NAA newsphoto files.
Pillow provides limited read support for IPTC/NAA newsphoto files.
MCIDAS
^^^^^^
PIL identifies and reads 8-bit McIdas area files.
Pillow identifies and reads 8-bit McIdas area files.
MIC
^^^
PIL identifies and reads Microsoft Image Composer (MIC) files. When opened, the
first sprite in the file is loaded. You can use :py:meth:`~file.seek` and
Pillow identifies and reads Microsoft Image Composer (MIC) files. When opened,
the first sprite in the file is loaded. You can use :py:meth:`~file.seek` and
:py:meth:`~file.tell` to read other sprites from the file.
Note that there may be an embedded gamma of 2.2 in MIC files.
@ -941,22 +958,22 @@ zero-indexed and random access is supported.
PCD
^^^
PIL reads PhotoCD files containing ``RGB`` data. This only reads the 768x512
Pillow reads PhotoCD files containing ``RGB`` data. This only reads the 768x512
resolution image from the file. Higher resolutions are encoded in a proprietary
encoding.
PIXAR
^^^^^
PIL provides limited support for PIXAR raster files. The library can identify
and read “dumped” RGB files.
Pillow provides limited support for PIXAR raster files. The library can
identify and read “dumped” RGB files.
The format code is ``PIXAR``.
PSD
^^^
PIL identifies and reads PSD files written by Adobe Photoshop 2.5 and 3.0.
Pillow identifies and reads PSD files written by Adobe Photoshop 2.5 and 3.0.
WAL
@ -964,7 +981,7 @@ WAL
.. versionadded:: 1.1.4
PIL reads Quake2 WAL texture files.
Pillow reads Quake2 WAL texture files.
Note that this file format cannot be automatically identified, so you must use
the open function in the :py:mod:`~PIL.WalImageFile` module to read files in
@ -976,7 +993,7 @@ the palette, use the putpalette method.
XPM
^^^
PIL reads X pixmap files (mode ``P``) with 256 colors or less.
Pillow reads X pixmap files (mode ``P``) with 256 colors or less.
The :py:meth:`~PIL.Image.Image.open` method sets the following
:py:attr:`~PIL.Image.Image.info` properties:
@ -991,14 +1008,14 @@ Write-only formats
PALM
^^^^
PIL provides write-only support for PALM pixmap files.
Pillow provides write-only support for PALM pixmap files.
The format code is ``Palm``, the extension is ``.palm``.
PDF
^^^
PIL can write PDF (Acrobat) images. Such images are written as binary PDF 1.4
Pillow can write PDF (Acrobat) images. Such images are written as binary PDF 1.4
files, using either JPEG or HEX encoding depending on the image mode (and
whether JPEG support is available or not).
@ -1077,7 +1094,7 @@ The :py:meth:`~PIL.Image.Image.save` method can take the following keyword argum
XV Thumbnails
^^^^^^^^^^^^^
PIL can read XV thumbnail files.
Pillow can read XV thumbnail files.
Identify-only formats
---------------------
@ -1087,7 +1104,7 @@ BUFR
.. versionadded:: 1.1.3
PIL provides a stub driver for BUFR files.
Pillow provides a stub driver for BUFR files.
To add read or write support to your application, use
:py:func:`PIL.BufrStubImagePlugin.register_handler`.
@ -1097,7 +1114,7 @@ FITS
.. versionadded:: 1.1.5
PIL provides a stub driver for FITS files.
Pillow provides a stub driver for FITS files.
To add read or write support to your application, use
:py:func:`PIL.FitsStubImagePlugin.register_handler`.
@ -1107,11 +1124,11 @@ GRIB
.. versionadded:: 1.1.5
PIL provides a stub driver for GRIB files.
Pillow provides a stub driver for GRIB files.
The driver requires the file to start with a GRIB header. If you have files
with embedded GRIB data, or files with multiple GRIB fields, your application
has to seek to the header before passing the file handle to PIL.
has to seek to the header before passing the file handle to Pillow.
To add read or write support to your application, use
:py:func:`PIL.GribStubImagePlugin.register_handler`.
@ -1121,7 +1138,7 @@ HDF5
.. versionadded:: 1.1.5
PIL provides a stub driver for HDF5 files.
Pillow provides a stub driver for HDF5 files.
To add read or write support to your application, use
:py:func:`PIL.Hdf5StubImagePlugin.register_handler`.
@ -1129,12 +1146,12 @@ To add read or write support to your application, use
MPEG
^^^^
PIL identifies MPEG files.
Pillow identifies MPEG files.
WMF
^^^
PIL can identify playable WMF files.
Pillow can identify playable WMF files.
In PIL 1.1.4 and earlier, the WMF driver provides some limited rendering
support, but not enough to be useful for any real application.

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 |
+--------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
+---------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
|**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.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.
@ -76,15 +102,27 @@ Use ``PIL.__version__`` instead.
API Additions
=============
TODO
^^^^
DIB File Format
^^^^^^^^^^^^^^^
TODO
Pillow now supports reading and writing the DIB "Device Independent Bitmap" file format.
Image.quantize
^^^^^^^^^^^^^^
The `dither` option is now a customisable parameter (was previously hardcoded to `1`). This parameter takes the same values used in `Image.convert`
PNG EXIF Data
^^^^^^^^^^^^^
EXIF data can now be read from and saved to PNG images. However, unlike other image
formats, EXIF data is not guaranteed to have been read until
:py:meth:`~PIL.Image.Image.load` has been called.
Other Changes
=============
TODO
^^^^
Reading new DDS image format
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
TODO
Pillow can now read uncompressed RGB data from DDS images.

View File

@ -52,6 +52,10 @@ def _accept(prefix):
return prefix[:2] == b"BM"
def _dib_accept(prefix):
return i32(prefix[:4]) in [12, 40, 64, 108, 124]
# =============================================================================
# Image plugin for the Windows BMP format.
# =============================================================================
@ -291,7 +295,11 @@ SAVE = {
}
def _save(im, fp, filename):
def _dib_save(im, fp, filename):
_save(im, fp, filename, False)
def _save(im, fp, filename, bitmap_header=True):
try:
rawmode, bits, colors = SAVE[im.mode]
except KeyError:
@ -306,14 +314,15 @@ def _save(im, fp, filename):
stride = ((im.size[0]*bits+7)//8+3) & (~3)
header = 40 # or 64 for OS/2 version 2
offset = 14 + header + colors * 4
image = stride * im.size[1]
# bitmap header
fp.write(b"BM" + # file type (magic)
o32(offset+image) + # file size
o32(0) + # reserved
o32(offset)) # image data offset
if bitmap_header:
offset = 14 + header + colors * 4
fp.write(b"BM" + # file type (magic)
o32(offset+image) + # file size
o32(0) + # reserved
o32(offset)) # image data offset
# bitmap info header
fp.write(o32(header) + # info header size
@ -352,3 +361,10 @@ Image.register_save(BmpImageFile.format, _save)
Image.register_extension(BmpImageFile.format, ".bmp")
Image.register_mime(BmpImageFile.format, "image/bmp")
Image.register_open(DibImageFile.format, DibImageFile, _dib_accept)
Image.register_save(DibImageFile.format, _dib_save)
Image.register_extension(DibImageFile.format, ".dib")
Image.register_mime(DibImageFile.format, "image/bmp")

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

@ -123,43 +123,52 @@ class DdsImageFile(ImageFile.ImageFile):
# pixel format
pfsize, pfflags = struct.unpack("<2I", header.read(8))
fourcc = header.read(4)
bitcount, rmask, gmask, bmask, amask = struct.unpack("<5I",
header.read(20))
bitcount, = struct.unpack("<I", header.read(4))
masks = struct.unpack("<4I", header.read(16))
if pfflags & 0x40:
# DDPF_RGB - Texture contains uncompressed RGB data
masks = {mask: ["R", "G", "B", "A"][i] for i, mask in enumerate(masks)}
rawmode = ""
if bitcount == 32:
rawmode += masks[0xff000000]
rawmode += masks[0xff0000] + masks[0xff00] + masks[0xff]
data_start = header_size + 4
n = 0
if fourcc == b"DXT1":
self.pixel_format = "DXT1"
n = 1
elif fourcc == b"DXT3":
self.pixel_format = "DXT3"
n = 2
elif fourcc == b"DXT5":
self.pixel_format = "DXT5"
n = 3
elif fourcc == b"DX10":
data_start += 20
# ignoring flags which pertain to volume textures and cubemaps
dxt10 = BytesIO(self.fp.read(20))
dxgi_format, dimension = struct.unpack("<II", dxt10.read(8))
if dxgi_format in (DXGI_FORMAT_BC7_TYPELESS,
DXGI_FORMAT_BC7_UNORM):
self.pixel_format = "BC7"
n = 7
elif dxgi_format == DXGI_FORMAT_BC7_UNORM_SRGB:
self.pixel_format = "BC7"
self.im_info["gamma"] = 1/2.2
n = 7
else:
raise NotImplementedError("Unimplemented DXGI format %d" %
(dxgi_format))
self.tile = [("raw", (0, 0) + self.size, 0, (rawmode, 0, 1))]
else:
raise NotImplementedError("Unimplemented pixel format %r" %
(fourcc))
data_start = header_size + 4
n = 0
if fourcc == b"DXT1":
self.pixel_format = "DXT1"
n = 1
elif fourcc == b"DXT3":
self.pixel_format = "DXT3"
n = 2
elif fourcc == b"DXT5":
self.pixel_format = "DXT5"
n = 3
elif fourcc == b"DX10":
data_start += 20
# ignoring flags which pertain to volume textures and cubemaps
dxt10 = BytesIO(self.fp.read(20))
dxgi_format, dimension = struct.unpack("<II", dxt10.read(8))
if dxgi_format in (DXGI_FORMAT_BC7_TYPELESS,
DXGI_FORMAT_BC7_UNORM):
self.pixel_format = "BC7"
n = 7
elif dxgi_format == DXGI_FORMAT_BC7_UNORM_SRGB:
self.pixel_format = "BC7"
self.im_info["gamma"] = 1/2.2
n = 7
else:
raise NotImplementedError("Unimplemented DXGI format %d" %
(dxgi_format))
else:
raise NotImplementedError("Unimplemented pixel format %r" %
(fourcc))
self.tile = [
("bcn", (0, 0) + self.size, data_start, (n))
]
self.tile = [
("bcn", (0, 0) + self.size, data_start, (n))
]
def load_seek(self, pos):
pass

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

@ -24,10 +24,10 @@
# See the README file for information on usage and redistribution.
#
# 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
@ -60,8 +60,7 @@ except ImportError:
from collections import Callable
# Silence warnings
assert VERSION
# Silence warning
assert PILLOW_VERSION
logger = logging.getLogger(__name__)
@ -655,8 +654,7 @@ class Image(object):
return filename
def __eq__(self, other):
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
@ -681,8 +679,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()
@ -1052,7 +1049,7 @@ class Image(object):
new_im.info['transparency'] = trns
return new_im
def quantize(self, colors=256, method=None, kmeans=0, palette=None):
def quantize(self, colors=256, method=None, kmeans=0, palette=None, dither=1):
"""
Convert the image to 'P' mode with the specified number
of colors.
@ -1065,6 +1062,10 @@ class Image(object):
:param kmeans: Integer
:param palette: Quantize to the palette of given
:py:class:`PIL.Image.Image`.
:param dither: Dithering method, used when converting from
mode "RGB" to "P" or from "RGB" or "L" to "1".
Available methods are NONE or FLOYDSTEINBERG (default).
Default: 1 (legacy setting)
:returns: A new image
"""
@ -1092,7 +1093,7 @@ class Image(object):
raise ValueError(
"only RGB or L mode images can be quantized to a palette"
)
im = self.im.convert("P", 1, palette.im)
im = self.im.convert("P", dither, palette.im)
return self._new(im)
return self._new(self.im.quantize(colors, method, kmeans))
@ -2005,7 +2006,7 @@ class Image(object):
library automatically seeks to frame 0.
Note that in the current version of the library, most sequence
formats only allows you to seek to the next frame.
formats only allow you to seek to the next frame.
See :py:meth:`~PIL.Image.Image.tell`.
@ -2024,10 +2025,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
@ -2129,11 +2130,12 @@ class Image(object):
self.draft(None, size)
im = self.resize(size, resample)
if self.size != size:
im = self.resize(size, resample)
self.im = im.im
self.mode = im.mode
self._size = size
self.im = im.im
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

@ -120,9 +120,10 @@ class ImageFile(Image.Image):
pass
def get_format_mimetype(self):
if self.format is None:
return
return self.custom_mimetype or Image.MIME.get(self.format.upper())
if self.custom_mimetype:
return self.custom_mimetype
if self.format is not None:
return Image.MIME.get(self.format.upper())
def verify(self):
"""Check file integrity"""
@ -491,7 +492,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)
@ -510,7 +511,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,18 +269,13 @@ 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]
value = self[key.encode("us-ascii")]
except KeyError:
try:
value = self[key.encode("us-ascii")]
except KeyError:
raise AttributeError(key)
raise AttributeError(key)
if isinstance(value, bytes):
value = decode_text(value)
if key.endswith("Date"):
@ -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

@ -194,7 +194,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
@ -529,6 +529,11 @@ class PngStream(ChunkStream):
return s
def chunk_eXIf(self, pos, length):
s = ImageFile._safe_read(self.fp, length)
self.im_info["exif"] = b"Exif\x00\x00"+s
return s
# APNG chunks
def chunk_acTL(self, pos, length):
s = ImageFile._safe_read(self.fp, length)
@ -683,6 +688,12 @@ class PngImageFile(ImageFile.ImageFile):
self.png.close()
self.png = None
def _getexif(self):
if "exif" not in self.info:
self.load()
from .JpegImagePlugin import _getexif
return _getexif(self)
# --------------------------------------------------------------------
# PNG writer
@ -861,6 +872,12 @@ def _save(im, fp, filename, chunk=putchunk):
chunks.remove(cid)
chunk(fp, cid, data)
exif = im.encoderinfo.get("exif", im.info.get("exif"))
if exif:
if exif.startswith(b"Exif\x00\x00"):
exif = exif[6:]
chunk(fp, b"eXIf", exif)
ImageFile._save(im, _idat(fp, chunk),
[("zip", (0, 0)+im.size, 0, rawmode)])

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):
@ -816,7 +819,7 @@ class ImageFileDirectory_v2(MutableMapping):
print("- value:", values)
# count is sum of lengths for string and arbitrary data
if typ in [TiffTags.ASCII, TiffTags.UNDEFINED]:
if typ in [TiffTags.BYTE, TiffTags.ASCII, TiffTags.UNDEFINED]:
count = len(data)
else:
count = len(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',)
@ -1440,7 +1443,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
@ -1684,7 +1687,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,13 +2,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)
def isPath(f):
return isinstance(f, (bytes, 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:
def isStringType(t):
return isinstance(t, basestring) # noqa: F821

View File

@ -39,7 +39,11 @@ ImagingFlipLeftRight(Imaging imOut, Imaging imIn)
ImagingSectionEnter(&cookie);
if (imIn->image8) {
FLIP_LEFT_RIGHT(UINT8, 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) {
ROTATE_180(UINT8, image8)
if (strncmp(imIn->mode, "I;16", 4) == 0) {
ROTATE_180(UINT16, image8)
} else {
ROTATE_180(UINT8, image8)
}
} else {
ROTATE_180(INT32, image32)
}