mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-07-03 19:33:07 +03:00
Merge remote-tracking branch 'remotes/upstream/master' into tiff-old-jpeg
This commit is contained in:
commit
28c6f555a2
28
.azure-pipelines/jobs/lint.yml
Normal file
28
.azure-pipelines/jobs/lint.yml
Normal 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'
|
22
.azure-pipelines/jobs/test-docker.yml
Normal file
22
.azure-pipelines/jobs/test-docker.yml
Normal 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'
|
2
.github/CONTRIBUTING.md
vendored
2
.github/CONTRIBUTING.md
vendored
|
@ -34,6 +34,6 @@ The best reproductions are self-contained scripts with minimal dependencies. If
|
||||||
|
|
||||||
## Security vulnerabilities
|
## 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.
|
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.
|
||||||
|
|
48
CHANGES.rst
48
CHANGES.rst
|
@ -5,6 +5,54 @@ Changelog (Pillow)
|
||||||
6.0.0 (unreleased)
|
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
|
- Remove unnecessary unittest.main() boilerplate from test files #3631
|
||||||
[jdufresne]
|
[jdufresne]
|
||||||
|
|
||||||
|
|
|
@ -23,9 +23,10 @@ exclude .codecov.yml
|
||||||
exclude .editorconfig
|
exclude .editorconfig
|
||||||
exclude .landscape.yaml
|
exclude .landscape.yaml
|
||||||
exclude .readthedocs.yml
|
exclude .readthedocs.yml
|
||||||
exclude .travis
|
exclude azure-pipelines.yml
|
||||||
exclude .travis/*
|
|
||||||
exclude tox.ini
|
exclude tox.ini
|
||||||
global-exclude .git*
|
global-exclude .git*
|
||||||
global-exclude *.pyc
|
global-exclude *.pyc
|
||||||
global-exclude *.so
|
global-exclude *.so
|
||||||
|
prune .azure-pipelines
|
||||||
|
prune .travis
|
||||||
|
|
|
@ -88,14 +88,7 @@ Released as needed privately to individual vendors for critical security-related
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/python-pillow/pillow-wheels
|
git clone https://github.com/python-pillow/pillow-wheels
|
||||||
cd pillow-wheels
|
cd pillow-wheels
|
||||||
git submodule init
|
./update-pillow-tag.sh [[release tag]]
|
||||||
git submodule update Pillow
|
|
||||||
cd Pillow
|
|
||||||
git fetch --all
|
|
||||||
git checkout [[release tag]]
|
|
||||||
cd ..
|
|
||||||
git commit -m "Pillow -> 5.2.0" Pillow
|
|
||||||
git push
|
|
||||||
```
|
```
|
||||||
* [ ] Download distributions from the [Pillow Wheel Builder container](http://a365fff413fe338398b6-1c8a9b3114517dc5fe17b7c3f8c63a43.r19.cf2.rackcdn.com/).
|
* [ ] Download distributions from the [Pillow Wheel Builder container](http://a365fff413fe338398b6-1c8a9b3114517dc5fe17b7c3f8c63a43.r19.cf2.rackcdn.com/).
|
||||||
```bash
|
```bash
|
||||||
|
|
BIN
Tests/images/exif.png
Normal file
BIN
Tests/images/exif.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 175 KiB |
BIN
Tests/images/itxt_chunks.png
Normal file
BIN
Tests/images/itxt_chunks.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 68 KiB |
BIN
Tests/images/uncompressed_rgb.dds
Executable file
BIN
Tests/images/uncompressed_rgb.dds
Executable file
Binary file not shown.
BIN
Tests/images/uncompressed_rgb.png
Normal file
BIN
Tests/images/uncompressed_rgb.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 143 KiB |
BIN
Tests/images/unimplemented_dxgi_format.dds
Normal file
BIN
Tests/images/unimplemented_dxgi_format.dds
Normal file
Binary file not shown.
BIN
Tests/images/unimplemented_pixel_format.dds
Executable file
BIN
Tests/images/unimplemented_pixel_format.dds
Executable file
Binary file not shown.
|
@ -11,8 +11,6 @@ class TestSanity(PillowTestCase):
|
||||||
# Make sure we have the binary extension
|
# Make sure we have the binary extension
|
||||||
PIL.Image.core.new("L", (100, 100))
|
PIL.Image.core.new("L", (100, 100))
|
||||||
|
|
||||||
self.assertEqual(PIL.Image.VERSION[:3], '1.1')
|
|
||||||
|
|
||||||
# Create an image and do stuff with it.
|
# Create an image and do stuff with it.
|
||||||
im = PIL.Image.new("1", (100, 100))
|
im = PIL.Image.new("1", (100, 100))
|
||||||
self.assertEqual((im.mode, im.size), ('1', (100, 100)))
|
self.assertEqual((im.mode, im.size), ('1', (100, 100)))
|
||||||
|
|
|
@ -16,6 +16,7 @@ class TestFileBmp(PillowTestCase):
|
||||||
self.assertEqual(im.mode, reloaded.mode)
|
self.assertEqual(im.mode, reloaded.mode)
|
||||||
self.assertEqual(im.size, reloaded.size)
|
self.assertEqual(im.size, reloaded.size)
|
||||||
self.assertEqual(reloaded.format, "BMP")
|
self.assertEqual(reloaded.format, "BMP")
|
||||||
|
self.assertEqual(reloaded.get_format_mimetype(), "image/bmp")
|
||||||
|
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
self.roundtrip(hopper())
|
self.roundtrip(hopper())
|
||||||
|
@ -72,6 +73,20 @@ class TestFileBmp(PillowTestCase):
|
||||||
|
|
||||||
def test_load_dib(self):
|
def test_load_dib(self):
|
||||||
# test for #1293, Imagegrab returning Unsupported Bitfields Format
|
# 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')
|
target = Image.open('Tests/images/clipboard_target.png')
|
||||||
self.assert_image_equal(im, target)
|
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)
|
||||||
|
|
|
@ -16,7 +16,7 @@ class TestFileContainer(PillowTestCase):
|
||||||
im = hopper()
|
im = hopper()
|
||||||
container = ContainerIO.ContainerIO(im, 0, 0)
|
container = ContainerIO.ContainerIO(im, 0, 0)
|
||||||
|
|
||||||
self.assertEqual(container.isatty(), 0)
|
self.assertFalse(container.isatty())
|
||||||
|
|
||||||
def test_seek_mode_0(self):
|
def test_seek_mode_0(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
|
|
@ -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_DXT3 = "Tests/images/dxt3-argb-8bbp-explicitalpha_MipMaps-1.dds"
|
||||||
TEST_FILE_DXT5 = "Tests/images/dxt5-argb-8bbp-interpolatedalpha_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_DX10_BC7 = "Tests/images/bc7-argb-8bpp_MipMaps-1.dds"
|
||||||
|
TEST_FILE_UNCOMPRESSED_RGB = "Tests/images/uncompressed_rgb.dds"
|
||||||
|
|
||||||
|
|
||||||
class TestFileDds(PillowTestCase):
|
class TestFileDds(PillowTestCase):
|
||||||
|
@ -67,6 +68,24 @@ class TestFileDds(PillowTestCase):
|
||||||
|
|
||||||
self.assert_image_equal(target, im)
|
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):
|
def test__validate_true(self):
|
||||||
"""Check valid prefix"""
|
"""Check valid prefix"""
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -110,3 +129,7 @@ class TestFileDds(PillowTestCase):
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
self.assertRaises(IOError, short_file)
|
self.assertRaises(IOError, short_file)
|
||||||
|
|
||||||
|
def test_unimplemented_pixel_format(self):
|
||||||
|
self.assertRaises(NotImplementedError, Image.open,
|
||||||
|
"Tests/images/unimplemented_pixel_format.dds")
|
||||||
|
|
|
@ -234,11 +234,11 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
|
|
||||||
def test_custom_metadata(self):
|
def test_custom_metadata(self):
|
||||||
custom = {
|
custom = {
|
||||||
37000: 4,
|
37000: [4, TiffTags.SHORT],
|
||||||
37001: 4.2,
|
37001: [4.2, TiffTags.RATIONAL],
|
||||||
37002: 'custom tag value',
|
37002: ['custom tag value', TiffTags.ASCII],
|
||||||
37003: u'custom tag value',
|
37003: [u'custom tag value', TiffTags.ASCII],
|
||||||
37004: b'custom tag value'
|
37004: [b'custom tag value', TiffTags.BYTE]
|
||||||
}
|
}
|
||||||
|
|
||||||
libtiff_version = TiffImagePlugin._libtiff_version()
|
libtiff_version = TiffImagePlugin._libtiff_version()
|
||||||
|
@ -251,17 +251,33 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
for libtiff in libtiffs:
|
for libtiff in libtiffs:
|
||||||
TiffImagePlugin.WRITE_LIBTIFF = libtiff
|
TiffImagePlugin.WRITE_LIBTIFF = libtiff
|
||||||
|
|
||||||
|
def check_tags(tiffinfo):
|
||||||
im = hopper()
|
im = hopper()
|
||||||
|
|
||||||
out = self.tempfile("temp.tif")
|
out = self.tempfile("temp.tif")
|
||||||
im.save(out, tiffinfo=custom)
|
im.save(out, tiffinfo=tiffinfo)
|
||||||
TiffImagePlugin.WRITE_LIBTIFF = False
|
|
||||||
|
|
||||||
reloaded = Image.open(out)
|
reloaded = Image.open(out)
|
||||||
for tag, value in custom.items():
|
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):
|
if libtiff and isinstance(value, bytes):
|
||||||
value = value.decode()
|
value = value.decode()
|
||||||
self.assertEqual(reloaded.tag_v2[tag], value)
|
|
||||||
|
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):
|
def test_int_dpi(self):
|
||||||
# issue #1765
|
# issue #1765
|
||||||
|
|
|
@ -12,11 +12,11 @@ YA_EXTRA_DIR = "Tests/images/msp"
|
||||||
class TestFileMsp(PillowTestCase):
|
class TestFileMsp(PillowTestCase):
|
||||||
|
|
||||||
def test_sanity(self):
|
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()
|
im.load()
|
||||||
self.assertEqual(im.mode, "1")
|
self.assertEqual(im.mode, "1")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
|
|
|
@ -590,6 +590,40 @@ class TestFilePng(PillowTestCase):
|
||||||
im = Image.open("Tests/images/hopper_idat_after_image_end.png")
|
im = Image.open("Tests/images/hopper_idat_after_image_end.png")
|
||||||
self.assertEqual(im.text, {'TXT': 'VALUE', 'ZIP': 'VALUE'})
|
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,
|
@unittest.skipUnless(HAVE_WEBP and _webp.HAVE_WEBPANIM,
|
||||||
"WebP support not installed with animation")
|
"WebP support not installed with animation")
|
||||||
def test_apng(self):
|
def test_apng(self):
|
||||||
|
|
|
@ -14,12 +14,14 @@ class TestFilePpm(PillowTestCase):
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assertEqual(im.mode, "RGB")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "PPM")
|
self.assertEqual(im.format, "PPM")
|
||||||
|
self.assertEqual(im.get_format_mimetype(), "image/x-portable-pixmap")
|
||||||
|
|
||||||
def test_16bit_pgm(self):
|
def test_16bit_pgm(self):
|
||||||
im = Image.open('Tests/images/16_bit_binary.pgm')
|
im = Image.open('Tests/images/16_bit_binary.pgm')
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, 'I')
|
self.assertEqual(im.mode, 'I')
|
||||||
self.assertEqual(im.size, (20, 100))
|
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')
|
tgt = Image.open('Tests/images/16_bit_binary_pgm.png')
|
||||||
self.assert_image_equal(im, tgt)
|
self.assert_image_equal(im, tgt)
|
||||||
|
@ -49,3 +51,16 @@ class TestFilePpm(PillowTestCase):
|
||||||
|
|
||||||
with self.assertRaises(IOError):
|
with self.assertRaises(IOError):
|
||||||
Image.open('Tests/images/negative_size.ppm')
|
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")
|
||||||
|
|
|
@ -46,3 +46,19 @@ class TestImageQuantize(PillowTestCase):
|
||||||
converted = image.quantize()
|
converted = image.quantize()
|
||||||
self.assert_image(converted, 'P', converted.size)
|
self.assert_image(converted, 'P', converted.size)
|
||||||
self.assert_image_similar(converted.convert('RGB'), image, 1)
|
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())
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from .helper import PillowTestCase, hopper
|
from .helper import PillowTestCase, hopper
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
|
||||||
class TestImageThumbnail(PillowTestCase):
|
class TestImageThumbnail(PillowTestCase):
|
||||||
|
@ -35,3 +36,14 @@ class TestImageThumbnail(PillowTestCase):
|
||||||
im = hopper().resize((128, 128))
|
im = hopper().resize((128, 128))
|
||||||
im.thumbnail((100, 100))
|
im.thumbnail((100, 100))
|
||||||
self.assert_image(im, im.mode, (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))
|
||||||
|
|
|
@ -7,10 +7,9 @@ from PIL.Image import (FLIP_LEFT_RIGHT, FLIP_TOP_BOTTOM, ROTATE_90, ROTATE_180,
|
||||||
|
|
||||||
class TestImageTranspose(PillowTestCase):
|
class TestImageTranspose(PillowTestCase):
|
||||||
|
|
||||||
hopper = {
|
hopper = {mode: helper.hopper(mode).crop((0, 0, 121, 127)).copy() for mode in [
|
||||||
'L': helper.hopper('L').crop((0, 0, 121, 127)).copy(),
|
'L', 'RGB', 'I;16', 'I;16L', 'I;16B'
|
||||||
'RGB': helper.hopper('RGB').crop((0, 0, 121, 127)).copy(),
|
]}
|
||||||
}
|
|
||||||
|
|
||||||
def test_flip_left_right(self):
|
def test_flip_left_right(self):
|
||||||
def transpose(mode):
|
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((1, y-2)), out.getpixel((x-2, y-2)))
|
||||||
self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((1, 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)
|
transpose(mode)
|
||||||
|
|
||||||
def test_flip_top_bottom(self):
|
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((1, y-2)), out.getpixel((1, 1)))
|
||||||
self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((x-2, 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)
|
transpose(mode)
|
||||||
|
|
||||||
def test_rotate_90(self):
|
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((1, y-2)), out.getpixel((x-2, 1)))
|
||||||
self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((1, 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)
|
transpose(mode)
|
||||||
|
|
||||||
def test_rotate_270(self):
|
def test_rotate_270(self):
|
||||||
|
|
|
@ -1,7 +1,16 @@
|
||||||
from .helper import PillowTestCase, hopper
|
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:
|
if ImageQt.qt_is_installed:
|
||||||
from PIL.ImageQt import qRgba
|
from PIL.ImageQt import qRgba
|
||||||
|
@ -78,3 +87,6 @@ class TestImageQt(PillowQtTestCase, PillowTestCase):
|
||||||
def test_image(self):
|
def test_image(self):
|
||||||
for mode in ('1', 'RGB', 'RGBA', 'L', 'P'):
|
for mode in ('1', 'RGB', 'RGBA', 'L', 'P'):
|
||||||
ImageQt.ImageQt(hopper(mode))
|
ImageQt.ImageQt(hopper(mode))
|
||||||
|
|
||||||
|
def test_deprecated(self):
|
||||||
|
self.assertEqual(ImageQt.qt_version in ["4", "side"], deprecated)
|
||||||
|
|
|
@ -67,9 +67,13 @@ class TestPickle(PillowTestCase):
|
||||||
"Tests/images/non_zero_bb.png",
|
"Tests/images/non_zero_bb.png",
|
||||||
"Tests/images/non_zero_bb_scale2.png",
|
"Tests/images/non_zero_bb_scale2.png",
|
||||||
"Tests/images/p_trns_single.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):
|
def test_pickle_l_mode(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from .helper import PillowTestCase
|
from .helper import unittest, PillowTestCase
|
||||||
|
|
||||||
from PIL import _util
|
from PIL import _util
|
||||||
|
|
||||||
|
@ -35,6 +35,18 @@ class TestUtil(PillowTestCase):
|
||||||
# Assert
|
# Assert
|
||||||
self.assertTrue(it_is)
|
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):
|
def test_is_not_path(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
filename = self.tempfile("temp.ext")
|
filename = self.tempfile("temp.ext")
|
||||||
|
|
67
azure-pipelines.yml
Normal file
67
azure-pipelines.yml
Normal 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'
|
|
@ -12,6 +12,27 @@ Deprecated features
|
||||||
Below are features which are considered deprecated. Where appropriate,
|
Below are features which are considered deprecated. Where appropriate,
|
||||||
a ``DeprecationWarning`` is issued.
|
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
|
PIL.*ImagePlugin.__version__ attributes
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -50,13 +71,12 @@ a ``DeprecationWarning``:
|
||||||
Setting the size of a TIFF image directly is deprecated, and will
|
Setting the size of a TIFF image directly is deprecated, and will
|
||||||
be removed in a future version. Use the resize method instead.
|
be removed in a future version. Use the resize method instead.
|
||||||
|
|
||||||
PILLOW_VERSION and VERSION constants
|
PILLOW_VERSION constant
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. deprecated:: 5.2.0
|
.. deprecated:: 5.2.0
|
||||||
|
|
||||||
Two version constants – ``VERSION`` (the old PIL version, always 1.1.7) and
|
``PILLOW_VERSION`` has been deprecated and will be removed in the next
|
||||||
``PILLOW_VERSION`` – have been deprecated and will be removed in the next
|
|
||||||
major release. Use ``__version__`` instead.
|
major release. Use ``__version__`` instead.
|
||||||
|
|
||||||
Removed features
|
Removed features
|
||||||
|
@ -65,6 +85,14 @@ Removed features
|
||||||
Deprecated features are only removed in major releases after an appropriate
|
Deprecated features are only removed in major releases after an appropriate
|
||||||
period of deprecation has passed.
|
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
|
Undocumented ImageOps functions
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ Fully supported formats
|
||||||
BMP
|
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
|
or ``RGB`` data. 16-colour images are read as ``P`` images. Run-length encoding
|
||||||
is not supported.
|
is not supported.
|
||||||
|
|
||||||
|
@ -31,13 +31,21 @@ The :py:meth:`~PIL.Image.Image.open` method sets the following
|
||||||
**compression**
|
**compression**
|
||||||
Set to ``bmp_rle`` if the file is run-length encoded.
|
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
|
EPS
|
||||||
^^^
|
^^^
|
||||||
|
|
||||||
PIL identifies EPS files containing image data, and can read files that contain
|
Pillow identifies EPS files containing image data, and can read files that
|
||||||
embedded raster images (ImageData descriptors). If Ghostscript is available,
|
contain embedded raster images (ImageData descriptors). If Ghostscript is
|
||||||
other EPS files can be read as well. The EPS driver can also write EPS
|
available, other EPS files can be read as well. The EPS driver can also write
|
||||||
images. The EPS driver can read EPS images in ``L``, ``LAB``, ``RGB`` and
|
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
|
``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
|
than leaving them in the original color space. The EPS driver can write images
|
||||||
in ``L``, ``RGB`` and ``CMYK`` modes.
|
in ``L``, ``RGB`` and ``CMYK`` modes.
|
||||||
|
@ -59,8 +67,8 @@ method with the following parameter to affect how Ghostscript renders the EPS
|
||||||
GIF
|
GIF
|
||||||
^^^
|
^^^
|
||||||
|
|
||||||
PIL reads GIF87a and GIF89a versions of the GIF file format. The library writes
|
Pillow reads GIF87a and GIF89a versions of the GIF file format. The library
|
||||||
run-length encoded files in GIF87a by default, unless GIF89a features
|
writes run-length encoded files in GIF87a by default, unless GIF89a features
|
||||||
are used or GIF89a is already in use.
|
are used or GIF89a is already in use.
|
||||||
|
|
||||||
Note that GIF files are always read as grayscale (``L``)
|
Note that GIF files are always read as grayscale (``L``)
|
||||||
|
@ -192,7 +200,7 @@ attributes before loading the file::
|
||||||
ICNS
|
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
|
largest available icon is read, though you can override this by setting the
|
||||||
:py:attr:`~PIL.Image.Image.size` property before calling
|
:py:attr:`~PIL.Image.Image.size` property before calling
|
||||||
:py:meth:`~PIL.Image.Image.load`. The :py:meth:`~PIL.Image.Image.open` method
|
: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
|
processing library. The library reads and writes most uncompressed interchange
|
||||||
versions of this format.
|
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
|
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.
|
``CMYK`` data. It writes standard and progressive JFIF files.
|
||||||
|
|
||||||
Using the :py:meth:`~PIL.Image.Image.draft` method, you can speed things up by
|
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
|
.. 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
|
``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
|
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
|
an alpha channel. Pillow supports JPEG 2000 raw codestreams (``.j2k`` files),
|
||||||
well as boxed JPEG 2000 files (``.j2p`` or ``.jpx`` files). PIL does *not*
|
as well as boxed JPEG 2000 files (``.j2p`` or ``.jpx`` files). Pillow does
|
||||||
support files whose components have different sampling frequencies.
|
*not* support files whose components have different sampling frequencies.
|
||||||
|
|
||||||
When loading, if you set the ``mode`` on the image prior to the
|
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
|
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
|
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),
|
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.
|
Library.
|
||||||
|
|
||||||
Windows users can install the OpenJPEG binaries available on the
|
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
|
you fail to do this, you will get errors about not being able to load the
|
||||||
``_imaging`` DLL).
|
``_imaging`` DLL).
|
||||||
|
|
||||||
MSP
|
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.
|
uncompressed (Windows 1) versions of this format.
|
||||||
|
|
||||||
PCX
|
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
|
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.
|
``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
|
The :py:meth:`~PIL.Image.Image.open` method sets the following
|
||||||
:py:attr:`~PIL.Image.Image.info` properties, when appropriate:
|
: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**
|
**icc_profile**
|
||||||
The ICC Profile to include in the saved file.
|
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)**
|
**bits (experimental)**
|
||||||
For ``P`` images, this option controls how many bits to store. If omitted,
|
For ``P`` images, this option controls how many bits to store. If omitted,
|
||||||
the PNG writer uses 8 bits (256 colors).
|
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
|
PPM
|
||||||
^^^
|
^^^
|
||||||
|
|
||||||
PIL reads and writes PBM, PGM and PPM files containing ``1``, ``L`` or ``RGB``
|
Pillow reads and writes PBM, PGM and PPM files containing ``1``, ``L`` or
|
||||||
data.
|
``RGB`` data.
|
||||||
|
|
||||||
SGI
|
SGI
|
||||||
^^^
|
^^^
|
||||||
|
@ -547,10 +564,10 @@ Pillow reads and writes uncompressed ``L``, ``RGB``, and ``RGBA`` files.
|
||||||
SPIDER
|
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").
|
("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
|
:py:meth:`~file.seek` and :py:meth:`~file.tell` methods are supported, and
|
||||||
random access is allowed.
|
random access is allowed.
|
||||||
|
|
||||||
|
@ -587,8 +604,8 @@ For more information about the SPIDER image processing package, see the
|
||||||
TGA
|
TGA
|
||||||
^^^
|
^^^
|
||||||
|
|
||||||
PIL reads and writes TGA images containing ``L``, ``LA``, ``P``,
|
Pillow reads and writes TGA images containing ``L``, ``LA``, ``P``,
|
||||||
``RGB``, and ``RGBA`` data. PIL can read and write both uncompressed and
|
``RGB``, and ``RGBA`` data. Pillow can read and write both uncompressed and
|
||||||
run-length encoded TGAs.
|
run-length encoded TGAs.
|
||||||
|
|
||||||
TIFF
|
TIFF
|
||||||
|
@ -596,8 +613,8 @@ TIFF
|
||||||
|
|
||||||
Pillow reads and writes TIFF files. It can read both striped and tiled
|
Pillow reads and writes TIFF files. It can read both striped and tiled
|
||||||
images, pixel and plane interleaved multi-band images. If you have
|
images, pixel and plane interleaved multi-band images. If you have
|
||||||
libtiff and its headers installed, PIL can read and write many kinds
|
libtiff and its headers installed, Pillow can read and write many kinds
|
||||||
of compressed TIFF files. If not, PIL will only read and write
|
of compressed TIFF files. If not, Pillow will only read and write
|
||||||
uncompressed files.
|
uncompressed files.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
@ -734,8 +751,8 @@ using the general tags available through tiffinfo.
|
||||||
WebP
|
WebP
|
||||||
^^^^
|
^^^^
|
||||||
|
|
||||||
PIL reads and writes WebP files. The specifics of PIL's capabilities with this
|
Pillow reads and writes WebP files. The specifics of Pillow's capabilities with
|
||||||
format are currently undocumented.
|
this format are currently undocumented.
|
||||||
|
|
||||||
The :py:meth:`~PIL.Image.Image.save` method supports the following options:
|
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
|
XBM
|
||||||
^^^
|
^^^
|
||||||
|
|
||||||
PIL reads and writes X bitmap files (mode ``1``).
|
Pillow reads and writes X bitmap files (mode ``1``).
|
||||||
|
|
||||||
Read-only formats
|
Read-only formats
|
||||||
-----------------
|
-----------------
|
||||||
|
@ -841,15 +858,15 @@ DDS
|
||||||
|
|
||||||
DDS is a popular container texture format used in video games and natively
|
DDS is a popular container texture format used in video games and natively
|
||||||
supported by DirectX.
|
supported by DirectX.
|
||||||
Currently, DXT1, DXT3, and DXT5 pixel formats are supported and only in ``RGBA``
|
Currently, uncompressed RGB data and DXT1, DXT3, and DXT5 pixel formats are
|
||||||
mode.
|
supported, and only in ``RGBA`` mode.
|
||||||
|
|
||||||
.. versionadded:: 3.4.0 DXT3
|
.. versionadded:: 3.4.0 DXT3
|
||||||
|
|
||||||
FLI, FLC
|
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
|
The :py:meth:`~PIL.Image.Image.open` method sets the following
|
||||||
:py:attr:`~PIL.Image.Image.info` properties:
|
:py:attr:`~PIL.Image.Image.info` properties:
|
||||||
|
@ -860,7 +877,7 @@ The :py:meth:`~PIL.Image.Image.open` method sets the following
|
||||||
FPX
|
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
|
resolution image is read from the file, and the viewing transform is not taken
|
||||||
into account.
|
into account.
|
||||||
|
|
||||||
|
@ -896,7 +913,7 @@ The :py:meth:`~PIL.Image.Image.open` method sets the following
|
||||||
GD
|
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.
|
:py:func:`PIL.GdImageFile.open` to read such a file.
|
||||||
|
|
||||||
The :py:meth:`~PIL.Image.Image.open` method sets the following
|
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
|
IMT
|
||||||
^^^
|
^^^
|
||||||
|
|
||||||
PIL reads Image Tools images containing ``L`` data.
|
Pillow reads Image Tools images containing ``L`` data.
|
||||||
|
|
||||||
IPTC/NAA
|
IPTC/NAA
|
||||||
^^^^^^^^
|
^^^^^^^^
|
||||||
|
|
||||||
PIL provides limited read support for IPTC/NAA newsphoto files.
|
Pillow provides limited read support for IPTC/NAA newsphoto files.
|
||||||
|
|
||||||
MCIDAS
|
MCIDAS
|
||||||
^^^^^^
|
^^^^^^
|
||||||
|
|
||||||
PIL identifies and reads 8-bit McIdas area files.
|
Pillow identifies and reads 8-bit McIdas area files.
|
||||||
|
|
||||||
MIC
|
MIC
|
||||||
^^^
|
^^^
|
||||||
|
|
||||||
PIL identifies and reads Microsoft Image Composer (MIC) files. When opened, the
|
Pillow identifies and reads Microsoft Image Composer (MIC) files. When opened,
|
||||||
first sprite in the file is loaded. You can use :py:meth:`~file.seek` and
|
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.
|
: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.
|
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
|
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
|
resolution image from the file. Higher resolutions are encoded in a proprietary
|
||||||
encoding.
|
encoding.
|
||||||
|
|
||||||
PIXAR
|
PIXAR
|
||||||
^^^^^
|
^^^^^
|
||||||
|
|
||||||
PIL provides limited support for PIXAR raster files. The library can identify
|
Pillow provides limited support for PIXAR raster files. The library can
|
||||||
and read “dumped” RGB files.
|
identify and read “dumped” RGB files.
|
||||||
|
|
||||||
The format code is ``PIXAR``.
|
The format code is ``PIXAR``.
|
||||||
|
|
||||||
PSD
|
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
|
WAL
|
||||||
|
@ -964,7 +981,7 @@ WAL
|
||||||
|
|
||||||
.. versionadded:: 1.1.4
|
.. 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
|
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
|
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
|
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
|
The :py:meth:`~PIL.Image.Image.open` method sets the following
|
||||||
:py:attr:`~PIL.Image.Image.info` properties:
|
:py:attr:`~PIL.Image.Image.info` properties:
|
||||||
|
@ -991,14 +1008,14 @@ Write-only formats
|
||||||
PALM
|
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``.
|
The format code is ``Palm``, the extension is ``.palm``.
|
||||||
|
|
||||||
PDF
|
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
|
files, using either JPEG or HEX encoding depending on the image mode (and
|
||||||
whether JPEG support is available or not).
|
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
|
XV Thumbnails
|
||||||
^^^^^^^^^^^^^
|
^^^^^^^^^^^^^
|
||||||
|
|
||||||
PIL can read XV thumbnail files.
|
Pillow can read XV thumbnail files.
|
||||||
|
|
||||||
Identify-only formats
|
Identify-only formats
|
||||||
---------------------
|
---------------------
|
||||||
|
@ -1087,7 +1104,7 @@ BUFR
|
||||||
|
|
||||||
.. versionadded:: 1.1.3
|
.. 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
|
To add read or write support to your application, use
|
||||||
:py:func:`PIL.BufrStubImagePlugin.register_handler`.
|
:py:func:`PIL.BufrStubImagePlugin.register_handler`.
|
||||||
|
@ -1097,7 +1114,7 @@ FITS
|
||||||
|
|
||||||
.. versionadded:: 1.1.5
|
.. 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
|
To add read or write support to your application, use
|
||||||
:py:func:`PIL.FitsStubImagePlugin.register_handler`.
|
:py:func:`PIL.FitsStubImagePlugin.register_handler`.
|
||||||
|
@ -1107,11 +1124,11 @@ GRIB
|
||||||
|
|
||||||
.. versionadded:: 1.1.5
|
.. 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
|
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
|
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
|
To add read or write support to your application, use
|
||||||
:py:func:`PIL.GribStubImagePlugin.register_handler`.
|
:py:func:`PIL.GribStubImagePlugin.register_handler`.
|
||||||
|
@ -1121,7 +1138,7 @@ HDF5
|
||||||
|
|
||||||
.. versionadded:: 1.1.5
|
.. 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
|
To add read or write support to your application, use
|
||||||
:py:func:`PIL.Hdf5StubImagePlugin.register_handler`.
|
:py:func:`PIL.Hdf5StubImagePlugin.register_handler`.
|
||||||
|
@ -1129,12 +1146,12 @@ To add read or write support to your application, use
|
||||||
MPEG
|
MPEG
|
||||||
^^^^
|
^^^^
|
||||||
|
|
||||||
PIL identifies MPEG files.
|
Pillow identifies MPEG files.
|
||||||
|
|
||||||
WMF
|
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
|
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.
|
support, but not enough to be useful for any real application.
|
||||||
|
|
|
@ -15,21 +15,23 @@ Notes
|
||||||
|
|
||||||
.. note:: Pillow is supported on the following Python versions
|
.. 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**|
|
|**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.0.0 | Yes | Yes | Yes | Yes | | | | | | |
|
||||||
+--------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
|
+---------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
|
||||||
|Pillow 2.x - 3.x | | | Yes | Yes | Yes | Yes | Yes | Yes | | |
|
|Pillow 2.x - 3.x | | | Yes | Yes | Yes | Yes | Yes | Yes | | |
|
||||||
+--------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
|
+---------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
|
||||||
|Pillow 4.x | | | | 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.0.x - 5.1.x | | | | Yes | | | Yes | Yes | Yes | |
|
||||||
+--------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
|
+---------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
|
||||||
|Pillow 5.2.x - 5.4.x| | | | Yes | | | 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
|
Basic Installation
|
||||||
------------------
|
------------------
|
||||||
|
|
|
@ -7,6 +7,12 @@
|
||||||
The :py:mod:`ImageQt` module contains support for creating PyQt4, PyQt5, PySide or
|
The :py:mod:`ImageQt` module contains support for creating PyQt4, PyQt5, PySide or
|
||||||
PySide2 QImage objects from PIL images.
|
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
|
.. versionadded:: 1.1.6
|
||||||
|
|
||||||
.. py:class:: ImageQt.ImageQt(image)
|
.. py:class:: ImageQt.ImageQt(image)
|
||||||
|
|
|
@ -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
|
and have now been removed: ``gaussian_blur``, ``gblur``, ``unsharp_mask``, ``usm`` and
|
||||||
``box_blur``. Use the equivalent operations in ``ImageFilter`` instead.
|
``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
|
API Changes
|
||||||
===========
|
===========
|
||||||
|
|
||||||
Deprecations
|
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
|
These version constants have been deprecated and will be removed in a future
|
||||||
version.
|
version.
|
||||||
|
|
||||||
|
@ -76,15 +102,27 @@ Use ``PIL.__version__`` instead.
|
||||||
API Additions
|
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
|
Other Changes
|
||||||
=============
|
=============
|
||||||
|
|
||||||
TODO
|
Reading new DDS image format
|
||||||
^^^^
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
TODO
|
Pillow can now read uncompressed RGB data from DDS images.
|
||||||
|
|
|
@ -52,6 +52,10 @@ def _accept(prefix):
|
||||||
return prefix[:2] == b"BM"
|
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.
|
# 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:
|
try:
|
||||||
rawmode, bits, colors = SAVE[im.mode]
|
rawmode, bits, colors = SAVE[im.mode]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@ -306,10 +314,11 @@ def _save(im, fp, filename):
|
||||||
|
|
||||||
stride = ((im.size[0]*bits+7)//8+3) & (~3)
|
stride = ((im.size[0]*bits+7)//8+3) & (~3)
|
||||||
header = 40 # or 64 for OS/2 version 2
|
header = 40 # or 64 for OS/2 version 2
|
||||||
offset = 14 + header + colors * 4
|
|
||||||
image = stride * im.size[1]
|
image = stride * im.size[1]
|
||||||
|
|
||||||
# bitmap header
|
# bitmap header
|
||||||
|
if bitmap_header:
|
||||||
|
offset = 14 + header + colors * 4
|
||||||
fp.write(b"BM" + # file type (magic)
|
fp.write(b"BM" + # file type (magic)
|
||||||
o32(offset+image) + # file size
|
o32(offset+image) + # file size
|
||||||
o32(0) + # reserved
|
o32(0) + # reserved
|
||||||
|
@ -352,3 +361,10 @@ Image.register_save(BmpImageFile.format, _save)
|
||||||
Image.register_extension(BmpImageFile.format, ".bmp")
|
Image.register_extension(BmpImageFile.format, ".bmp")
|
||||||
|
|
||||||
Image.register_mime(BmpImageFile.format, "image/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")
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
# A file object that provides read access to a part of an existing
|
# A file object that provides read access to a part of an existing
|
||||||
# file (for example a TAR file).
|
# file (for example a TAR file).
|
||||||
|
|
||||||
|
import io
|
||||||
|
|
||||||
|
|
||||||
class ContainerIO(object):
|
class ContainerIO(object):
|
||||||
|
|
||||||
|
@ -39,9 +41,9 @@ class ContainerIO(object):
|
||||||
# Always false.
|
# Always false.
|
||||||
|
|
||||||
def isatty(self):
|
def isatty(self):
|
||||||
return 0
|
return False
|
||||||
|
|
||||||
def seek(self, offset, mode=0):
|
def seek(self, offset, mode=io.SEEK_SET):
|
||||||
"""
|
"""
|
||||||
Move file pointer.
|
Move file pointer.
|
||||||
|
|
||||||
|
|
|
@ -123,9 +123,18 @@ class DdsImageFile(ImageFile.ImageFile):
|
||||||
# pixel format
|
# pixel format
|
||||||
pfsize, pfflags = struct.unpack("<2I", header.read(8))
|
pfsize, pfflags = struct.unpack("<2I", header.read(8))
|
||||||
fourcc = header.read(4)
|
fourcc = header.read(4)
|
||||||
bitcount, rmask, gmask, bmask, amask = struct.unpack("<5I",
|
bitcount, = struct.unpack("<I", header.read(4))
|
||||||
header.read(20))
|
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]
|
||||||
|
|
||||||
|
self.tile = [("raw", (0, 0) + self.size, 0, (rawmode, 0, 1))]
|
||||||
|
else:
|
||||||
data_start = header_size + 4
|
data_start = header_size + 4
|
||||||
n = 0
|
n = 0
|
||||||
if fourcc == b"DXT1":
|
if fourcc == b"DXT1":
|
||||||
|
|
|
@ -104,7 +104,7 @@ def Ghostscript(tile, size, fp, scale=1):
|
||||||
# Copy whole file to read in Ghostscript
|
# Copy whole file to read in Ghostscript
|
||||||
with open(infile_temp, 'wb') as f:
|
with open(infile_temp, 'wb') as f:
|
||||||
# fetch length of fp
|
# fetch length of fp
|
||||||
fp.seek(0, 2)
|
fp.seek(0, io.SEEK_END)
|
||||||
fsize = fp.tell()
|
fsize = fp.tell()
|
||||||
# ensure start position
|
# ensure start position
|
||||||
# go back
|
# go back
|
||||||
|
@ -169,7 +169,7 @@ class PSFile(object):
|
||||||
self.fp = fp
|
self.fp = fp
|
||||||
self.char = None
|
self.char = None
|
||||||
|
|
||||||
def seek(self, offset, whence=0):
|
def seek(self, offset, whence=io.SEEK_SET):
|
||||||
self.char = None
|
self.char = None
|
||||||
self.fp.seek(offset, whence)
|
self.fp.seek(offset, whence)
|
||||||
|
|
||||||
|
@ -312,7 +312,7 @@ class EpsImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
if s[:4] == b"%!PS":
|
if s[:4] == b"%!PS":
|
||||||
# for HEAD without binary preview
|
# for HEAD without binary preview
|
||||||
fp.seek(0, 2)
|
fp.seek(0, io.SEEK_END)
|
||||||
length = fp.tell()
|
length = fp.tell()
|
||||||
offset = 0
|
offset = 0
|
||||||
elif i32(s[0:4]) == 0xC6D3D0C5:
|
elif i32(s[0:4]) == 0xC6D3D0C5:
|
||||||
|
|
|
@ -604,16 +604,16 @@ def _save_netpbm(im, fp, filename):
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from subprocess import Popen, check_call, PIPE, CalledProcessError
|
from subprocess import Popen, check_call, PIPE, CalledProcessError
|
||||||
file = im._dump()
|
tempfile = im._dump()
|
||||||
|
|
||||||
with open(filename, 'wb') as f:
|
with open(filename, 'wb') as f:
|
||||||
if im.mode != "RGB":
|
if im.mode != "RGB":
|
||||||
with open(os.devnull, 'wb') as devnull:
|
with open(os.devnull, 'wb') as devnull:
|
||||||
check_call(["ppmtogif", file], stdout=f, stderr=devnull)
|
check_call(["ppmtogif", tempfile], stdout=f, stderr=devnull)
|
||||||
else:
|
else:
|
||||||
# Pipe ppmquant output into ppmtogif
|
# Pipe ppmquant output into ppmtogif
|
||||||
# "ppmquant 256 %s | ppmtogif > %s" % (file, filename)
|
# "ppmquant 256 %s | ppmtogif > %s" % (tempfile, filename)
|
||||||
quant_cmd = ["ppmquant", "256", file]
|
quant_cmd = ["ppmquant", "256", tempfile]
|
||||||
togif_cmd = ["ppmtogif"]
|
togif_cmd = ["ppmtogif"]
|
||||||
with open(os.devnull, 'wb') as devnull:
|
with open(os.devnull, 'wb') as devnull:
|
||||||
quant_proc = Popen(quant_cmd, stdout=PIPE, stderr=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)
|
raise CalledProcessError(retcode, togif_cmd)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
os.unlink(file)
|
os.unlink(tempfile)
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -195,7 +195,7 @@ class IcnsFile(object):
|
||||||
i += HEADERSIZE
|
i += HEADERSIZE
|
||||||
blocksize -= HEADERSIZE
|
blocksize -= HEADERSIZE
|
||||||
dct[sig] = (i, blocksize)
|
dct[sig] = (i, blocksize)
|
||||||
fobj.seek(blocksize, 1)
|
fobj.seek(blocksize, io.SEEK_CUR)
|
||||||
i += blocksize
|
i += blocksize
|
||||||
|
|
||||||
def itersizes(self):
|
def itersizes(self):
|
||||||
|
|
|
@ -24,10 +24,10 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
# VERSION is deprecated and will be removed in Pillow 6.0.0.
|
# VERSION was removed in Pillow 6.0.0.
|
||||||
# PILLOW_VERSION is deprecated and will be removed after that.
|
# PILLOW_VERSION is deprecated and will be removed in Pillow 7.0.0.
|
||||||
# Use __version__ instead.
|
# Use __version__ instead.
|
||||||
from . import VERSION, PILLOW_VERSION, __version__, _plugins
|
from . import PILLOW_VERSION, __version__, _plugins
|
||||||
from ._util import py3
|
from ._util import py3
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
@ -60,8 +60,7 @@ except ImportError:
|
||||||
from collections import Callable
|
from collections import Callable
|
||||||
|
|
||||||
|
|
||||||
# Silence warnings
|
# Silence warning
|
||||||
assert VERSION
|
|
||||||
assert PILLOW_VERSION
|
assert PILLOW_VERSION
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -655,8 +654,7 @@ class Image(object):
|
||||||
return filename
|
return filename
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return (isinstance(other, Image) and
|
return (self.__class__ is other.__class__ and
|
||||||
self.__class__.__name__ == other.__class__.__name__ and
|
|
||||||
self.mode == other.mode and
|
self.mode == other.mode and
|
||||||
self.size == other.size and
|
self.size == other.size and
|
||||||
self.info == other.info and
|
self.info == other.info and
|
||||||
|
@ -681,8 +679,7 @@ class Image(object):
|
||||||
|
|
||||||
:returns: png version of the image as bytes
|
:returns: png version of the image as bytes
|
||||||
"""
|
"""
|
||||||
from io import BytesIO
|
b = io.BytesIO()
|
||||||
b = BytesIO()
|
|
||||||
self.save(b, 'PNG')
|
self.save(b, 'PNG')
|
||||||
return b.getvalue()
|
return b.getvalue()
|
||||||
|
|
||||||
|
@ -1052,7 +1049,7 @@ class Image(object):
|
||||||
new_im.info['transparency'] = trns
|
new_im.info['transparency'] = trns
|
||||||
return new_im
|
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
|
Convert the image to 'P' mode with the specified number
|
||||||
of colors.
|
of colors.
|
||||||
|
@ -1065,6 +1062,10 @@ class Image(object):
|
||||||
:param kmeans: Integer
|
:param kmeans: Integer
|
||||||
:param palette: Quantize to the palette of given
|
:param palette: Quantize to the palette of given
|
||||||
:py:class:`PIL.Image.Image`.
|
: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
|
:returns: A new image
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -1092,7 +1093,7 @@ class Image(object):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"only RGB or L mode images can be quantized to a palette"
|
"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(im)
|
||||||
|
|
||||||
return self._new(self.im.quantize(colors, method, kmeans))
|
return self._new(self.im.quantize(colors, method, kmeans))
|
||||||
|
@ -2005,7 +2006,7 @@ class Image(object):
|
||||||
library automatically seeks to frame 0.
|
library automatically seeks to frame 0.
|
||||||
|
|
||||||
Note that in the current version of the library, most sequence
|
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`.
|
See :py:meth:`~PIL.Image.Image.tell`.
|
||||||
|
|
||||||
|
@ -2024,10 +2025,10 @@ class Image(object):
|
||||||
debugging purposes.
|
debugging purposes.
|
||||||
|
|
||||||
On Unix platforms, this method saves the image to a temporary
|
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.
|
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.
|
opens it with the native Preview application.
|
||||||
|
|
||||||
On Windows, it saves the image to a temporary BMP file, and uses
|
On Windows, it saves the image to a temporary BMP file, and uses
|
||||||
|
@ -2129,11 +2130,12 @@ class Image(object):
|
||||||
|
|
||||||
self.draft(None, size)
|
self.draft(None, size)
|
||||||
|
|
||||||
|
if self.size != size:
|
||||||
im = self.resize(size, resample)
|
im = self.resize(size, resample)
|
||||||
|
|
||||||
self.im = im.im
|
self.im = im.im
|
||||||
self.mode = im.mode
|
|
||||||
self._size = size
|
self._size = size
|
||||||
|
self.mode = self.im.mode
|
||||||
|
|
||||||
self.readonly = 0
|
self.readonly = 0
|
||||||
self.pyaccess = None
|
self.pyaccess = None
|
||||||
|
|
|
@ -950,5 +950,5 @@ def versions():
|
||||||
|
|
||||||
return (
|
return (
|
||||||
VERSION, core.littlecms_version,
|
VERSION, core.littlecms_version,
|
||||||
sys.version.split()[0], Image.VERSION
|
sys.version.split()[0], Image.__version__
|
||||||
)
|
)
|
||||||
|
|
|
@ -120,9 +120,10 @@ class ImageFile(Image.Image):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def get_format_mimetype(self):
|
def get_format_mimetype(self):
|
||||||
if self.format is None:
|
if self.custom_mimetype:
|
||||||
return
|
return self.custom_mimetype
|
||||||
return self.custom_mimetype or Image.MIME.get(self.format.upper())
|
if self.format is not None:
|
||||||
|
return Image.MIME.get(self.format.upper())
|
||||||
|
|
||||||
def verify(self):
|
def verify(self):
|
||||||
"""Check file integrity"""
|
"""Check file integrity"""
|
||||||
|
@ -491,7 +492,7 @@ def _save(im, fp, tile, bufsize=0):
|
||||||
for e, b, o, a in tile:
|
for e, b, o, a in tile:
|
||||||
e = Image._getencoder(im.mode, e, a, im.encoderconfig)
|
e = Image._getencoder(im.mode, e, a, im.encoderconfig)
|
||||||
if o > 0:
|
if o > 0:
|
||||||
fp.seek(o, 0)
|
fp.seek(o)
|
||||||
e.setimage(im.im, b)
|
e.setimage(im.im, b)
|
||||||
if e.pushes_fd:
|
if e.pushes_fd:
|
||||||
e.setfd(fp)
|
e.setfd(fp)
|
||||||
|
@ -510,7 +511,7 @@ def _save(im, fp, tile, bufsize=0):
|
||||||
for e, b, o, a in tile:
|
for e, b, o, a in tile:
|
||||||
e = Image._getencoder(im.mode, e, a, im.encoderconfig)
|
e = Image._getencoder(im.mode, e, a, im.encoderconfig)
|
||||||
if o > 0:
|
if o > 0:
|
||||||
fp.seek(o, 0)
|
fp.seek(o)
|
||||||
e.setimage(im.im, b)
|
e.setimage(im.im, b)
|
||||||
if e.pushes_fd:
|
if e.pushes_fd:
|
||||||
e.setfd(fp)
|
e.setfd(fp)
|
||||||
|
|
|
@ -28,7 +28,7 @@ VERBOSE = 0
|
||||||
|
|
||||||
|
|
||||||
def _isconstant(v):
|
def _isconstant(v):
|
||||||
return isinstance(v, int) or isinstance(v, float)
|
return isinstance(v, (int, float))
|
||||||
|
|
||||||
|
|
||||||
class _Operand(object):
|
class _Operand(object):
|
||||||
|
|
|
@ -20,6 +20,7 @@ from . import Image
|
||||||
from ._util import isPath, py3
|
from ._util import isPath, py3
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
import sys
|
import sys
|
||||||
|
import warnings
|
||||||
|
|
||||||
qt_versions = [
|
qt_versions = [
|
||||||
['5', 'PyQt5'],
|
['5', 'PyQt5'],
|
||||||
|
@ -27,6 +28,12 @@ qt_versions = [
|
||||||
['4', 'PyQt4'],
|
['4', 'PyQt4'],
|
||||||
['side', 'PySide']
|
['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
|
# If a version has already been imported, attempt it first
|
||||||
qt_versions.sort(key=lambda qt_version: qt_version[1] in sys.modules,
|
qt_versions.sort(key=lambda qt_version: qt_version[1] in sys.modules,
|
||||||
reverse=True)
|
reverse=True)
|
||||||
|
@ -41,9 +48,13 @@ for qt_version, qt_module in qt_versions:
|
||||||
elif qt_module == 'PyQt4':
|
elif qt_module == 'PyQt4':
|
||||||
from PyQt4.QtGui import QImage, qRgba, QPixmap
|
from PyQt4.QtGui import QImage, qRgba, QPixmap
|
||||||
from PyQt4.QtCore import QBuffer, QIODevice
|
from PyQt4.QtCore import QBuffer, QIODevice
|
||||||
|
|
||||||
|
warnings.warn(WARNING_TEXT.format(qt_module), DeprecationWarning)
|
||||||
elif qt_module == 'PySide':
|
elif qt_module == 'PySide':
|
||||||
from PySide.QtGui import QImage, qRgba, QPixmap
|
from PySide.QtGui import QImage, qRgba, QPixmap
|
||||||
from PySide.QtCore import QBuffer, QIODevice
|
from PySide.QtCore import QBuffer, QIODevice
|
||||||
|
|
||||||
|
warnings.warn(WARNING_TEXT.format(qt_module), DeprecationWarning)
|
||||||
except (ImportError, RuntimeError):
|
except (ImportError, RuntimeError):
|
||||||
continue
|
continue
|
||||||
qt_is_installed = True
|
qt_is_installed = True
|
||||||
|
@ -67,7 +78,7 @@ def fromqimage(im):
|
||||||
"""
|
"""
|
||||||
buffer = QBuffer()
|
buffer = QBuffer()
|
||||||
buffer.open(QIODevice.ReadWrite)
|
buffer.open(QIODevice.ReadWrite)
|
||||||
# preserve alha channel with png
|
# preserve alpha channel with png
|
||||||
# otherwise ppm is more friendly with Image.open
|
# otherwise ppm is more friendly with Image.open
|
||||||
if im.hasAlphaChannel():
|
if im.hasAlphaChannel():
|
||||||
im.save(buffer, 'png')
|
im.save(buffer, 'png')
|
||||||
|
|
|
@ -193,9 +193,9 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
|
||||||
fd = -1
|
fd = -1
|
||||||
try:
|
try:
|
||||||
pos = self.fp.tell()
|
pos = self.fp.tell()
|
||||||
self.fp.seek(0, 2)
|
self.fp.seek(0, io.SEEK_END)
|
||||||
length = self.fp.tell()
|
length = self.fp.tell()
|
||||||
self.fp.seek(pos, 0)
|
self.fp.seek(pos)
|
||||||
except Exception:
|
except Exception:
|
||||||
length = -1
|
length = -1
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
import io
|
||||||
from . import Image, FontFile
|
from . import Image, FontFile
|
||||||
from ._binary import i8, i16le as l16, i32le as l32, i16be as b16, i32be as b32
|
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):
|
for i in range(nprops):
|
||||||
p.append((i32(fp.read(4)), i8(fp.read(1)), i32(fp.read(4))))
|
p.append((i32(fp.read(4)), i8(fp.read(1)), i32(fp.read(4))))
|
||||||
if nprops & 3:
|
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)))
|
data = fp.read(i32(fp.read(4)))
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
import io
|
||||||
import logging
|
import logging
|
||||||
from . import Image, ImageFile, ImagePalette
|
from . import Image, ImageFile, ImagePalette
|
||||||
from ._binary import i8, i16le as i16, o8, o16le as o16
|
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:
|
elif version == 5 and bits == 8 and planes == 1:
|
||||||
mode = rawmode = "L"
|
mode = rawmode = "L"
|
||||||
# FIXME: hey, this doesn't work with the incremental loader !!!
|
# 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)
|
s = self.fp.read(769)
|
||||||
if len(s) == 769 and i8(s[0]) == 12:
|
if len(s) == 769 and i8(s[0]) == 12:
|
||||||
# check if the palette is linear greyscale
|
# check if the palette is linear greyscale
|
||||||
|
|
|
@ -269,14 +269,9 @@ class PdfDict(UserDict):
|
||||||
else:
|
else:
|
||||||
self.__dict__[key] = value
|
self.__dict__[key] = value
|
||||||
else:
|
else:
|
||||||
if isinstance(key, str):
|
self[key.encode("us-ascii")] = value
|
||||||
key = key.encode("us-ascii")
|
|
||||||
self[key] = value
|
|
||||||
|
|
||||||
def __getattr__(self, key):
|
def __getattr__(self, key):
|
||||||
try:
|
|
||||||
value = self[key]
|
|
||||||
except KeyError:
|
|
||||||
try:
|
try:
|
||||||
value = self[key.encode("us-ascii")]
|
value = self[key.encode("us-ascii")]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@ -361,8 +356,7 @@ def pdf_repr(x):
|
||||||
return b"false"
|
return b"false"
|
||||||
elif x is None:
|
elif x is None:
|
||||||
return b"null"
|
return b"null"
|
||||||
elif (isinstance(x, PdfName) or isinstance(x, PdfDict) or
|
elif isinstance(x, (PdfName, PdfDict, PdfArray, PdfBinary)):
|
||||||
isinstance(x, PdfArray) or isinstance(x, PdfBinary)):
|
|
||||||
return bytes(x)
|
return bytes(x)
|
||||||
elif isinstance(x, int):
|
elif isinstance(x, int):
|
||||||
return str(x).encode("us-ascii")
|
return str(x).encode("us-ascii")
|
||||||
|
|
|
@ -194,7 +194,7 @@ class iTXt(str):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@staticmethod
|
@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 cls: the class to use when creating the instance
|
||||||
:param text: value for this key
|
:param text: value for this key
|
||||||
|
@ -529,6 +529,11 @@ class PngStream(ChunkStream):
|
||||||
|
|
||||||
return s
|
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
|
# APNG chunks
|
||||||
def chunk_acTL(self, pos, length):
|
def chunk_acTL(self, pos, length):
|
||||||
s = ImageFile._safe_read(self.fp, length)
|
s = ImageFile._safe_read(self.fp, length)
|
||||||
|
@ -683,6 +688,12 @@ class PngImageFile(ImageFile.ImageFile):
|
||||||
self.png.close()
|
self.png.close()
|
||||||
self.png = None
|
self.png = None
|
||||||
|
|
||||||
|
def _getexif(self):
|
||||||
|
if "exif" not in self.info:
|
||||||
|
self.load()
|
||||||
|
from .JpegImagePlugin import _getexif
|
||||||
|
return _getexif(self)
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# PNG writer
|
# PNG writer
|
||||||
|
@ -861,6 +872,12 @@ def _save(im, fp, filename, chunk=putchunk):
|
||||||
chunks.remove(cid)
|
chunks.remove(cid)
|
||||||
chunk(fp, cid, data)
|
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),
|
ImageFile._save(im, _idat(fp, chunk),
|
||||||
[("zip", (0, 0)+im.size, 0, rawmode)])
|
[("zip", (0, 0)+im.size, 0, rawmode)])
|
||||||
|
|
||||||
|
|
|
@ -70,7 +70,14 @@ class PpmImageFile(ImageFile.ImageFile):
|
||||||
s = self.fp.read(1)
|
s = self.fp.read(1)
|
||||||
if s != b"P":
|
if s != b"P":
|
||||||
raise SyntaxError("not a PPM file")
|
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":
|
if mode == "1":
|
||||||
self.mode = "1"
|
self.mode = "1"
|
||||||
|
@ -158,3 +165,5 @@ Image.register_open(PpmImageFile.format, PpmImageFile, _accept)
|
||||||
Image.register_save(PpmImageFile.format, _save)
|
Image.register_save(PpmImageFile.format, _save)
|
||||||
|
|
||||||
Image.register_extensions(PpmImageFile.format, [".pbm", ".pgm", ".ppm"])
|
Image.register_extensions(PpmImageFile.format, [".pbm", ".pgm", ".ppm"])
|
||||||
|
|
||||||
|
Image.register_mime(PpmImageFile.format, "image/x-portable-anymap")
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
# PIL.__version__ instead.
|
# PIL.__version__ instead.
|
||||||
__version__ = "0.4"
|
__version__ = "0.4"
|
||||||
|
|
||||||
|
import io
|
||||||
from . import Image, ImageFile, ImagePalette
|
from . import Image, ImageFile, ImagePalette
|
||||||
from ._binary import i8, i16be as i16, i32be as i32
|
from ._binary import i8, i16be as i16, i32be as i32
|
||||||
|
|
||||||
|
@ -216,12 +217,12 @@ def _layerinfo(file):
|
||||||
if size:
|
if size:
|
||||||
length = i32(read(4))
|
length = i32(read(4))
|
||||||
if length:
|
if length:
|
||||||
file.seek(length - 16, 1)
|
file.seek(length - 16, io.SEEK_CUR)
|
||||||
combined += length + 4
|
combined += length + 4
|
||||||
|
|
||||||
length = i32(read(4))
|
length = i32(read(4))
|
||||||
if length:
|
if length:
|
||||||
file.seek(length, 1)
|
file.seek(length, io.SEEK_CUR)
|
||||||
combined += length + 4
|
combined += length + 4
|
||||||
|
|
||||||
length = i8(read(1))
|
length = i8(read(1))
|
||||||
|
@ -231,7 +232,7 @@ def _layerinfo(file):
|
||||||
name = read(length).decode('latin-1', 'replace')
|
name = read(length).decode('latin-1', 'replace')
|
||||||
combined += length + 1
|
combined += length + 1
|
||||||
|
|
||||||
file.seek(size - combined, 1)
|
file.seek(size - combined, io.SEEK_CUR)
|
||||||
layers.append((name, mode, (x0, y0, x1, y1)))
|
layers.append((name, mode, (x0, y0, x1, y1)))
|
||||||
|
|
||||||
# get tiles
|
# get tiles
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
import io
|
||||||
import sys
|
import sys
|
||||||
from . import ContainerIO
|
from . import ContainerIO
|
||||||
|
|
||||||
|
@ -51,7 +52,7 @@ class TarIO(ContainerIO.ContainerIO):
|
||||||
if file == name:
|
if file == name:
|
||||||
break
|
break
|
||||||
|
|
||||||
self.fh.seek((size + 511) & (~511), 1)
|
self.fh.seek((size + 511) & (~511), io.SEEK_CUR)
|
||||||
|
|
||||||
# Open region
|
# Open region
|
||||||
ContainerIO.ContainerIO.__init__(self, self.fh, self.fh.tell(), size)
|
ContainerIO.ContainerIO.__init__(self, self.fh, self.fh.tell(), size)
|
||||||
|
|
|
@ -135,6 +135,9 @@ COMPRESSION_INFO = {
|
||||||
32946: "tiff_deflate",
|
32946: "tiff_deflate",
|
||||||
34676: "tiff_sgilog",
|
34676: "tiff_sgilog",
|
||||||
34677: "tiff_sgilog24",
|
34677: "tiff_sgilog24",
|
||||||
|
34925: "lzma",
|
||||||
|
50000: "zstd",
|
||||||
|
50001: "webp",
|
||||||
}
|
}
|
||||||
|
|
||||||
COMPRESSION_INFO_REV = {v: k for k, v in COMPRESSION_INFO.items()}
|
COMPRESSION_INFO_REV = {v: k for k, v in COMPRESSION_INFO.items()}
|
||||||
|
@ -423,7 +426,7 @@ class ImageFileDirectory_v2(MutableMapping):
|
||||||
|
|
||||||
ifd = ImageFileDirectory_v2()
|
ifd = ImageFileDirectory_v2()
|
||||||
ifd[key] = 'Some Data'
|
ifd[key] = 'Some Data'
|
||||||
ifd.tagtype[key] = 2
|
ifd.tagtype[key] = TiffTags.ASCII
|
||||||
print(ifd[key])
|
print(ifd[key])
|
||||||
'Some Data'
|
'Some Data'
|
||||||
|
|
||||||
|
@ -557,7 +560,7 @@ class ImageFileDirectory_v2(MutableMapping):
|
||||||
if info.type:
|
if info.type:
|
||||||
self.tagtype[tag] = info.type
|
self.tagtype[tag] = info.type
|
||||||
else:
|
else:
|
||||||
self.tagtype[tag] = 7
|
self.tagtype[tag] = TiffTags.UNDEFINED
|
||||||
if all(isinstance(v, IFDRational) for v in values):
|
if all(isinstance(v, IFDRational) for v in values):
|
||||||
self.tagtype[tag] = TiffTags.RATIONAL
|
self.tagtype[tag] = TiffTags.RATIONAL
|
||||||
elif all(isinstance(v, int) for v in values):
|
elif all(isinstance(v, int) for v in values):
|
||||||
|
@ -816,7 +819,7 @@ class ImageFileDirectory_v2(MutableMapping):
|
||||||
print("- value:", values)
|
print("- value:", values)
|
||||||
|
|
||||||
# count is sum of lengths for string and arbitrary data
|
# 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)
|
count = len(data)
|
||||||
else:
|
else:
|
||||||
count = len(values)
|
count = len(values)
|
||||||
|
@ -872,7 +875,7 @@ class ImageFileDirectory_v1(ImageFileDirectory_v2):
|
||||||
|
|
||||||
ifd = ImageFileDirectory_v1()
|
ifd = ImageFileDirectory_v1()
|
||||||
ifd[key] = 'Some Data'
|
ifd[key] = 'Some Data'
|
||||||
ifd.tagtype[key] = 2
|
ifd.tagtype[key] = TiffTags.ASCII
|
||||||
print(ifd[key])
|
print(ifd[key])
|
||||||
('Some Data',)
|
('Some Data',)
|
||||||
|
|
||||||
|
@ -1440,7 +1443,7 @@ def _save(im, fp, filename):
|
||||||
try:
|
try:
|
||||||
ifd.tagtype[key] = info.tagtype[key]
|
ifd.tagtype[key] = info.tagtype[key]
|
||||||
except Exception:
|
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
|
# additions written by Greg Couch, gregc@cgl.ucsf.edu
|
||||||
# inspired by image-sig posting from Kevin Cazabon, kcazabon@home.com
|
# inspired by image-sig posting from Kevin Cazabon, kcazabon@home.com
|
||||||
|
@ -1684,7 +1687,7 @@ class AppendingTiffWriter:
|
||||||
def tell(self):
|
def tell(self):
|
||||||
return self.f.tell() - self.offsetOfNewPage
|
return self.f.tell() - self.offsetOfNewPage
|
||||||
|
|
||||||
def seek(self, offset, whence):
|
def seek(self, offset, whence=io.SEEK_SET):
|
||||||
if whence == os.SEEK_SET:
|
if whence == os.SEEK_SET:
|
||||||
offset += self.offsetOfNewPage
|
offset += self.offsetOfNewPage
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,9 @@ PIL.VERSION is the old PIL version and will be removed in the future.
|
||||||
|
|
||||||
from . import _version
|
from . import _version
|
||||||
|
|
||||||
# VERSION is deprecated and will be removed in Pillow 6.0.0.
|
# VERSION was removed in Pillow 6.0.0.
|
||||||
# PILLOW_VERSION is deprecated and will be removed after that.
|
# PILLOW_VERSION is deprecated and will be removed in Pillow 7.0.0.
|
||||||
# Use __version__ instead.
|
# Use __version__ instead.
|
||||||
VERSION = '1.1.7' # PIL Version
|
|
||||||
PILLOW_VERSION = __version__ = _version.__version__
|
PILLOW_VERSION = __version__ = _version.__version__
|
||||||
|
|
||||||
del _version
|
del _version
|
||||||
|
|
|
@ -2,11 +2,18 @@ import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
py3 = sys.version_info.major >= 3
|
py3 = sys.version_info.major >= 3
|
||||||
|
py36 = sys.version_info[0:2] >= (3, 6)
|
||||||
|
|
||||||
if py3:
|
if py3:
|
||||||
def isStringType(t):
|
def isStringType(t):
|
||||||
return isinstance(t, str)
|
return isinstance(t, str)
|
||||||
|
|
||||||
|
if py36:
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
def isPath(f):
|
||||||
|
return isinstance(f, (bytes, str, Path))
|
||||||
|
else:
|
||||||
def isPath(f):
|
def isPath(f):
|
||||||
return isinstance(f, (bytes, str))
|
return isinstance(f, (bytes, str))
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -39,7 +39,11 @@ ImagingFlipLeftRight(Imaging imOut, Imaging imIn)
|
||||||
ImagingSectionEnter(&cookie);
|
ImagingSectionEnter(&cookie);
|
||||||
|
|
||||||
if (imIn->image8) {
|
if (imIn->image8) {
|
||||||
|
if (strncmp(imIn->mode, "I;16", 4) == 0) {
|
||||||
|
FLIP_LEFT_RIGHT(UINT16, image8)
|
||||||
|
} else {
|
||||||
FLIP_LEFT_RIGHT(UINT8, image8)
|
FLIP_LEFT_RIGHT(UINT8, image8)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
FLIP_LEFT_RIGHT(INT32, image32)
|
FLIP_LEFT_RIGHT(INT32, image32)
|
||||||
}
|
}
|
||||||
|
@ -253,7 +257,11 @@ ImagingRotate180(Imaging imOut, Imaging imIn)
|
||||||
|
|
||||||
yr = imIn->ysize-1;
|
yr = imIn->ysize-1;
|
||||||
if (imIn->image8) {
|
if (imIn->image8) {
|
||||||
|
if (strncmp(imIn->mode, "I;16", 4) == 0) {
|
||||||
|
ROTATE_180(UINT16, image8)
|
||||||
|
} else {
|
||||||
ROTATE_180(UINT8, image8)
|
ROTATE_180(UINT8, image8)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ROTATE_180(INT32, image32)
|
ROTATE_180(INT32, image32)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user