Merge branch 'master' into imagecms-deprecations
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
|
@ -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
|
@ -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.
|
||||||
|
|
72
CHANGES.rst
|
@ -5,6 +5,78 @@ Changelog (Pillow)
|
||||||
6.0.0 (unreleased)
|
6.0.0 (unreleased)
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
- Python 2.7 support will be removed in Pillow 7.0.0 #3682
|
||||||
|
[hugovk]
|
||||||
|
|
||||||
|
- Added transparency for all PNG greyscale modes #3744
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Fix deprecation warnings in Python 3.8 #3749
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Fixed GIF bug when rewinding to a non-zero frame #3716
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Only close original fp in __del__ and __exit__ if original fp is exclusive #3683
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Fix BytesWarning in Tests/test_numpy.py #3725
|
||||||
|
[jdufresne]
|
||||||
|
|
||||||
|
- Add missing MIME types and extensions #3520
|
||||||
|
[pirate486743186]
|
||||||
|
|
||||||
|
- Add I;16 PNG save #3566
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Add support for BMP RGBA bitfield compression #3705
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Added ability to set language for text rendering #3693
|
||||||
|
[iwsfutcmd]
|
||||||
|
|
||||||
|
- Only close exclusive fp on Image __exit__ #3698
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Changed EPS subprocess stdout from devnull to None #3635
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Add reading old-JPEG compressed TIFFs #3489
|
||||||
|
[kkopachev]
|
||||||
|
|
||||||
|
- 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 to all transpose operations for I;16 modes #3563, #3741
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
- Deprecate support for PyQt4 and PySide #3655
|
- Deprecate support for PyQt4 and PySide #3655
|
||||||
[hugovk, radarhere]
|
[hugovk, radarhere]
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
45
README.rst
|
@ -20,6 +20,30 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors <https://github.
|
||||||
* - social
|
* - social
|
||||||
- |gitter| |twitter|
|
- |gitter| |twitter|
|
||||||
|
|
||||||
|
.. end-badges
|
||||||
|
|
||||||
|
More Information
|
||||||
|
----------------
|
||||||
|
|
||||||
|
- `Documentation <https://pillow.readthedocs.io/>`_
|
||||||
|
|
||||||
|
- `Installation <https://pillow.readthedocs.io/en/latest/installation.html>`_
|
||||||
|
- `Handbook <https://pillow.readthedocs.io/en/latest/handbook/index.html>`_
|
||||||
|
|
||||||
|
- `Contribute <https://github.com/python-pillow/Pillow/blob/master/.github/CONTRIBUTING.md>`_
|
||||||
|
|
||||||
|
- `Issues <https://github.com/python-pillow/Pillow/issues>`_
|
||||||
|
- `Pull requests <https://github.com/python-pillow/Pillow/pulls>`_
|
||||||
|
|
||||||
|
- `Changelog <https://github.com/python-pillow/Pillow/blob/master/CHANGES.rst>`_
|
||||||
|
|
||||||
|
- `Pre-fork <https://github.com/python-pillow/Pillow/blob/master/CHANGES.rst#pre-fork>`_
|
||||||
|
|
||||||
|
Report a Vulnerability
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
To report a security vulnerability, please follow the procedure described in the `Tidelift security policy <https://tidelift.com/docs/security>`_.
|
||||||
|
|
||||||
.. |docs| image:: https://readthedocs.org/projects/pillow/badge/?version=latest
|
.. |docs| image:: https://readthedocs.org/projects/pillow/badge/?version=latest
|
||||||
:target: https://pillow.readthedocs.io/?badge=latest
|
:target: https://pillow.readthedocs.io/?badge=latest
|
||||||
:alt: Documentation Status
|
:alt: Documentation Status
|
||||||
|
@ -61,24 +85,3 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors <https://github.
|
||||||
.. |twitter| image:: https://img.shields.io/badge/tweet-on%20Twitter-00aced.svg
|
.. |twitter| image:: https://img.shields.io/badge/tweet-on%20Twitter-00aced.svg
|
||||||
:target: https://twitter.com/PythonPillow
|
:target: https://twitter.com/PythonPillow
|
||||||
:alt: Follow on https://twitter.com/PythonPillow
|
:alt: Follow on https://twitter.com/PythonPillow
|
||||||
|
|
||||||
.. end-badges
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
More Information
|
|
||||||
----------------
|
|
||||||
|
|
||||||
- `Documentation <https://pillow.readthedocs.io/>`_
|
|
||||||
|
|
||||||
- `Installation <https://pillow.readthedocs.io/en/latest/installation.html>`_
|
|
||||||
- `Handbook <https://pillow.readthedocs.io/en/latest/handbook/index.html>`_
|
|
||||||
|
|
||||||
- `Contribute <https://github.com/python-pillow/Pillow/blob/master/.github/CONTRIBUTING.md>`_
|
|
||||||
|
|
||||||
- `Issues <https://github.com/python-pillow/Pillow/issues>`_
|
|
||||||
- `Pull requests <https://github.com/python-pillow/Pillow/pulls>`_
|
|
||||||
|
|
||||||
- `Changelog <https://github.com/python-pillow/Pillow/blob/master/CHANGES.rst>`_
|
|
||||||
|
|
||||||
- `Pre-fork <https://github.com/python-pillow/Pillow/blob/master/CHANGES.rst#pre-fork>`_
|
|
||||||
|
|
|
@ -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/1_trns.png
Normal file
After Width: | Height: | Size: 612 B |
BIN
Tests/images/exif.png
Normal file
After Width: | Height: | Size: 175 KiB |
BIN
Tests/images/hopper.pnm
Normal file
BIN
Tests/images/i_trns.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Tests/images/old-style-jpeg-compression.png
Normal file
After Width: | Height: | Size: 1.4 MiB |
BIN
Tests/images/old-style-jpeg-compression.tif
Normal file
BIN
Tests/images/rgb32bf-rgba.bmp
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
Tests/images/test_language.png
Normal file
After Width: | Height: | Size: 777 B |
BIN
Tests/images/uncompressed_rgb.dds
Executable file
BIN
Tests/images/uncompressed_rgb.png
Normal file
After Width: | Height: | Size: 143 KiB |
BIN
Tests/images/unimplemented_dxgi_format.dds
Normal file
BIN
Tests/images/unimplemented_pixel_format.dds
Executable file
|
@ -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,32 @@ 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)
|
||||||
|
|
||||||
|
def test_rgba_bitfields(self):
|
||||||
|
# This test image has been manually hexedited
|
||||||
|
# to change the bitfield compression in the header from XBGR to RGBA
|
||||||
|
im = Image.open("Tests/images/rgb32bf-rgba.bmp")
|
||||||
|
|
||||||
|
# So before the comparing the image, swap the channels
|
||||||
|
b, g, r = im.split()[1:]
|
||||||
|
im = Image.merge("RGB", (r, g, b))
|
||||||
|
|
||||||
|
target = Image.open("Tests/images/bmp/q/rgb32bf-xbgr.bmp")
|
||||||
|
self.assert_image_equal(im, target)
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -230,6 +230,15 @@ class TestFileGif(PillowTestCase):
|
||||||
|
|
||||||
self.assertEqual(im.info, info)
|
self.assertEqual(im.info, info)
|
||||||
|
|
||||||
|
def test_seek_rewind(self):
|
||||||
|
im = Image.open("Tests/images/iss634.gif")
|
||||||
|
im.seek(2)
|
||||||
|
im.seek(1)
|
||||||
|
|
||||||
|
expected = Image.open("Tests/images/iss634.gif")
|
||||||
|
expected.seek(1)
|
||||||
|
self.assert_image_equal(im, expected)
|
||||||
|
|
||||||
def test_n_frames(self):
|
def test_n_frames(self):
|
||||||
for path, n_frames in [
|
for path, n_frames in [
|
||||||
[TEST_GIF, 1],
|
[TEST_GIF, 1],
|
||||||
|
|
|
@ -14,6 +14,7 @@ class TestFileIco(PillowTestCase):
|
||||||
self.assertEqual(im.mode, "RGBA")
|
self.assertEqual(im.mode, "RGBA")
|
||||||
self.assertEqual(im.size, (16, 16))
|
self.assertEqual(im.size, (16, 16))
|
||||||
self.assertEqual(im.format, "ICO")
|
self.assertEqual(im.format, "ICO")
|
||||||
|
self.assertEqual(im.get_format_mimetype(), "image/x-icon")
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
with open("Tests/images/flower.jpg", "rb") as fp:
|
with open("Tests/images/flower.jpg", "rb") as fp:
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
im = hopper()
|
def check_tags(tiffinfo):
|
||||||
|
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():
|
||||||
if libtiff and isinstance(value, bytes):
|
reloaded_value = reloaded.tag_v2[tag]
|
||||||
value = value.decode()
|
if isinstance(reloaded_value, TiffImagePlugin.IFDRational):
|
||||||
self.assertEqual(reloaded.tag_v2[tag], value)
|
reloaded_value = float(reloaded_value)
|
||||||
|
|
||||||
|
if libtiff and isinstance(value, bytes):
|
||||||
|
value = value.decode()
|
||||||
|
|
||||||
|
self.assertEqual(reloaded_value, value)
|
||||||
|
|
||||||
|
# Test with types
|
||||||
|
ifd = TiffImagePlugin.ImageFileDirectory_v2()
|
||||||
|
for tag, tagdata in custom.items():
|
||||||
|
ifd[tag] = tagdata[0]
|
||||||
|
ifd.tagtype[tag] = tagdata[1]
|
||||||
|
check_tags(ifd)
|
||||||
|
|
||||||
|
# Test without types
|
||||||
|
check_tags({tag: tagdata[0] for tag, tagdata in custom.items()})
|
||||||
|
TiffImagePlugin.WRITE_LIBTIFF = False
|
||||||
|
|
||||||
def test_int_dpi(self):
|
def test_int_dpi(self):
|
||||||
# issue #1765
|
# issue #1765
|
||||||
|
@ -700,3 +716,10 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
im = Image.open(infile)
|
im = Image.open(infile)
|
||||||
|
|
||||||
self.assert_image_similar_tofile(im, "Tests/images/flower.jpg", 0.5)
|
self.assert_image_similar_tofile(im, "Tests/images/flower.jpg", 0.5)
|
||||||
|
|
||||||
|
def test_old_style_jpeg(self):
|
||||||
|
infile = "Tests/images/old-style-jpeg-compression.tif"
|
||||||
|
im = Image.open(infile)
|
||||||
|
|
||||||
|
self.assert_image_equal_tofile(im,
|
||||||
|
"Tests/images/old-style-jpeg-compression.png")
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -13,6 +13,7 @@ class TestFilePcx(PillowTestCase):
|
||||||
self.assertEqual(im2.mode, im.mode)
|
self.assertEqual(im2.mode, im.mode)
|
||||||
self.assertEqual(im2.size, im.size)
|
self.assertEqual(im2.size, im.size)
|
||||||
self.assertEqual(im2.format, "PCX")
|
self.assertEqual(im2.format, "PCX")
|
||||||
|
self.assertEqual(im2.get_format_mimetype(), "image/x-pcx")
|
||||||
self.assert_image_equal(im2, im)
|
self.assert_image_equal(im2, im)
|
||||||
|
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
|
|
|
@ -88,20 +88,13 @@ class TestFilePng(PillowTestCase):
|
||||||
self.assertEqual(im.format, "PNG")
|
self.assertEqual(im.format, "PNG")
|
||||||
self.assertEqual(im.get_format_mimetype(), 'image/png')
|
self.assertEqual(im.get_format_mimetype(), 'image/png')
|
||||||
|
|
||||||
hopper("1").save(test_file)
|
for mode in ["1", "L", "P", "RGB", "I", "I;16"]:
|
||||||
Image.open(test_file)
|
im = hopper(mode)
|
||||||
|
im.save(test_file)
|
||||||
hopper("L").save(test_file)
|
reloaded = Image.open(test_file)
|
||||||
Image.open(test_file)
|
if mode == "I;16":
|
||||||
|
reloaded = reloaded.convert(mode)
|
||||||
hopper("P").save(test_file)
|
self.assert_image_equal(reloaded, im)
|
||||||
Image.open(test_file)
|
|
||||||
|
|
||||||
hopper("RGB").save(test_file)
|
|
||||||
Image.open(test_file)
|
|
||||||
|
|
||||||
hopper("I").save(test_file)
|
|
||||||
Image.open(test_file)
|
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
@ -298,30 +291,32 @@ class TestFilePng(PillowTestCase):
|
||||||
self.assert_image(im, "RGBA", (10, 10))
|
self.assert_image(im, "RGBA", (10, 10))
|
||||||
self.assertEqual(im.getcolors(), [(100, (0, 0, 0, 0))])
|
self.assertEqual(im.getcolors(), [(100, (0, 0, 0, 0))])
|
||||||
|
|
||||||
def test_save_l_transparency(self):
|
def test_save_greyscale_transparency(self):
|
||||||
# There are 559 transparent pixels in l_trns.png.
|
for mode, num_transparent in {
|
||||||
num_transparent = 559
|
"1": 1994,
|
||||||
|
"L": 559,
|
||||||
|
"I": 559,
|
||||||
|
}.items():
|
||||||
|
in_file = "Tests/images/"+mode.lower()+"_trns.png"
|
||||||
|
im = Image.open(in_file)
|
||||||
|
self.assertEqual(im.mode, mode)
|
||||||
|
self.assertEqual(im.info["transparency"], 255)
|
||||||
|
|
||||||
in_file = "Tests/images/l_trns.png"
|
im_rgba = im.convert('RGBA')
|
||||||
im = Image.open(in_file)
|
self.assertEqual(
|
||||||
self.assertEqual(im.mode, "L")
|
im_rgba.getchannel("A").getcolors()[0][0], num_transparent)
|
||||||
self.assertEqual(im.info["transparency"], 255)
|
|
||||||
|
|
||||||
im_rgba = im.convert('RGBA')
|
test_file = self.tempfile("temp.png")
|
||||||
self.assertEqual(
|
im.save(test_file)
|
||||||
im_rgba.getchannel("A").getcolors()[0][0], num_transparent)
|
|
||||||
|
|
||||||
test_file = self.tempfile("temp.png")
|
test_im = Image.open(test_file)
|
||||||
im.save(test_file)
|
self.assertEqual(test_im.mode, mode)
|
||||||
|
self.assertEqual(test_im.info["transparency"], 255)
|
||||||
|
self.assert_image_equal(im, test_im)
|
||||||
|
|
||||||
test_im = Image.open(test_file)
|
test_im_rgba = test_im.convert('RGBA')
|
||||||
self.assertEqual(test_im.mode, "L")
|
self.assertEqual(
|
||||||
self.assertEqual(test_im.info["transparency"], 255)
|
test_im_rgba.getchannel('A').getcolors()[0][0], num_transparent)
|
||||||
self.assert_image_equal(im, test_im)
|
|
||||||
|
|
||||||
test_im_rgba = test_im.convert('RGBA')
|
|
||||||
self.assertEqual(
|
|
||||||
test_im_rgba.getchannel('A').getcolors()[0][0], num_transparent)
|
|
||||||
|
|
||||||
def test_save_rgb_single_transparency(self):
|
def test_save_rgb_single_transparency(self):
|
||||||
in_file = "Tests/images/caption_6_33_22.png"
|
in_file = "Tests/images/caption_6_33_22.png"
|
||||||
|
@ -590,6 +585,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):
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from .helper import PillowTestCase
|
from .helper import PillowTestCase, hopper
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -34,6 +36,16 @@ class TestFilePpm(PillowTestCase):
|
||||||
reloaded = Image.open(f)
|
reloaded = Image.open(f)
|
||||||
self.assert_image_equal(im, reloaded)
|
self.assert_image_equal(im, reloaded)
|
||||||
|
|
||||||
|
def test_pnm(self):
|
||||||
|
im = Image.open('Tests/images/hopper.pnm')
|
||||||
|
self.assert_image_similar(im, hopper(), 0.0001)
|
||||||
|
|
||||||
|
f = self.tempfile('temp.pnm')
|
||||||
|
im.save(f)
|
||||||
|
|
||||||
|
reloaded = Image.open(f)
|
||||||
|
self.assert_image_equal(im, reloaded)
|
||||||
|
|
||||||
def test_truncated_file(self):
|
def test_truncated_file(self):
|
||||||
path = self.tempfile('temp.pgm')
|
path = self.tempfile('temp.pgm')
|
||||||
with open(path, 'w') as f:
|
with open(path, 'w') as f:
|
||||||
|
@ -49,3 +61,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")
|
||||||
|
|
|
@ -37,6 +37,8 @@ class TestFileTga(PillowTestCase):
|
||||||
path_no_ext, origin, "rle" if rle else "raw")
|
path_no_ext, origin, "rle" if rle else "raw")
|
||||||
|
|
||||||
original_im = Image.open(tga_path)
|
original_im = Image.open(tga_path)
|
||||||
|
self.assertEqual(original_im.format, "TGA")
|
||||||
|
self.assertEqual(original_im.get_format_mimetype(), "image/x-tga")
|
||||||
if rle:
|
if rle:
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
original_im.info["compression"], "tga_rle")
|
original_im.info["compression"], "tga_rle")
|
||||||
|
|
|
@ -532,6 +532,18 @@ class TestImage(PillowTestCase):
|
||||||
with Image.open(test_file) as im:
|
with Image.open(test_file) as im:
|
||||||
self.assert_warning(None, im.save, temp_file)
|
self.assert_warning(None, im.save, temp_file)
|
||||||
|
|
||||||
|
def test_load_on_nonexclusive_multiframe(self):
|
||||||
|
with open("Tests/images/frozenpond.mpo", "rb") as fp:
|
||||||
|
def act(fp):
|
||||||
|
im = Image.open(fp)
|
||||||
|
im.load()
|
||||||
|
act(fp)
|
||||||
|
|
||||||
|
with Image.open(fp) as im:
|
||||||
|
im.load()
|
||||||
|
|
||||||
|
self.assertFalse(fp.closed)
|
||||||
|
|
||||||
|
|
||||||
class MockEncoder(object):
|
class MockEncoder(object):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -28,3 +28,10 @@ class TestImageLoad(PillowTestCase):
|
||||||
os.fstat(fn)
|
os.fstat(fn)
|
||||||
|
|
||||||
self.assertRaises(OSError, os.fstat, fn)
|
self.assertRaises(OSError, os.fstat, fn)
|
||||||
|
|
||||||
|
def test_contextmanager_non_exclusive_fp(self):
|
||||||
|
with open("Tests/images/hopper.gif", "rb") as fp:
|
||||||
|
with Image.open(fp):
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.assertFalse(fp.closed)
|
||||||
|
|
|
@ -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 self.hopper:
|
||||||
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 self.hopper:
|
||||||
transpose(mode)
|
transpose(mode)
|
||||||
|
|
||||||
def test_rotate_90(self):
|
def test_rotate_90(self):
|
||||||
|
@ -57,7 +56,7 @@ class TestImageTranspose(PillowTestCase):
|
||||||
self.assertEqual(im.getpixel((1, y-2)), out.getpixel((y-2, x-2)))
|
self.assertEqual(im.getpixel((1, y-2)), out.getpixel((y-2, x-2)))
|
||||||
self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((y-2, 1)))
|
self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((y-2, 1)))
|
||||||
|
|
||||||
for mode in ("L", "RGB"):
|
for mode in self.hopper:
|
||||||
transpose(mode)
|
transpose(mode)
|
||||||
|
|
||||||
def test_rotate_180(self):
|
def test_rotate_180(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 self.hopper:
|
||||||
transpose(mode)
|
transpose(mode)
|
||||||
|
|
||||||
def test_rotate_270(self):
|
def test_rotate_270(self):
|
||||||
|
@ -89,7 +88,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((1, x-2)))
|
self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((1, x-2)))
|
||||||
|
|
||||||
for mode in ("L", "RGB"):
|
for mode in self.hopper:
|
||||||
transpose(mode)
|
transpose(mode)
|
||||||
|
|
||||||
def test_transpose(self):
|
def test_transpose(self):
|
||||||
|
@ -105,7 +104,7 @@ class TestImageTranspose(PillowTestCase):
|
||||||
self.assertEqual(im.getpixel((1, y-2)), out.getpixel((y-2, 1)))
|
self.assertEqual(im.getpixel((1, y-2)), out.getpixel((y-2, 1)))
|
||||||
self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((y-2, x-2)))
|
self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((y-2, x-2)))
|
||||||
|
|
||||||
for mode in ("L", "RGB"):
|
for mode in self.hopper:
|
||||||
transpose(mode)
|
transpose(mode)
|
||||||
|
|
||||||
def test_tranverse(self):
|
def test_tranverse(self):
|
||||||
|
@ -121,28 +120,29 @@ class TestImageTranspose(PillowTestCase):
|
||||||
self.assertEqual(im.getpixel((1, y-2)), out.getpixel((1, x-2)))
|
self.assertEqual(im.getpixel((1, y-2)), out.getpixel((1, x-2)))
|
||||||
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 self.hopper:
|
||||||
transpose(mode)
|
transpose(mode)
|
||||||
|
|
||||||
def test_roundtrip(self):
|
def test_roundtrip(self):
|
||||||
im = self.hopper['L']
|
for mode in self.hopper:
|
||||||
|
im = self.hopper[mode]
|
||||||
|
|
||||||
def transpose(first, second):
|
def transpose(first, second):
|
||||||
return im.transpose(first).transpose(second)
|
return im.transpose(first).transpose(second)
|
||||||
|
|
||||||
self.assert_image_equal(
|
self.assert_image_equal(
|
||||||
im, transpose(FLIP_LEFT_RIGHT, FLIP_LEFT_RIGHT))
|
im, transpose(FLIP_LEFT_RIGHT, FLIP_LEFT_RIGHT))
|
||||||
self.assert_image_equal(
|
self.assert_image_equal(
|
||||||
im, transpose(FLIP_TOP_BOTTOM, FLIP_TOP_BOTTOM))
|
im, transpose(FLIP_TOP_BOTTOM, FLIP_TOP_BOTTOM))
|
||||||
self.assert_image_equal(im, transpose(ROTATE_90, ROTATE_270))
|
self.assert_image_equal(im, transpose(ROTATE_90, ROTATE_270))
|
||||||
self.assert_image_equal(im, transpose(ROTATE_180, ROTATE_180))
|
self.assert_image_equal(im, transpose(ROTATE_180, ROTATE_180))
|
||||||
self.assert_image_equal(
|
self.assert_image_equal(
|
||||||
im.transpose(TRANSPOSE), transpose(ROTATE_90, FLIP_TOP_BOTTOM))
|
im.transpose(TRANSPOSE), transpose(ROTATE_90, FLIP_TOP_BOTTOM))
|
||||||
self.assert_image_equal(
|
self.assert_image_equal(
|
||||||
im.transpose(TRANSPOSE), transpose(ROTATE_270, FLIP_LEFT_RIGHT))
|
im.transpose(TRANSPOSE), transpose(ROTATE_270, FLIP_LEFT_RIGHT))
|
||||||
self.assert_image_equal(
|
self.assert_image_equal(
|
||||||
im.transpose(TRANSVERSE), transpose(ROTATE_90, FLIP_LEFT_RIGHT))
|
im.transpose(TRANSVERSE), transpose(ROTATE_90, FLIP_LEFT_RIGHT))
|
||||||
self.assert_image_equal(
|
self.assert_image_equal(
|
||||||
im.transpose(TRANSVERSE), transpose(ROTATE_270, FLIP_TOP_BOTTOM))
|
im.transpose(TRANSVERSE), transpose(ROTATE_270, FLIP_TOP_BOTTOM))
|
||||||
self.assert_image_equal(
|
self.assert_image_equal(
|
||||||
im.transpose(TRANSVERSE), transpose(ROTATE_180, TRANSPOSE))
|
im.transpose(TRANSVERSE), transpose(ROTATE_180, TRANSPOSE))
|
||||||
|
|
|
@ -6,6 +6,8 @@ from io import BytesIO
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import copy
|
import copy
|
||||||
|
import re
|
||||||
|
import distutils.version
|
||||||
|
|
||||||
FONT_PATH = "Tests/fonts/FreeMono.ttf"
|
FONT_PATH = "Tests/fonts/FreeMono.ttf"
|
||||||
FONT_SIZE = 20
|
FONT_SIZE = 20
|
||||||
|
@ -49,29 +51,40 @@ class TestImageFont(PillowTestCase):
|
||||||
# Freetype has different metrics depending on the version.
|
# Freetype has different metrics depending on the version.
|
||||||
# (and, other things, but first things first)
|
# (and, other things, but first things first)
|
||||||
METRICS = {
|
METRICS = {
|
||||||
('2', '3'): {'multiline': 30,
|
('>=2.3', '<2.4'): {
|
||||||
'textsize': 12,
|
'multiline': 30,
|
||||||
'getters': (13, 16)},
|
'textsize': 12,
|
||||||
('2', '7'): {'multiline': 6.2,
|
'getters': (13, 16)},
|
||||||
'textsize': 2.5,
|
('>=2.7',): {
|
||||||
'getters': (12, 16)},
|
'multiline': 6.2,
|
||||||
('2', '8'): {'multiline': 6.2,
|
'textsize': 2.5,
|
||||||
'textsize': 2.5,
|
'getters': (12, 16)},
|
||||||
'getters': (12, 16)},
|
'Default': {
|
||||||
('2', '9'): {'multiline': 6.2,
|
'multiline': 0.5,
|
||||||
'textsize': 2.5,
|
'textsize': 0.5,
|
||||||
'getters': (12, 16)},
|
'getters': (12, 16)},
|
||||||
'Default': {'multiline': 0.5,
|
|
||||||
'textsize': 0.5,
|
|
||||||
'getters': (12, 16)},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
freetype_version = tuple(
|
freetype = distutils.version.StrictVersion(ImageFont.core.freetype2_version)
|
||||||
ImageFont.core.freetype2_version.split('.')
|
|
||||||
)[:2]
|
self.metrics = self.METRICS['Default']
|
||||||
self.metrics = self.METRICS.get(freetype_version,
|
for conditions, metrics in self.METRICS.items():
|
||||||
self.METRICS['Default'])
|
if not isinstance(conditions, tuple):
|
||||||
|
continue
|
||||||
|
|
||||||
|
for condition in conditions:
|
||||||
|
version = re.sub('[<=>]', '', condition)
|
||||||
|
if (condition.startswith('>=') and freetype >= version) or \
|
||||||
|
(condition.startswith('<') and freetype < version):
|
||||||
|
# Condition was met
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Condition failed
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# All conditions were met
|
||||||
|
self.metrics = metrics
|
||||||
|
|
||||||
def get_font(self):
|
def get_font(self):
|
||||||
return ImageFont.truetype(FONT_PATH, FONT_SIZE,
|
return ImageFont.truetype(FONT_PATH, FONT_SIZE,
|
||||||
|
@ -525,6 +538,15 @@ class TestImageFont(PillowTestCase):
|
||||||
self.assertEqual(t.getsize_multiline('ABC\nA'), (36, 36))
|
self.assertEqual(t.getsize_multiline('ABC\nA'), (36, 36))
|
||||||
self.assertEqual(t.getsize_multiline('ABC\nAaaa'), (48, 36))
|
self.assertEqual(t.getsize_multiline('ABC\nAaaa'), (48, 36))
|
||||||
|
|
||||||
|
def test_complex_font_settings(self):
|
||||||
|
# Arrange
|
||||||
|
t = self.get_font()
|
||||||
|
# Act / Assert
|
||||||
|
if t.layout_engine == ImageFont.LAYOUT_BASIC:
|
||||||
|
self.assertRaises(KeyError, t.getmask, 'абвг', direction='rtl')
|
||||||
|
self.assertRaises(KeyError, t.getmask, 'абвг', features=['-kern'])
|
||||||
|
self.assertRaises(KeyError, t.getmask, 'абвг', language='sr')
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_RAQM, "Raqm not Available")
|
@unittest.skipUnless(HAS_RAQM, "Raqm not Available")
|
||||||
class TestImageFont_RaqmLayout(TestImageFont):
|
class TestImageFont_RaqmLayout(TestImageFont):
|
||||||
|
|
|
@ -130,3 +130,16 @@ class TestImagecomplextext(PillowTestCase):
|
||||||
target_img = Image.open(target)
|
target_img = Image.open(target)
|
||||||
|
|
||||||
self.assert_image_similar(im, target_img, .5)
|
self.assert_image_similar(im, target_img, .5)
|
||||||
|
|
||||||
|
def test_language(self):
|
||||||
|
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
|
||||||
|
|
||||||
|
im = Image.new(mode='RGB', size=(300, 100))
|
||||||
|
draw = ImageDraw.Draw(im)
|
||||||
|
draw.text((0, 0), 'абвг', font=ttf, fill=500,
|
||||||
|
language='sr')
|
||||||
|
|
||||||
|
target = 'Tests/images/test_language.png'
|
||||||
|
target_img = Image.open(target)
|
||||||
|
|
||||||
|
self.assert_image_similar(im, target_img, .5)
|
||||||
|
|
|
@ -115,7 +115,7 @@ class TestNumpy(PillowTestCase):
|
||||||
arr_back = numpy.array(img)
|
arr_back = numpy.array(img)
|
||||||
# numpy 1.8 and earlier return this as a boolean. (trusty/precise)
|
# numpy 1.8 and earlier return this as a boolean. (trusty/precise)
|
||||||
if arr_back.dtype == numpy.bool:
|
if arr_back.dtype == numpy.bool:
|
||||||
arr_bool = numpy.array([[1, 0, 0, 1, 0], [0, 1, 0, 0, 0]], 'bool')
|
arr_bool = numpy.array([[1, 0, 0, 1, 0], [0, 1, 0, 0, 0]], numpy.bool)
|
||||||
numpy.testing.assert_array_equal(arr_bool, arr_back)
|
numpy.testing.assert_array_equal(arr_bool, arr_back)
|
||||||
else:
|
else:
|
||||||
numpy.testing.assert_array_equal(arr, arr_back)
|
numpy.testing.assert_array_equal(arr, arr_back)
|
||||||
|
@ -143,21 +143,21 @@ class TestNumpy(PillowTestCase):
|
||||||
|
|
||||||
np_img = numpy.array(img)
|
np_img = numpy.array(img)
|
||||||
self._test_img_equals_nparray(img, np_img)
|
self._test_img_equals_nparray(img, np_img)
|
||||||
self.assertEqual(np_img.dtype, numpy.dtype(dtype))
|
self.assertEqual(np_img.dtype, dtype)
|
||||||
|
|
||||||
modes = [("L", 'uint8'),
|
modes = [("L", numpy.uint8),
|
||||||
("I", 'int32'),
|
("I", numpy.int32),
|
||||||
("F", 'float32'),
|
("F", numpy.float32),
|
||||||
("LA", 'uint8'),
|
("LA", numpy.uint8),
|
||||||
("RGB", 'uint8'),
|
("RGB", numpy.uint8),
|
||||||
("RGBA", 'uint8'),
|
("RGBA", numpy.uint8),
|
||||||
("RGBX", 'uint8'),
|
("RGBX", numpy.uint8),
|
||||||
("CMYK", 'uint8'),
|
("CMYK", numpy.uint8),
|
||||||
("YCbCr", 'uint8'),
|
("YCbCr", numpy.uint8),
|
||||||
("I;16", '<u2'),
|
("I;16", '<u2'),
|
||||||
("I;16B", '>u2'),
|
("I;16B", '>u2'),
|
||||||
("I;16L", '<u2'),
|
("I;16L", '<u2'),
|
||||||
("HSV", 'uint8'),
|
("HSV", numpy.uint8),
|
||||||
]
|
]
|
||||||
|
|
||||||
for mode in modes:
|
for mode in modes:
|
||||||
|
@ -167,7 +167,7 @@ class TestNumpy(PillowTestCase):
|
||||||
# see https://github.com/python-pillow/Pillow/issues/439
|
# see https://github.com/python-pillow/Pillow/issues/439
|
||||||
|
|
||||||
data = list(range(256))*3
|
data = list(range(256))*3
|
||||||
lut = numpy.array(data, dtype='uint8')
|
lut = numpy.array(data, dtype=numpy.uint8)
|
||||||
|
|
||||||
im = hopper()
|
im = hopper()
|
||||||
|
|
||||||
|
|
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,16 @@ 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
|
PyQt4 and PySide
|
||||||
~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -61,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.
|
||||||
|
|
||||||
ImageCms.CmsProfile attributes
|
ImageCms.CmsProfile attributes
|
||||||
|
@ -96,6 +105,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``)
|
||||||
|
@ -97,8 +105,8 @@ Reading sequences
|
||||||
~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
The GIF loader supports the :py:meth:`~file.seek` and :py:meth:`~file.tell`
|
The GIF loader supports the :py:meth:`~file.seek` and :py:meth:`~file.tell`
|
||||||
methods. You can seek to the next frame (``im.seek(im.tell() + 1)``), or rewind
|
methods. You can combine these methods to seek to the next frame
|
||||||
the file by seeking to the first frame. Random access is not supported.
|
(``im.seek(im.tell() + 1)``).
|
||||||
|
|
||||||
``im.seek()`` raises an ``EOFError`` if you try to seek after the last frame.
|
``im.seek()`` raises an ``EOFError`` if you try to seek after the last frame.
|
||||||
|
|
||||||
|
@ -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,26 +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``, ``LA``,
|
||||||
``RGB``, or ``RGBA`` data. Interlaced files are supported as of v1.1.7.
|
``I``, ``P``, ``RGB`` or ``RGBA`` data. Interlaced files are supported as of
|
||||||
|
v1.1.7.
|
||||||
|
|
||||||
|
As of Pillow 6.0, EXIF data can be read from PNG images. However, unlike other
|
||||||
|
image formats, EXIF data is not guaranteed to have been read until
|
||||||
|
:py:meth:`~PIL.Image.Image.load` has been called.
|
||||||
|
|
||||||
The :py:meth:`~PIL.Image.Image.open` method sets the following
|
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:
|
||||||
|
@ -477,12 +490,12 @@ The :py:meth:`~PIL.Image.Image.open` method sets the following
|
||||||
For ``P`` images: Either the palette index for full transparent pixels,
|
For ``P`` images: Either the palette index for full transparent pixels,
|
||||||
or a byte string with alpha values for each palette entry.
|
or a byte string with alpha values for each palette entry.
|
||||||
|
|
||||||
For ``L`` and ``RGB`` images, the color that represents full transparent
|
For ``1``, ``L``, ``I`` and ``RGB`` images, the color that represents
|
||||||
pixels in this image.
|
full transparent pixels in this image.
|
||||||
|
|
||||||
This key is omitted if the image is not a transparent palette image.
|
This key is omitted if the image is not a transparent palette image.
|
||||||
|
|
||||||
``Open`` also sets ``Image.text`` to a dictionary of the values of the
|
``open`` also sets ``Image.text`` to a dictionary of the values of the
|
||||||
``tEXt``, ``zTXt``, and ``iTXt`` chunks of the PNG image. Individual
|
``tEXt``, ``zTXt``, and ``iTXt`` chunks of the PNG image. Individual
|
||||||
compressed chunks are limited to a decompressed size of
|
compressed chunks are limited to a decompressed size of
|
||||||
``PngImagePlugin.MAX_TEXT_CHUNK``, by default 1MB, to prevent
|
``PngImagePlugin.MAX_TEXT_CHUNK``, by default 1MB, to prevent
|
||||||
|
@ -498,8 +511,8 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
|
||||||
encoder settings.
|
encoder settings.
|
||||||
|
|
||||||
**transparency**
|
**transparency**
|
||||||
For ``P``, ``L``, and ``RGB`` images, this option controls what
|
For ``P``, ``1``, ``L``, ``I``, and ``RGB`` images, this option controls
|
||||||
color image to mark as transparent.
|
what color from the image to mark as transparent.
|
||||||
|
|
||||||
For ``P`` images, this can be a either the palette index,
|
For ``P`` images, this can be a either the palette index,
|
||||||
or a byte string with alpha values for each palette entry.
|
or a byte string with alpha values for each palette entry.
|
||||||
|
@ -519,6 +532,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 +553,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, PPM and PNM files containing ``1``, ``L`` or
|
||||||
data.
|
``RGB`` data.
|
||||||
|
|
||||||
SGI
|
SGI
|
||||||
^^^
|
^^^
|
||||||
|
@ -547,10 +565,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 +605,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 +614,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 +752,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 +825,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 +859,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 +878,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 +914,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 +927,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 +959,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 +982,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 +994,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 +1009,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 +1095,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 +1105,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 +1115,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 +1125,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 +1139,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 +1147,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.
|
||||||
|
|
|
@ -396,10 +396,6 @@ Reading sequences
|
||||||
As seen in this example, you’ll get an :py:exc:`EOFError` exception when the
|
As seen in this example, you’ll get an :py:exc:`EOFError` exception when the
|
||||||
sequence ends.
|
sequence ends.
|
||||||
|
|
||||||
Note that most drivers in the current version of the library only allow you to
|
|
||||||
seek to the next frame (as in the above example). To rewind the file, you may
|
|
||||||
have to reopen it.
|
|
||||||
|
|
||||||
The following class lets you use the for-statement to loop over the sequence:
|
The following class lets you use the for-statement to loop over the sequence:
|
||||||
|
|
||||||
Using the ImageSequence Iterator class
|
Using the ImageSequence Iterator class
|
||||||
|
|
|
@ -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
|
||||||
------------------
|
------------------
|
||||||
|
|
|
@ -255,7 +255,7 @@ Methods
|
||||||
|
|
||||||
Draw a shape.
|
Draw a shape.
|
||||||
|
|
||||||
.. py:method:: PIL.ImageDraw.ImageDraw.text(xy, text, fill=None, font=None, anchor=None, spacing=0, align="left", direction=None, features=None)
|
.. py:method:: PIL.ImageDraw.ImageDraw.text(xy, text, fill=None, font=None, anchor=None, spacing=0, align="left", direction=None, features=None, language=None)
|
||||||
|
|
||||||
Draws the string at the given position.
|
Draws the string at the given position.
|
||||||
|
|
||||||
|
@ -287,7 +287,17 @@ Methods
|
||||||
|
|
||||||
.. versionadded:: 4.2.0
|
.. versionadded:: 4.2.0
|
||||||
|
|
||||||
.. py:method:: PIL.ImageDraw.ImageDraw.multiline_text(xy, text, fill=None, font=None, anchor=None, spacing=0, align="left", direction=None, features=None)
|
:param language: Language of the text. Different languages may use
|
||||||
|
different glyph shapes or ligatures. This parameter tells
|
||||||
|
the font which language the text is in, and to apply the
|
||||||
|
correct substitutions as appropriate, if available.
|
||||||
|
It should be a `BCP47 language code
|
||||||
|
<https://www.w3.org/International/articles/language-tags/>`
|
||||||
|
Requires libraqm.
|
||||||
|
|
||||||
|
.. versionadded:: 6.0.0
|
||||||
|
|
||||||
|
.. py:method:: PIL.ImageDraw.ImageDraw.multiline_text(xy, text, fill=None, font=None, anchor=None, spacing=0, align="left", direction=None, features=None, language=None)
|
||||||
|
|
||||||
Draws the string at the given position.
|
Draws the string at the given position.
|
||||||
|
|
||||||
|
@ -316,7 +326,17 @@ Methods
|
||||||
|
|
||||||
.. versionadded:: 4.2.0
|
.. versionadded:: 4.2.0
|
||||||
|
|
||||||
.. py:method:: PIL.ImageDraw.ImageDraw.textsize(text, font=None, spacing=4, direction=None, features=None)
|
:param language: Language of the text. Different languages may use
|
||||||
|
different glyph shapes or ligatures. This parameter tells
|
||||||
|
the font which language the text is in, and to apply the
|
||||||
|
correct substitutions as appropriate, if available.
|
||||||
|
It should be a `BCP47 language code
|
||||||
|
<https://www.w3.org/International/articles/language-tags/>`
|
||||||
|
Requires libraqm.
|
||||||
|
|
||||||
|
.. versionadded:: 6.0.0
|
||||||
|
|
||||||
|
.. py:method:: PIL.ImageDraw.ImageDraw.textsize(text, font=None, spacing=4, direction=None, features=None, language=None)
|
||||||
|
|
||||||
Return the size of the given string, in pixels.
|
Return the size of the given string, in pixels.
|
||||||
|
|
||||||
|
@ -330,7 +350,6 @@ Methods
|
||||||
Requires libraqm.
|
Requires libraqm.
|
||||||
|
|
||||||
.. versionadded:: 4.2.0
|
.. versionadded:: 4.2.0
|
||||||
|
|
||||||
:param features: A list of OpenType font features to be used during text
|
:param features: A list of OpenType font features to be used during text
|
||||||
layout. This is usually used to turn on optional
|
layout. This is usually used to turn on optional
|
||||||
font features that are not enabled by default,
|
font features that are not enabled by default,
|
||||||
|
@ -343,8 +362,17 @@ Methods
|
||||||
Requires libraqm.
|
Requires libraqm.
|
||||||
|
|
||||||
.. versionadded:: 4.2.0
|
.. versionadded:: 4.2.0
|
||||||
|
:param language: Language of the text. Different languages may use
|
||||||
|
different glyph shapes or ligatures. This parameter tells
|
||||||
|
the font which language the text is in, and to apply the
|
||||||
|
correct substitutions as appropriate, if available.
|
||||||
|
It should be a `BCP47 language code
|
||||||
|
<https://www.w3.org/International/articles/language-tags/>`
|
||||||
|
Requires libraqm.
|
||||||
|
|
||||||
.. py:method:: PIL.ImageDraw.ImageDraw.multiline_textsize(text, font=None, spacing=4, direction=None, features=None)
|
.. versionadded:: 6.0.0
|
||||||
|
|
||||||
|
.. py:method:: PIL.ImageDraw.ImageDraw.multiline_textsize(text, font=None, spacing=4, direction=None, features=None, language=None)
|
||||||
|
|
||||||
Return the size of the given string, in pixels.
|
Return the size of the given string, in pixels.
|
||||||
|
|
||||||
|
@ -370,6 +398,16 @@ Methods
|
||||||
|
|
||||||
.. versionadded:: 4.2.0
|
.. versionadded:: 4.2.0
|
||||||
|
|
||||||
|
:param language: Language of the text. Different languages may use
|
||||||
|
different glyph shapes or ligatures. This parameter tells
|
||||||
|
the font which language the text is in, and to apply the
|
||||||
|
correct substitutions as appropriate, if available.
|
||||||
|
It should be a `BCP47 language code
|
||||||
|
<https://www.w3.org/International/articles/language-tags/>`
|
||||||
|
Requires libraqm.
|
||||||
|
|
||||||
|
.. versionadded:: 6.0.0
|
||||||
|
|
||||||
.. py:method:: PIL.ImageDraw.getdraw(im=None, hints=None)
|
.. py:method:: PIL.ImageDraw.getdraw(im=None, hints=None)
|
||||||
|
|
||||||
.. warning:: This method is experimental.
|
.. warning:: This method is experimental.
|
||||||
|
|
|
@ -47,11 +47,45 @@ Functions
|
||||||
Methods
|
Methods
|
||||||
-------
|
-------
|
||||||
|
|
||||||
.. py:method:: PIL.ImageFont.ImageFont.getsize(text)
|
.. py:method:: PIL.ImageFont.ImageFont.getsize(text, direction=None, features=[], language=None)
|
||||||
|
|
||||||
|
Returns width and height (in pixels) of given text if rendered in font with
|
||||||
|
provided direction, features, and language.
|
||||||
|
|
||||||
|
:param text: Text to measure.
|
||||||
|
|
||||||
|
:param direction: Direction of the text. It can be 'rtl' (right to
|
||||||
|
left), 'ltr' (left to right) or 'ttb' (top to bottom).
|
||||||
|
Requires libraqm.
|
||||||
|
|
||||||
|
.. versionadded:: 4.2.0
|
||||||
|
|
||||||
|
:param features: A list of OpenType font features to be used during text
|
||||||
|
layout. This is usually used to turn on optional
|
||||||
|
font features that are not enabled by default,
|
||||||
|
for example 'dlig' or 'ss01', but can be also
|
||||||
|
used to turn off default font features for
|
||||||
|
example '-liga' to disable ligatures or '-kern'
|
||||||
|
to disable kerning. To get all supported
|
||||||
|
features, see
|
||||||
|
https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist
|
||||||
|
Requires libraqm.
|
||||||
|
|
||||||
|
.. versionadded:: 4.2.0
|
||||||
|
|
||||||
|
:param language: Language of the text. Different languages may use
|
||||||
|
different glyph shapes or ligatures. This parameter tells
|
||||||
|
the font which language the text is in, and to apply the
|
||||||
|
correct substitutions as appropriate, if available.
|
||||||
|
It should be a `BCP47 language code
|
||||||
|
<https://www.w3.org/International/articles/language-tags/>`
|
||||||
|
Requires libraqm.
|
||||||
|
|
||||||
|
.. versionadded:: 6.0.0
|
||||||
|
|
||||||
:return: (width, height)
|
:return: (width, height)
|
||||||
|
|
||||||
.. py:method:: PIL.ImageFont.ImageFont.getmask(text, mode='', direction=None, features=[])
|
.. py:method:: PIL.ImageFont.ImageFont.getmask(text, mode='', direction=None, features=[], language=None)
|
||||||
|
|
||||||
Create a bitmap for the text.
|
Create a bitmap for the text.
|
||||||
|
|
||||||
|
@ -85,5 +119,15 @@ Methods
|
||||||
|
|
||||||
.. versionadded:: 4.2.0
|
.. versionadded:: 4.2.0
|
||||||
|
|
||||||
|
:param language: Language of the text. Different languages may use
|
||||||
|
different glyph shapes or ligatures. This parameter tells
|
||||||
|
the font which language the text is in, and to apply the
|
||||||
|
correct substitutions as appropriate, if available.
|
||||||
|
It should be a `BCP47 language code
|
||||||
|
<https://www.w3.org/International/articles/language-tags/>`
|
||||||
|
Requires libraqm.
|
||||||
|
|
||||||
|
.. versionadded:: 6.0.0
|
||||||
|
|
||||||
:return: An internal PIL storage memory instance as defined by the
|
:return: An internal PIL storage memory instance as defined by the
|
||||||
:py:mod:`PIL.Image.core` interface module.
|
:py:mod:`PIL.Image.core` interface module.
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
File Handling in Pillow
|
File Handling in Pillow
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
When opening a file as an image, Pillow requires a filename,
|
When opening a file as an image, Pillow requires a filename, ``pathlib.Path``
|
||||||
pathlib.Path object, or a file-like object. Pillow uses the filename
|
object, or a file-like object. Pillow uses the filename or ``Path`` to open a
|
||||||
or Path to open a file, so for the rest of this article, they will all
|
file, so for the rest of this article, they will all be treated as a file-like
|
||||||
be treated as a file-like object.
|
object.
|
||||||
|
|
||||||
The first four of these items are equivalent, the last is dangerous
|
The first four of these items are equivalent, the last is dangerous
|
||||||
and may fail::
|
and may fail::
|
||||||
|
@ -48,24 +48,23 @@ Issues
|
||||||
Image Lifecycle
|
Image Lifecycle
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
* ``Image.open()`` Path-like objects are opened as a file. Metadata is read
|
* ``Image.open()`` Filenames and ``Path`` objects are opened as a file.
|
||||||
from the open file. The file is left open for further usage.
|
Metadata is read from the open file. The file is left open for further usage.
|
||||||
|
|
||||||
* ``Image.Image.load()`` When the pixel data from the image is
|
* ``Image.Image.load()`` When the pixel data from the image is
|
||||||
required, ``load()`` is called. The current frame is read into
|
required, ``load()`` is called. The current frame is read into
|
||||||
memory. The image can now be used independently of the underlying
|
memory. The image can now be used independently of the underlying
|
||||||
image file.
|
image file.
|
||||||
|
|
||||||
If a filename or a path-like object was passed to ``Image.open()``, then
|
If a filename or a ``Path`` object was passed to ``Image.open()``, then the
|
||||||
the file object was opened by Pillow and is considered to be used exclusively
|
file object was opened by Pillow and is considered to be used exclusively by
|
||||||
by Pillow. So if the image is a single-frame image, the file will
|
Pillow. So if the image is a single-frame image, the file will be closed in
|
||||||
be closed in this method after the frame is read. If the image is a
|
this method after the frame is read. If the image is a multi-frame image,
|
||||||
multi-frame image, (e.g. multipage TIFF and animated GIF) the image file is
|
(e.g. multipage TIFF and animated GIF) the image file is left open so that
|
||||||
left open so that ``Image.Image.seek()`` can load the appropriate frame.
|
``Image.Image.seek()`` can load the appropriate frame.
|
||||||
|
|
||||||
* ``Image.Image.close()`` Closes the file pointer and destroys the
|
* ``Image.Image.close()`` Closes the file and destroys the core image object.
|
||||||
core image object. This is used in the Pillow context manager
|
This is used in the Pillow context manager support. e.g.::
|
||||||
support. e.g.::
|
|
||||||
|
|
||||||
with Image.open('test.jpg') as img:
|
with Image.open('test.jpg') as img:
|
||||||
... # image operations here.
|
... # image operations here.
|
||||||
|
@ -84,17 +83,9 @@ data until the caller has explicitly closed the image.
|
||||||
Complications
|
Complications
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
* TiffImagePlugin has some code to pass the underlying file descriptor
|
* ``TiffImagePlugin`` has some code to pass the underlying file descriptor into
|
||||||
into libtiff (if working on an actual file). Since libtiff closes
|
libtiff (if working on an actual file). Since libtiff closes the file
|
||||||
the file descriptor internally, it is duplicated prior to passing it
|
descriptor internally, it is duplicated prior to passing it into libtiff.
|
||||||
into libtiff.
|
|
||||||
|
|
||||||
* ``decoder.handles_eof`` This slightly misnamed flag indicates that
|
|
||||||
the decoder wants to be called with a 0 length buffer when reads are
|
|
||||||
done. Despite the comments in ``ImageFile.load()``, the only decoder
|
|
||||||
that actually uses this flag is the Jpeg2K decoder. The use of this
|
|
||||||
flag in Jpeg2K predated the change to the decoder that added the
|
|
||||||
pulls_fd flag, and is therefore not used.
|
|
||||||
|
|
||||||
* I don't think that there's any way to make this safe without
|
* I don't think that there's any way to make this safe without
|
||||||
changing the lazy loading::
|
changing the lazy loading::
|
||||||
|
|
|
@ -26,12 +26,26 @@ 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
|
PyQt4 and PySide
|
||||||
~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -103,18 +117,76 @@ Deprecated Use instead
|
||||||
``product_model`` Unicode ``model``
|
``product_model`` Unicode ``model``
|
||||||
======================== ===============================
|
======================== ===============================
|
||||||
|
|
||||||
|
MIME type improvements
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Previously, all JPEG2000 images had the MIME type "image/jpx". This has now been
|
||||||
|
corrected. After the file format drivers have been loaded, ``Image.MIME["JPEG2000"]``
|
||||||
|
will return "image/jp2". ``ImageFile.get_format_mimetype`` will return "image/jpx" if
|
||||||
|
a JPX profile is present, or "image/jp2" otherwise.
|
||||||
|
|
||||||
|
Previously, all SGI images had the MIME type "image/rgb". This has now been
|
||||||
|
corrected. After the file format drivers have been loaded, ``Image.MIME["SGI"]``
|
||||||
|
will return "image/sgi". ``ImageFile.get_format_mimetype`` will return "image/rgb" if
|
||||||
|
RGB image data is present, or "image/sgi" otherwise.
|
||||||
|
|
||||||
|
MIME types have been added to the PPM format. After the file format drivers have been
|
||||||
|
loaded, ``Image.MIME["PPM"]`` will now return the generic "image/x-portable-anymap".
|
||||||
|
``ImageFile.get_format_mimetype`` will return a MIME type specific to the color type.
|
||||||
|
|
||||||
|
The TGA, PCX and ICO formats also now have MIME types: "image/x-tga", "image/x-pcx" and
|
||||||
|
"image/x-icon" respectively.
|
||||||
|
|
||||||
API Additions
|
API Additions
|
||||||
=============
|
=============
|
||||||
|
|
||||||
TODO
|
DIB file format
|
||||||
^^^^
|
^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
TODO
|
Pillow now supports reading and writing the 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``.
|
||||||
|
|
||||||
|
New language parameter
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
These text-rendering functions now accept a ``language`` parameter to request
|
||||||
|
language-specific glyphs and ligatures from the font:
|
||||||
|
|
||||||
|
* ``ImageDraw.ImageDraw.multiline_text()``
|
||||||
|
* ``ImageDraw.ImageDraw.multiline_textsize()``
|
||||||
|
* ``ImageDraw.ImageDraw.text()``
|
||||||
|
* ``ImageDraw.ImageDraw.textsize()``
|
||||||
|
* ``ImageFont.ImageFont.getmask()``
|
||||||
|
* ``ImageFont.ImageFont.getsize_multiline()``
|
||||||
|
* ``ImageFont.ImageFont.getsize()``
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Reading TIFF with old-style JPEG compression
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Added support reading TIFF files with old-style JPEG compression through LibTIFF. All
|
||||||
|
YCbCr TIFF images are now always read as RGB.
|
||||||
|
|
||||||
|
TIFF compression codecs
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Support has been added for the LZMA, Zstd and WebP TIFF compression codecs.
|
||||||
|
|
|
@ -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.
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
@ -101,53 +105,52 @@ class BmpImageFile(ImageFile.ImageFile):
|
||||||
# --------------------------------------------- Windows Bitmap v2 to v5
|
# --------------------------------------------- Windows Bitmap v2 to v5
|
||||||
# v3, OS/2 v2, v4, v5
|
# v3, OS/2 v2, v4, v5
|
||||||
elif file_info['header_size'] in (40, 64, 108, 124):
|
elif file_info['header_size'] in (40, 64, 108, 124):
|
||||||
if file_info['header_size'] >= 40: # v3 and OS/2
|
file_info['y_flip'] = i8(header_data[7]) == 0xff
|
||||||
file_info['y_flip'] = i8(header_data[7]) == 0xff
|
file_info['direction'] = 1 if file_info['y_flip'] else -1
|
||||||
file_info['direction'] = 1 if file_info['y_flip'] else -1
|
file_info['width'] = i32(header_data[0:4])
|
||||||
file_info['width'] = i32(header_data[0:4])
|
file_info['height'] = (i32(header_data[4:8])
|
||||||
file_info['height'] = (i32(header_data[4:8])
|
if not file_info['y_flip']
|
||||||
if not file_info['y_flip']
|
else 2**32 - i32(header_data[4:8]))
|
||||||
else 2**32 - i32(header_data[4:8]))
|
file_info['planes'] = i16(header_data[8:10])
|
||||||
file_info['planes'] = i16(header_data[8:10])
|
file_info['bits'] = i16(header_data[10:12])
|
||||||
file_info['bits'] = i16(header_data[10:12])
|
file_info['compression'] = i32(header_data[12:16])
|
||||||
file_info['compression'] = i32(header_data[12:16])
|
# byte size of pixel data
|
||||||
# byte size of pixel data
|
file_info['data_size'] = i32(header_data[16:20])
|
||||||
file_info['data_size'] = i32(header_data[16:20])
|
file_info['pixels_per_meter'] = (i32(header_data[20:24]),
|
||||||
file_info['pixels_per_meter'] = (i32(header_data[20:24]),
|
i32(header_data[24:28]))
|
||||||
i32(header_data[24:28]))
|
file_info['colors'] = i32(header_data[28:32])
|
||||||
file_info['colors'] = i32(header_data[28:32])
|
file_info['palette_padding'] = 4
|
||||||
file_info['palette_padding'] = 4
|
self.info["dpi"] = tuple(
|
||||||
self.info["dpi"] = tuple(
|
map(lambda x: int(math.ceil(x / 39.3701)),
|
||||||
map(lambda x: int(math.ceil(x / 39.3701)),
|
file_info['pixels_per_meter']))
|
||||||
file_info['pixels_per_meter']))
|
if file_info['compression'] == self.BITFIELDS:
|
||||||
if file_info['compression'] == self.BITFIELDS:
|
if len(header_data) >= 52:
|
||||||
if len(header_data) >= 52:
|
for idx, mask in enumerate(['r_mask',
|
||||||
for idx, mask in enumerate(['r_mask',
|
'g_mask',
|
||||||
'g_mask',
|
'b_mask',
|
||||||
'b_mask',
|
'a_mask']):
|
||||||
'a_mask']):
|
file_info[mask] = i32(
|
||||||
file_info[mask] = i32(
|
header_data[36 + idx * 4:40 + idx * 4]
|
||||||
header_data[36 + idx * 4:40 + idx * 4]
|
)
|
||||||
)
|
else:
|
||||||
else:
|
# 40 byte headers only have the three components in the
|
||||||
# 40 byte headers only have the three components in the
|
# bitfields masks, ref:
|
||||||
# bitfields masks, ref:
|
# https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
|
||||||
# https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
|
# See also
|
||||||
# See also
|
# https://github.com/python-pillow/Pillow/issues/1293
|
||||||
# https://github.com/python-pillow/Pillow/issues/1293
|
# There is a 4th component in the RGBQuad, in the alpha
|
||||||
# There is a 4th component in the RGBQuad, in the alpha
|
# location, but it is listed as a reserved component,
|
||||||
# location, but it is listed as a reserved component,
|
# and it is not generally an alpha channel
|
||||||
# and it is not generally an alpha channel
|
file_info['a_mask'] = 0x0
|
||||||
file_info['a_mask'] = 0x0
|
for mask in ['r_mask', 'g_mask', 'b_mask']:
|
||||||
for mask in ['r_mask', 'g_mask', 'b_mask']:
|
file_info[mask] = i32(read(4))
|
||||||
file_info[mask] = i32(read(4))
|
file_info['rgb_mask'] = (file_info['r_mask'],
|
||||||
file_info['rgb_mask'] = (file_info['r_mask'],
|
file_info['g_mask'],
|
||||||
file_info['g_mask'],
|
file_info['b_mask'])
|
||||||
file_info['b_mask'])
|
file_info['rgba_mask'] = (file_info['r_mask'],
|
||||||
file_info['rgba_mask'] = (file_info['r_mask'],
|
file_info['g_mask'],
|
||||||
file_info['g_mask'],
|
file_info['b_mask'],
|
||||||
file_info['b_mask'],
|
file_info['a_mask'])
|
||||||
file_info['a_mask'])
|
|
||||||
else:
|
else:
|
||||||
raise IOError("Unsupported BMP header type (%d)" %
|
raise IOError("Unsupported BMP header type (%d)" %
|
||||||
file_info['header_size'])
|
file_info['header_size'])
|
||||||
|
@ -176,6 +179,7 @@ class BmpImageFile(ImageFile.ImageFile):
|
||||||
SUPPORTED = {
|
SUPPORTED = {
|
||||||
32: [(0xff0000, 0xff00, 0xff, 0x0),
|
32: [(0xff0000, 0xff00, 0xff, 0x0),
|
||||||
(0xff0000, 0xff00, 0xff, 0xff000000),
|
(0xff0000, 0xff00, 0xff, 0xff000000),
|
||||||
|
(0xff, 0xff00, 0xff0000, 0xff000000),
|
||||||
(0x0, 0x0, 0x0, 0x0),
|
(0x0, 0x0, 0x0, 0x0),
|
||||||
(0xff000000, 0xff0000, 0xff00, 0x0)],
|
(0xff000000, 0xff0000, 0xff00, 0x0)],
|
||||||
24: [(0xff0000, 0xff00, 0xff)],
|
24: [(0xff0000, 0xff00, 0xff)],
|
||||||
|
@ -184,6 +188,7 @@ class BmpImageFile(ImageFile.ImageFile):
|
||||||
MASK_MODES = {
|
MASK_MODES = {
|
||||||
(32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX",
|
(32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX",
|
||||||
(32, (0xff000000, 0xff0000, 0xff00, 0x0)): "XBGR",
|
(32, (0xff000000, 0xff0000, 0xff00, 0x0)): "XBGR",
|
||||||
|
(32, (0xff, 0xff00, 0xff0000, 0xff000000)): "RGBA",
|
||||||
(32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA",
|
(32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA",
|
||||||
(32, (0x0, 0x0, 0x0, 0x0)): "BGRA",
|
(32, (0x0, 0x0, 0x0, 0x0)): "BGRA",
|
||||||
(24, (0xff0000, 0xff00, 0xff)): "BGR",
|
(24, (0xff0000, 0xff00, 0xff)): "BGR",
|
||||||
|
@ -196,7 +201,7 @@ class BmpImageFile(ImageFile.ImageFile):
|
||||||
raw_mode = MASK_MODES[
|
raw_mode = MASK_MODES[
|
||||||
(file_info["bits"], file_info["rgba_mask"])
|
(file_info["bits"], file_info["rgba_mask"])
|
||||||
]
|
]
|
||||||
self.mode = "RGBA" if raw_mode in ("BGRA",) else self.mode
|
self.mode = "RGBA" if "A" in raw_mode else self.mode
|
||||||
elif (file_info['bits'] in (24, 16) and
|
elif (file_info['bits'] in (24, 16) and
|
||||||
file_info['rgb_mask'] in SUPPORTED[file_info['bits']]):
|
file_info['rgb_mask'] in SUPPORTED[file_info['bits']]):
|
||||||
raw_mode = MASK_MODES[
|
raw_mode = MASK_MODES[
|
||||||
|
@ -291,7 +296,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,14 +315,15 @@ 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
|
||||||
fp.write(b"BM" + # file type (magic)
|
if bitmap_header:
|
||||||
o32(offset+image) + # file size
|
offset = 14 + header + colors * 4
|
||||||
o32(0) + # reserved
|
fp.write(b"BM" + # file type (magic)
|
||||||
o32(offset)) # image data offset
|
o32(offset+image) + # file size
|
||||||
|
o32(0) + # reserved
|
||||||
|
o32(offset)) # image data offset
|
||||||
|
|
||||||
# bitmap info header
|
# bitmap info header
|
||||||
fp.write(o32(header) + # info header size
|
fp.write(o32(header) + # info header size
|
||||||
|
@ -352,3 +362,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,43 +123,52 @@ 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]
|
||||||
|
|
||||||
data_start = header_size + 4
|
self.tile = [("raw", (0, 0) + self.size, 0, (rawmode, 0, 1))]
|
||||||
n = 0
|
|
||||||
if fourcc == b"DXT1":
|
|
||||||
self.pixel_format = "DXT1"
|
|
||||||
n = 1
|
|
||||||
elif fourcc == b"DXT3":
|
|
||||||
self.pixel_format = "DXT3"
|
|
||||||
n = 2
|
|
||||||
elif fourcc == b"DXT5":
|
|
||||||
self.pixel_format = "DXT5"
|
|
||||||
n = 3
|
|
||||||
elif fourcc == b"DX10":
|
|
||||||
data_start += 20
|
|
||||||
# ignoring flags which pertain to volume textures and cubemaps
|
|
||||||
dxt10 = BytesIO(self.fp.read(20))
|
|
||||||
dxgi_format, dimension = struct.unpack("<II", dxt10.read(8))
|
|
||||||
if dxgi_format in (DXGI_FORMAT_BC7_TYPELESS,
|
|
||||||
DXGI_FORMAT_BC7_UNORM):
|
|
||||||
self.pixel_format = "BC7"
|
|
||||||
n = 7
|
|
||||||
elif dxgi_format == DXGI_FORMAT_BC7_UNORM_SRGB:
|
|
||||||
self.pixel_format = "BC7"
|
|
||||||
self.im_info["gamma"] = 1/2.2
|
|
||||||
n = 7
|
|
||||||
else:
|
|
||||||
raise NotImplementedError("Unimplemented DXGI format %d" %
|
|
||||||
(dxgi_format))
|
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError("Unimplemented pixel format %r" %
|
data_start = header_size + 4
|
||||||
(fourcc))
|
n = 0
|
||||||
|
if fourcc == b"DXT1":
|
||||||
|
self.pixel_format = "DXT1"
|
||||||
|
n = 1
|
||||||
|
elif fourcc == b"DXT3":
|
||||||
|
self.pixel_format = "DXT3"
|
||||||
|
n = 2
|
||||||
|
elif fourcc == b"DXT5":
|
||||||
|
self.pixel_format = "DXT5"
|
||||||
|
n = 3
|
||||||
|
elif fourcc == b"DX10":
|
||||||
|
data_start += 20
|
||||||
|
# ignoring flags which pertain to volume textures and cubemaps
|
||||||
|
dxt10 = BytesIO(self.fp.read(20))
|
||||||
|
dxgi_format, dimension = struct.unpack("<II", dxt10.read(8))
|
||||||
|
if dxgi_format in (DXGI_FORMAT_BC7_TYPELESS,
|
||||||
|
DXGI_FORMAT_BC7_UNORM):
|
||||||
|
self.pixel_format = "BC7"
|
||||||
|
n = 7
|
||||||
|
elif dxgi_format == DXGI_FORMAT_BC7_UNORM_SRGB:
|
||||||
|
self.pixel_format = "BC7"
|
||||||
|
self.im_info["gamma"] = 1/2.2
|
||||||
|
n = 7
|
||||||
|
else:
|
||||||
|
raise NotImplementedError("Unimplemented DXGI format %d" %
|
||||||
|
(dxgi_format))
|
||||||
|
else:
|
||||||
|
raise NotImplementedError("Unimplemented pixel format %r" %
|
||||||
|
(fourcc))
|
||||||
|
|
||||||
self.tile = [
|
self.tile = [
|
||||||
("bcn", (0, 0) + self.size, data_start, (n))
|
("bcn", (0, 0) + self.size, data_start, (n))
|
||||||
]
|
]
|
||||||
|
|
||||||
def load_seek(self, pos):
|
def load_seek(self, pos):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -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
|
||||||
|
@ -141,13 +141,11 @@ def Ghostscript(tile, size, fp, scale=1):
|
||||||
|
|
||||||
# push data through Ghostscript
|
# push data through Ghostscript
|
||||||
try:
|
try:
|
||||||
with open(os.devnull, 'w+b') as devnull:
|
startupinfo = None
|
||||||
startupinfo = None
|
if sys.platform.startswith('win'):
|
||||||
if sys.platform.startswith('win'):
|
startupinfo = subprocess.STARTUPINFO()
|
||||||
startupinfo = subprocess.STARTUPINFO()
|
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||||
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
subprocess.check_call(command, startupinfo=startupinfo)
|
||||||
subprocess.check_call(command, stdout=devnull,
|
|
||||||
startupinfo=startupinfo)
|
|
||||||
im = Image.open(outfile)
|
im = Image.open(outfile)
|
||||||
im.load()
|
im.load()
|
||||||
finally:
|
finally:
|
||||||
|
@ -169,7 +167,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 +310,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:
|
||||||
|
|
|
@ -122,6 +122,8 @@ class GifImageFile(ImageFile.ImageFile):
|
||||||
if not self._seek_check(frame):
|
if not self._seek_check(frame):
|
||||||
return
|
return
|
||||||
if frame < self.__frame:
|
if frame < self.__frame:
|
||||||
|
if frame != 0:
|
||||||
|
self.im = None
|
||||||
self._seek(0)
|
self._seek(0)
|
||||||
|
|
||||||
last_frame = self.__frame
|
last_frame = self.__frame
|
||||||
|
@ -604,16 +606,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 +634,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):
|
||||||
|
|
|
@ -295,3 +295,5 @@ class IcoImageFile(ImageFile.ImageFile):
|
||||||
Image.register_open(IcoImageFile.format, IcoImageFile, _accept)
|
Image.register_open(IcoImageFile.format, IcoImageFile, _accept)
|
||||||
Image.register_save(IcoImageFile.format, _save)
|
Image.register_save(IcoImageFile.format, _save)
|
||||||
Image.register_extension(IcoImageFile.format, ".ico")
|
Image.register_extension(IcoImageFile.format, ".ico")
|
||||||
|
|
||||||
|
Image.register_mime(IcoImageFile.format, "image/x-icon")
|
||||||
|
|
|
@ -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__)
|
||||||
|
@ -579,7 +578,12 @@ class Image(object):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __exit__(self, *args):
|
def __exit__(self, *args):
|
||||||
self.close()
|
if hasattr(self, 'fp') and getattr(self, '_exclusive_fp', False):
|
||||||
|
if hasattr(self, "_close__fp"):
|
||||||
|
self._close__fp()
|
||||||
|
if self.fp:
|
||||||
|
self.fp.close()
|
||||||
|
self.fp = None
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
"""
|
"""
|
||||||
|
@ -611,12 +615,7 @@ class Image(object):
|
||||||
|
|
||||||
if sys.version_info.major >= 3:
|
if sys.version_info.major >= 3:
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
if hasattr(self, "_close__fp"):
|
self.__exit__()
|
||||||
self._close__fp()
|
|
||||||
if (hasattr(self, 'fp') and hasattr(self, '_exclusive_fp')
|
|
||||||
and self.fp and self._exclusive_fp):
|
|
||||||
self.fp.close()
|
|
||||||
self.fp = None
|
|
||||||
|
|
||||||
def _copy(self):
|
def _copy(self):
|
||||||
self.load()
|
self.load()
|
||||||
|
@ -680,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()
|
||||||
|
|
||||||
|
@ -952,7 +950,7 @@ class Image(object):
|
||||||
delete_trns = False
|
delete_trns = False
|
||||||
# transparency handling
|
# transparency handling
|
||||||
if has_transparency:
|
if has_transparency:
|
||||||
if self.mode in ('L', 'RGB') and mode == 'RGBA':
|
if self.mode in ('1', 'L', 'I', 'RGB') and mode == 'RGBA':
|
||||||
# Use transparent conversion to promote from transparent
|
# Use transparent conversion to promote from transparent
|
||||||
# color to an alpha channel.
|
# color to an alpha channel.
|
||||||
new_im = self._new(self.im.convert_transparent(
|
new_im = self._new(self.im.convert_transparent(
|
||||||
|
@ -1051,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.
|
||||||
|
@ -1064,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
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -1091,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))
|
||||||
|
@ -2003,9 +2005,6 @@ class Image(object):
|
||||||
**EOFError** exception. When a sequence file is opened, the
|
**EOFError** exception. When a sequence file is opened, the
|
||||||
library automatically seeks to frame 0.
|
library automatically seeks to frame 0.
|
||||||
|
|
||||||
Note that in the current version of the library, most sequence
|
|
||||||
formats only allows you to seek to the next frame.
|
|
||||||
|
|
||||||
See :py:meth:`~PIL.Image.Image.tell`.
|
See :py:meth:`~PIL.Image.Image.tell`.
|
||||||
|
|
||||||
:param frame: Frame number, starting at 0.
|
:param frame: Frame number, starting at 0.
|
||||||
|
@ -2023,10 +2022,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
|
||||||
|
@ -2128,11 +2127,12 @@ class Image(object):
|
||||||
|
|
||||||
self.draft(None, size)
|
self.draft(None, size)
|
||||||
|
|
||||||
im = self.resize(size, resample)
|
if self.size != size:
|
||||||
|
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__
|
||||||
)
|
)
|
||||||
|
|
|
@ -282,13 +282,17 @@ class ImageDraw(object):
|
||||||
self.draw.draw_bitmap(xy, mask, ink)
|
self.draw.draw_bitmap(xy, mask, ink)
|
||||||
|
|
||||||
def multiline_text(self, xy, text, fill=None, font=None, anchor=None,
|
def multiline_text(self, xy, text, fill=None, font=None, anchor=None,
|
||||||
spacing=4, align="left", direction=None, features=None):
|
spacing=4, align="left", direction=None, features=None,
|
||||||
|
language=None):
|
||||||
widths = []
|
widths = []
|
||||||
max_width = 0
|
max_width = 0
|
||||||
lines = self._multiline_split(text)
|
lines = self._multiline_split(text)
|
||||||
line_spacing = self.textsize('A', font=font)[1] + spacing
|
line_spacing = self.textsize('A', font=font)[1] + spacing
|
||||||
for line in lines:
|
for line in lines:
|
||||||
line_width, line_height = self.textsize(line, font)
|
line_width, line_height = self.textsize(line, font,
|
||||||
|
direction=direction,
|
||||||
|
features=features,
|
||||||
|
language=language)
|
||||||
widths.append(line_width)
|
widths.append(line_width)
|
||||||
max_width = max(max_width, line_width)
|
max_width = max(max_width, line_width)
|
||||||
left, top = xy
|
left, top = xy
|
||||||
|
@ -302,29 +306,30 @@ class ImageDraw(object):
|
||||||
else:
|
else:
|
||||||
raise ValueError('align must be "left", "center" or "right"')
|
raise ValueError('align must be "left", "center" or "right"')
|
||||||
self.text((left, top), line, fill, font, anchor,
|
self.text((left, top), line, fill, font, anchor,
|
||||||
direction=direction, features=features)
|
direction=direction, features=features, language=language)
|
||||||
top += line_spacing
|
top += line_spacing
|
||||||
left = xy[0]
|
left = xy[0]
|
||||||
|
|
||||||
def textsize(self, text, font=None, spacing=4, direction=None,
|
def textsize(self, text, font=None, spacing=4, direction=None,
|
||||||
features=None):
|
features=None, language=None):
|
||||||
"""Get the size of a given string, in pixels."""
|
"""Get the size of a given string, in pixels."""
|
||||||
if self._multiline_check(text):
|
if self._multiline_check(text):
|
||||||
return self.multiline_textsize(text, font, spacing,
|
return self.multiline_textsize(text, font, spacing,
|
||||||
direction, features)
|
direction, features, language)
|
||||||
|
|
||||||
if font is None:
|
if font is None:
|
||||||
font = self.getfont()
|
font = self.getfont()
|
||||||
return font.getsize(text, direction, features)
|
return font.getsize(text, direction, features, language)
|
||||||
|
|
||||||
def multiline_textsize(self, text, font=None, spacing=4, direction=None,
|
def multiline_textsize(self, text, font=None, spacing=4, direction=None,
|
||||||
features=None):
|
features=None, language=None):
|
||||||
max_width = 0
|
max_width = 0
|
||||||
lines = self._multiline_split(text)
|
lines = self._multiline_split(text)
|
||||||
line_spacing = self.textsize('A', font=font)[1] + spacing
|
line_spacing = self.textsize('A', font=font)[1] + spacing
|
||||||
for line in lines:
|
for line in lines:
|
||||||
line_width, line_height = self.textsize(line, font, spacing,
|
line_width, line_height = self.textsize(line, font, spacing,
|
||||||
direction, features)
|
direction, features,
|
||||||
|
language)
|
||||||
max_width = max(max_width, line_width)
|
max_width = max(max_width, line_width)
|
||||||
return max_width, len(lines)*line_spacing - spacing
|
return max_width, len(lines)*line_spacing - spacing
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
@ -595,8 +596,6 @@ class PyDecoder(object):
|
||||||
Override to perform the decoding process.
|
Override to perform the decoding process.
|
||||||
|
|
||||||
:param buffer: A bytes object with the data to be decoded.
|
:param buffer: A bytes object with the data to be decoded.
|
||||||
If `handles_eof` is set, then `buffer` will be empty and `self.fd`
|
|
||||||
will be set.
|
|
||||||
:returns: A tuple of (bytes consumed, errcode).
|
:returns: A tuple of (bytes consumed, errcode).
|
||||||
If finished with decoding return <0 for the bytes consumed.
|
If finished with decoding return <0 for the bytes consumed.
|
||||||
Err codes are from `ERRORS`
|
Err codes are from `ERRORS`
|
||||||
|
|
|
@ -158,17 +158,17 @@ class FreeTypeFont(object):
|
||||||
def getmetrics(self):
|
def getmetrics(self):
|
||||||
return self.font.ascent, self.font.descent
|
return self.font.ascent, self.font.descent
|
||||||
|
|
||||||
def getsize(self, text, direction=None, features=None):
|
def getsize(self, text, direction=None, features=None, language=None):
|
||||||
size, offset = self.font.getsize(text, direction, features)
|
size, offset = self.font.getsize(text, direction, features, language)
|
||||||
return (size[0] + offset[0], size[1] + offset[1])
|
return (size[0] + offset[0], size[1] + offset[1])
|
||||||
|
|
||||||
def getsize_multiline(self, text, direction=None,
|
def getsize_multiline(self, text, direction=None, spacing=4,
|
||||||
spacing=4, features=None):
|
features=None, language=None):
|
||||||
max_width = 0
|
max_width = 0
|
||||||
lines = self._multiline_split(text)
|
lines = self._multiline_split(text)
|
||||||
line_spacing = self.getsize('A')[1] + spacing
|
line_spacing = self.getsize('A')[1] + spacing
|
||||||
for line in lines:
|
for line in lines:
|
||||||
line_width, line_height = self.getsize(line, direction, features)
|
line_width, line_height = self.getsize(line, direction, features, language)
|
||||||
max_width = max(max_width, line_width)
|
max_width = max(max_width, line_width)
|
||||||
|
|
||||||
return max_width, len(lines)*line_spacing - spacing
|
return max_width, len(lines)*line_spacing - spacing
|
||||||
|
@ -176,15 +176,15 @@ class FreeTypeFont(object):
|
||||||
def getoffset(self, text):
|
def getoffset(self, text):
|
||||||
return self.font.getsize(text)[1]
|
return self.font.getsize(text)[1]
|
||||||
|
|
||||||
def getmask(self, text, mode="", direction=None, features=None):
|
def getmask(self, text, mode="", direction=None, features=None, language=None):
|
||||||
return self.getmask2(text, mode, direction=direction,
|
return self.getmask2(text, mode, direction=direction, features=features,
|
||||||
features=features)[0]
|
language=language)[0]
|
||||||
|
|
||||||
def getmask2(self, text, mode="", fill=Image.core.fill, direction=None,
|
def getmask2(self, text, mode="", fill=Image.core.fill, direction=None,
|
||||||
features=None, *args, **kwargs):
|
features=None, language=None, *args, **kwargs):
|
||||||
size, offset = self.font.getsize(text, direction, features)
|
size, offset = self.font.getsize(text, direction, features, language)
|
||||||
im = fill("L", size, 0)
|
im = fill("L", size, 0)
|
||||||
self.font.render(text, im.id, mode == "1", direction, features)
|
self.font.render(text, im.id, mode == "1", direction, features, language)
|
||||||
return im, offset
|
return im, offset
|
||||||
|
|
||||||
def font_variant(self, font=None, size=None, index=None, encoding=None,
|
def font_variant(self, font=None, size=None, index=None, encoding=None,
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
@ -179,3 +180,5 @@ Image.register_open(PcxImageFile.format, PcxImageFile, _accept)
|
||||||
Image.register_save(PcxImageFile.format, _save)
|
Image.register_save(PcxImageFile.format, _save)
|
||||||
|
|
||||||
Image.register_extension(PcxImageFile.format, ".pcx")
|
Image.register_extension(PcxImageFile.format, ".pcx")
|
||||||
|
|
||||||
|
Image.register_mime(PcxImageFile.format, "image/x-pcx")
|
||||||
|
|
|
@ -269,18 +269,13 @@ 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:
|
try:
|
||||||
value = self[key]
|
value = self[key.encode("us-ascii")]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
try:
|
raise AttributeError(key)
|
||||||
value = self[key.encode("us-ascii")]
|
|
||||||
except KeyError:
|
|
||||||
raise AttributeError(key)
|
|
||||||
if isinstance(value, bytes):
|
if isinstance(value, bytes):
|
||||||
value = decode_text(value)
|
value = decode_text(value)
|
||||||
if key.endswith("Date"):
|
if key.endswith("Date"):
|
||||||
|
@ -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")
|
||||||
|
|
|
@ -54,19 +54,24 @@ _MAGIC = b"\211PNG\r\n\032\n"
|
||||||
|
|
||||||
_MODES = {
|
_MODES = {
|
||||||
# supported bits/color combinations, and corresponding modes/rawmodes
|
# supported bits/color combinations, and corresponding modes/rawmodes
|
||||||
|
# Greyscale
|
||||||
(1, 0): ("1", "1"),
|
(1, 0): ("1", "1"),
|
||||||
(2, 0): ("L", "L;2"),
|
(2, 0): ("L", "L;2"),
|
||||||
(4, 0): ("L", "L;4"),
|
(4, 0): ("L", "L;4"),
|
||||||
(8, 0): ("L", "L"),
|
(8, 0): ("L", "L"),
|
||||||
(16, 0): ("I", "I;16B"),
|
(16, 0): ("I", "I;16B"),
|
||||||
|
# Truecolour
|
||||||
(8, 2): ("RGB", "RGB"),
|
(8, 2): ("RGB", "RGB"),
|
||||||
(16, 2): ("RGB", "RGB;16B"),
|
(16, 2): ("RGB", "RGB;16B"),
|
||||||
|
# Indexed-colour
|
||||||
(1, 3): ("P", "P;1"),
|
(1, 3): ("P", "P;1"),
|
||||||
(2, 3): ("P", "P;2"),
|
(2, 3): ("P", "P;2"),
|
||||||
(4, 3): ("P", "P;4"),
|
(4, 3): ("P", "P;4"),
|
||||||
(8, 3): ("P", "P"),
|
(8, 3): ("P", "P"),
|
||||||
|
# Greyscale with alpha
|
||||||
(8, 4): ("LA", "LA"),
|
(8, 4): ("LA", "LA"),
|
||||||
(16, 4): ("RGBA", "LA;16B"), # LA;16B->LA not yet available
|
(16, 4): ("RGBA", "LA;16B"), # LA;16B->LA not yet available
|
||||||
|
# Truecolour with alpha
|
||||||
(8, 6): ("RGBA", "RGBA"),
|
(8, 6): ("RGBA", "RGBA"),
|
||||||
(16, 6): ("RGBA", "RGBA;16B"),
|
(16, 6): ("RGBA", "RGBA;16B"),
|
||||||
}
|
}
|
||||||
|
@ -386,7 +391,7 @@ class PngStream(ChunkStream):
|
||||||
# otherwise, we have a byte string with one alpha value
|
# otherwise, we have a byte string with one alpha value
|
||||||
# for each palette entry
|
# for each palette entry
|
||||||
self.im_info["transparency"] = s
|
self.im_info["transparency"] = s
|
||||||
elif self.im_mode == "L":
|
elif self.im_mode in ("1", "L", "I"):
|
||||||
self.im_info["transparency"] = i16(s)
|
self.im_info["transparency"] = i16(s)
|
||||||
elif self.im_mode == "RGB":
|
elif self.im_mode == "RGB":
|
||||||
self.im_info["transparency"] = i16(s), i16(s[2:]), i16(s[4:])
|
self.im_info["transparency"] = i16(s), i16(s[2:]), i16(s[4:])
|
||||||
|
@ -529,6 +534,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 +693,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
|
||||||
|
@ -696,6 +712,7 @@ _OUTMODES = {
|
||||||
"L": ("L", b'\x08\x00'),
|
"L": ("L", b'\x08\x00'),
|
||||||
"LA": ("LA", b'\x08\x04'),
|
"LA": ("LA", b'\x08\x04'),
|
||||||
"I": ("I;16B", b'\x10\x00'),
|
"I": ("I;16B", b'\x10\x00'),
|
||||||
|
"I;16": ("I;16B", b'\x10\x00'),
|
||||||
"P;1": ("P;1", b'\x01\x03'),
|
"P;1": ("P;1", b'\x01\x03'),
|
||||||
"P;2": ("P;2", b'\x02\x03'),
|
"P;2": ("P;2", b'\x02\x03'),
|
||||||
"P;4": ("P;4", b'\x04\x03'),
|
"P;4": ("P;4", b'\x04\x03'),
|
||||||
|
@ -829,7 +846,7 @@ def _save(im, fp, filename, chunk=putchunk):
|
||||||
transparency = max(0, min(255, transparency))
|
transparency = max(0, min(255, transparency))
|
||||||
alpha = b'\xFF' * transparency + b'\0'
|
alpha = b'\xFF' * transparency + b'\0'
|
||||||
chunk(fp, b"tRNS", alpha[:alpha_bytes])
|
chunk(fp, b"tRNS", alpha[:alpha_bytes])
|
||||||
elif im.mode == "L":
|
elif im.mode in ("1", "L", "I"):
|
||||||
transparency = max(0, min(65535, transparency))
|
transparency = max(0, min(65535, transparency))
|
||||||
chunk(fp, b"tRNS", o16(transparency))
|
chunk(fp, b"tRNS", o16(transparency))
|
||||||
elif im.mode == "RGB":
|
elif im.mode == "RGB":
|
||||||
|
@ -861,6 +878,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"
|
||||||
|
@ -157,4 +164,6 @@ def _save(im, fp, filename):
|
||||||
Image.register_open(PpmImageFile.format, PpmImageFile, _accept)
|
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", ".pnm"])
|
||||||
|
|
||||||
|
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
|
||||||
|
|
|
@ -208,7 +208,7 @@ class SpiderImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
# given a list of filenames, return a list of images
|
# given a list of filenames, return a list of images
|
||||||
def loadImageSeries(filelist=None):
|
def loadImageSeries(filelist=None):
|
||||||
"""create a list of Image.images for use in montage"""
|
"""create a list of :py:class:`~PIL.Image.Image` objects for use in a montage"""
|
||||||
if filelist is None or len(filelist) < 1:
|
if filelist is None or len(filelist) < 1:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -226,4 +226,6 @@ def _save(im, fp, filename):
|
||||||
Image.register_open(TgaImageFile.format, TgaImageFile)
|
Image.register_open(TgaImageFile.format, TgaImageFile)
|
||||||
Image.register_save(TgaImageFile.format, _save)
|
Image.register_save(TgaImageFile.format, _save)
|
||||||
|
|
||||||
Image.register_extension(TgaImageFile.format, ".tga")
|
Image.register_extensions(TgaImageFile.format, [".tga", ".icb", ".vda", ".vst"])
|
||||||
|
|
||||||
|
Image.register_mime(TgaImageFile.format, "image/x-tga")
|
||||||
|
|
|
@ -263,10 +263,10 @@ OPEN_INFO = {
|
||||||
(II, 5, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0)): ("CMYK", "CMYKXX"),
|
(II, 5, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0)): ("CMYK", "CMYKXX"),
|
||||||
(MM, 5, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0)): ("CMYK", "CMYKXX"),
|
(MM, 5, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0)): ("CMYK", "CMYKXX"),
|
||||||
|
|
||||||
# JPEG compressed images handled by LibTiff and auto-converted to RGB
|
# JPEG compressed images handled by LibTiff and auto-converted to RGBX
|
||||||
# Minimal Baseline TIFF requires YCbCr images to have 3 SamplesPerPixel
|
# Minimal Baseline TIFF requires YCbCr images to have 3 SamplesPerPixel
|
||||||
(II, 6, (1,), 1, (8, 8, 8), ()): ("RGB", "RGB"),
|
(II, 6, (1,), 1, (8, 8, 8), ()): ("RGB", "RGBX"),
|
||||||
(MM, 6, (1,), 1, (8, 8, 8), ()): ("RGB", "RGB"),
|
(MM, 6, (1,), 1, (8, 8, 8), ()): ("RGB", "RGBX"),
|
||||||
|
|
||||||
(II, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"),
|
(II, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"),
|
||||||
(MM, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"),
|
(MM, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"),
|
||||||
|
@ -819,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)
|
||||||
|
@ -1191,6 +1191,10 @@ class TiffImageFile(ImageFile.ImageFile):
|
||||||
# the specification
|
# the specification
|
||||||
photo = self.tag_v2.get(PHOTOMETRIC_INTERPRETATION, 0)
|
photo = self.tag_v2.get(PHOTOMETRIC_INTERPRETATION, 0)
|
||||||
|
|
||||||
|
# old style jpeg compression images most certainly are YCbCr
|
||||||
|
if self._compression == "tiff_jpeg":
|
||||||
|
photo = 6
|
||||||
|
|
||||||
fillorder = self.tag_v2.get(FILLORDER, 1)
|
fillorder = self.tag_v2.get(FILLORDER, 1)
|
||||||
|
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
|
@ -1439,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
|
||||||
|
@ -1683,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
|
||||||
|
|
||||||
|
|
|
@ -186,7 +186,7 @@ def _save_all(im, fp, filename):
|
||||||
# will preserve non-alpha modes
|
# will preserve non-alpha modes
|
||||||
total = 0
|
total = 0
|
||||||
for ims in [im]+append_images:
|
for ims in [im]+append_images:
|
||||||
total += 1 if not hasattr(ims, "n_frames") else ims.n_frames
|
total += getattr(ims, "n_frames", 1)
|
||||||
if total == 1:
|
if total == 1:
|
||||||
_save(im, fp, filename)
|
_save(im, fp, filename)
|
||||||
return
|
return
|
||||||
|
@ -254,10 +254,7 @@ def _save_all(im, fp, filename):
|
||||||
try:
|
try:
|
||||||
for ims in [im]+append_images:
|
for ims in [im]+append_images:
|
||||||
# Get # of frames in this image
|
# Get # of frames in this image
|
||||||
if not hasattr(ims, "n_frames"):
|
nfr = getattr(ims, "n_frames", 1)
|
||||||
nfr = 1
|
|
||||||
else:
|
|
||||||
nfr = ims.n_frames
|
|
||||||
|
|
||||||
for idx in range(nfr):
|
for idx in range(nfr):
|
||||||
ims.seek(idx)
|
ims.seek(idx)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -71,6 +71,7 @@
|
||||||
* See the README file for information on usage and redistribution.
|
* See the README file for information on usage and redistribution.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define PY_SSIZE_T_CLEAN
|
||||||
#include "Python.h"
|
#include "Python.h"
|
||||||
|
|
||||||
#ifdef HAVE_LIBJPEG
|
#ifdef HAVE_LIBJPEG
|
||||||
|
@ -1564,7 +1565,7 @@ _putpalette(ImagingObject* self, PyObject* args)
|
||||||
|
|
||||||
char* rawmode;
|
char* rawmode;
|
||||||
UINT8* palette;
|
UINT8* palette;
|
||||||
int palettesize;
|
Py_ssize_t palettesize;
|
||||||
if (!PyArg_ParseTuple(args, "s"PY_ARG_BYTES_LENGTH, &rawmode, &palette, &palettesize))
|
if (!PyArg_ParseTuple(args, "s"PY_ARG_BYTES_LENGTH, &rawmode, &palette, &palettesize))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
@ -1626,7 +1627,7 @@ _putpalettealphas(ImagingObject* self, PyObject* args)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
UINT8 *values;
|
UINT8 *values;
|
||||||
int length;
|
Py_ssize_t length;
|
||||||
if (!PyArg_ParseTuple(args, PY_ARG_BYTES_LENGTH, &values, &length))
|
if (!PyArg_ParseTuple(args, PY_ARG_BYTES_LENGTH, &values, &length))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
@ -1770,7 +1771,7 @@ im_setmode(ImagingObject* self, PyObject* args)
|
||||||
Imaging im;
|
Imaging im;
|
||||||
|
|
||||||
char* mode;
|
char* mode;
|
||||||
int modelen;
|
Py_ssize_t modelen;
|
||||||
if (!PyArg_ParseTuple(args, "s#:setmode", &mode, &modelen))
|
if (!PyArg_ParseTuple(args, "s#:setmode", &mode, &modelen))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
@ -2066,8 +2067,8 @@ _getprojection(ImagingObject* self, PyObject* args)
|
||||||
ImagingGetProjection(self->image, (unsigned char *)xprofile, (unsigned char *)yprofile);
|
ImagingGetProjection(self->image, (unsigned char *)xprofile, (unsigned char *)yprofile);
|
||||||
|
|
||||||
result = Py_BuildValue(PY_ARG_BYTES_LENGTH PY_ARG_BYTES_LENGTH,
|
result = Py_BuildValue(PY_ARG_BYTES_LENGTH PY_ARG_BYTES_LENGTH,
|
||||||
xprofile, self->image->xsize,
|
xprofile, (Py_ssize_t)self->image->xsize,
|
||||||
yprofile, self->image->ysize);
|
yprofile, (Py_ssize_t)self->image->ysize);
|
||||||
|
|
||||||
free(xprofile);
|
free(xprofile);
|
||||||
free(yprofile);
|
free(yprofile);
|
||||||
|
@ -2342,7 +2343,7 @@ _font_new(PyObject* self_, PyObject* args)
|
||||||
|
|
||||||
ImagingObject* imagep;
|
ImagingObject* imagep;
|
||||||
unsigned char* glyphdata;
|
unsigned char* glyphdata;
|
||||||
int glyphdata_length;
|
Py_ssize_t glyphdata_length;
|
||||||
if (!PyArg_ParseTuple(args, "O!"PY_ARG_BYTES_LENGTH,
|
if (!PyArg_ParseTuple(args, "O!"PY_ARG_BYTES_LENGTH,
|
||||||
&Imaging_Type, &imagep,
|
&Imaging_Type, &imagep,
|
||||||
&glyphdata, &glyphdata_length))
|
&glyphdata, &glyphdata_length))
|
||||||
|
|
|
@ -25,6 +25,7 @@ kevin@cazabon.com\n\
|
||||||
http://www.cazabon.com\n\
|
http://www.cazabon.com\n\
|
||||||
"
|
"
|
||||||
|
|
||||||
|
#define PY_SSIZE_T_CLEAN
|
||||||
#include "Python.h" // Include before wchar.h so _GNU_SOURCE is set
|
#include "Python.h" // Include before wchar.h so _GNU_SOURCE is set
|
||||||
#include "wchar.h"
|
#include "wchar.h"
|
||||||
#include "datetime.h"
|
#include "datetime.h"
|
||||||
|
@ -120,7 +121,7 @@ cms_profile_fromstring(PyObject* self, PyObject* args)
|
||||||
cmsHPROFILE hProfile;
|
cmsHPROFILE hProfile;
|
||||||
|
|
||||||
char* pProfile;
|
char* pProfile;
|
||||||
int nProfile;
|
Py_ssize_t nProfile;
|
||||||
#if PY_VERSION_HEX >= 0x03000000
|
#if PY_VERSION_HEX >= 0x03000000
|
||||||
if (!PyArg_ParseTuple(args, "y#:profile_frombytes", &pProfile, &nProfile))
|
if (!PyArg_ParseTuple(args, "y#:profile_frombytes", &pProfile, &nProfile))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
* Copyright (c) 1998-2007 by Secret Labs AB
|
* Copyright (c) 1998-2007 by Secret Labs AB
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define PY_SSIZE_T_CLEAN
|
||||||
#include "Python.h"
|
#include "Python.h"
|
||||||
#include "Imaging.h"
|
#include "Imaging.h"
|
||||||
|
|
||||||
|
@ -87,6 +88,10 @@ typedef bool (*t_raqm_set_text_utf8) (raqm_t *rq,
|
||||||
size_t len);
|
size_t len);
|
||||||
typedef bool (*t_raqm_set_par_direction) (raqm_t *rq,
|
typedef bool (*t_raqm_set_par_direction) (raqm_t *rq,
|
||||||
raqm_direction_t dir);
|
raqm_direction_t dir);
|
||||||
|
typedef bool (*t_raqm_set_language) (raqm_t *rq,
|
||||||
|
const char *lang,
|
||||||
|
size_t start,
|
||||||
|
size_t len);
|
||||||
typedef bool (*t_raqm_add_font_feature) (raqm_t *rq,
|
typedef bool (*t_raqm_add_font_feature) (raqm_t *rq,
|
||||||
const char *feature,
|
const char *feature,
|
||||||
int len);
|
int len);
|
||||||
|
@ -106,6 +111,7 @@ typedef struct {
|
||||||
t_raqm_set_text set_text;
|
t_raqm_set_text set_text;
|
||||||
t_raqm_set_text_utf8 set_text_utf8;
|
t_raqm_set_text_utf8 set_text_utf8;
|
||||||
t_raqm_set_par_direction set_par_direction;
|
t_raqm_set_par_direction set_par_direction;
|
||||||
|
t_raqm_set_language set_language;
|
||||||
t_raqm_add_font_feature add_font_feature;
|
t_raqm_add_font_feature add_font_feature;
|
||||||
t_raqm_set_freetype_face set_freetype_face;
|
t_raqm_set_freetype_face set_freetype_face;
|
||||||
t_raqm_layout layout;
|
t_raqm_layout layout;
|
||||||
|
@ -160,6 +166,7 @@ setraqm(void)
|
||||||
p_raqm.set_text = (t_raqm_set_text)dlsym(p_raqm.raqm, "raqm_set_text");
|
p_raqm.set_text = (t_raqm_set_text)dlsym(p_raqm.raqm, "raqm_set_text");
|
||||||
p_raqm.set_text_utf8 = (t_raqm_set_text_utf8)dlsym(p_raqm.raqm, "raqm_set_text_utf8");
|
p_raqm.set_text_utf8 = (t_raqm_set_text_utf8)dlsym(p_raqm.raqm, "raqm_set_text_utf8");
|
||||||
p_raqm.set_par_direction = (t_raqm_set_par_direction)dlsym(p_raqm.raqm, "raqm_set_par_direction");
|
p_raqm.set_par_direction = (t_raqm_set_par_direction)dlsym(p_raqm.raqm, "raqm_set_par_direction");
|
||||||
|
p_raqm.set_language = (t_raqm_set_language)dlsym(p_raqm.raqm, "raqm_set_language");
|
||||||
p_raqm.add_font_feature = (t_raqm_add_font_feature)dlsym(p_raqm.raqm, "raqm_add_font_feature");
|
p_raqm.add_font_feature = (t_raqm_add_font_feature)dlsym(p_raqm.raqm, "raqm_add_font_feature");
|
||||||
p_raqm.set_freetype_face = (t_raqm_set_freetype_face)dlsym(p_raqm.raqm, "raqm_set_freetype_face");
|
p_raqm.set_freetype_face = (t_raqm_set_freetype_face)dlsym(p_raqm.raqm, "raqm_set_freetype_face");
|
||||||
p_raqm.layout = (t_raqm_layout)dlsym(p_raqm.raqm, "raqm_layout");
|
p_raqm.layout = (t_raqm_layout)dlsym(p_raqm.raqm, "raqm_layout");
|
||||||
|
@ -176,6 +183,7 @@ setraqm(void)
|
||||||
p_raqm.set_text &&
|
p_raqm.set_text &&
|
||||||
p_raqm.set_text_utf8 &&
|
p_raqm.set_text_utf8 &&
|
||||||
p_raqm.set_par_direction &&
|
p_raqm.set_par_direction &&
|
||||||
|
p_raqm.set_language &&
|
||||||
p_raqm.add_font_feature &&
|
p_raqm.add_font_feature &&
|
||||||
p_raqm.set_freetype_face &&
|
p_raqm.set_freetype_face &&
|
||||||
p_raqm.layout &&
|
p_raqm.layout &&
|
||||||
|
@ -190,6 +198,7 @@ setraqm(void)
|
||||||
p_raqm.set_text = (t_raqm_set_text)GetProcAddress(p_raqm.raqm, "raqm_set_text");
|
p_raqm.set_text = (t_raqm_set_text)GetProcAddress(p_raqm.raqm, "raqm_set_text");
|
||||||
p_raqm.set_text_utf8 = (t_raqm_set_text_utf8)GetProcAddress(p_raqm.raqm, "raqm_set_text_utf8");
|
p_raqm.set_text_utf8 = (t_raqm_set_text_utf8)GetProcAddress(p_raqm.raqm, "raqm_set_text_utf8");
|
||||||
p_raqm.set_par_direction = (t_raqm_set_par_direction)GetProcAddress(p_raqm.raqm, "raqm_set_par_direction");
|
p_raqm.set_par_direction = (t_raqm_set_par_direction)GetProcAddress(p_raqm.raqm, "raqm_set_par_direction");
|
||||||
|
p_raqm.set_language = (t_raqm_set_language)GetProcAddress(p_raqm.raqm, "raqm_set_language");
|
||||||
p_raqm.add_font_feature = (t_raqm_add_font_feature)GetProcAddress(p_raqm.raqm, "raqm_add_font_feature");
|
p_raqm.add_font_feature = (t_raqm_add_font_feature)GetProcAddress(p_raqm.raqm, "raqm_add_font_feature");
|
||||||
p_raqm.set_freetype_face = (t_raqm_set_freetype_face)GetProcAddress(p_raqm.raqm, "raqm_set_freetype_face");
|
p_raqm.set_freetype_face = (t_raqm_set_freetype_face)GetProcAddress(p_raqm.raqm, "raqm_set_freetype_face");
|
||||||
p_raqm.layout = (t_raqm_layout)GetProcAddress(p_raqm.raqm, "raqm_layout");
|
p_raqm.layout = (t_raqm_layout)GetProcAddress(p_raqm.raqm, "raqm_layout");
|
||||||
|
@ -205,6 +214,7 @@ setraqm(void)
|
||||||
p_raqm.set_text &&
|
p_raqm.set_text &&
|
||||||
p_raqm.set_text_utf8 &&
|
p_raqm.set_text_utf8 &&
|
||||||
p_raqm.set_par_direction &&
|
p_raqm.set_par_direction &&
|
||||||
|
p_raqm.set_language &&
|
||||||
p_raqm.add_font_feature &&
|
p_raqm.add_font_feature &&
|
||||||
p_raqm.set_freetype_face &&
|
p_raqm.set_freetype_face &&
|
||||||
p_raqm.layout &&
|
p_raqm.layout &&
|
||||||
|
@ -228,12 +238,12 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw)
|
||||||
int error = 0;
|
int error = 0;
|
||||||
|
|
||||||
char* filename = NULL;
|
char* filename = NULL;
|
||||||
int size;
|
Py_ssize_t size;
|
||||||
int index = 0;
|
Py_ssize_t index = 0;
|
||||||
int layout_engine = 0;
|
Py_ssize_t layout_engine = 0;
|
||||||
unsigned char* encoding;
|
unsigned char* encoding;
|
||||||
unsigned char* font_bytes;
|
unsigned char* font_bytes;
|
||||||
int font_bytes_size = 0;
|
Py_ssize_t font_bytes_size = 0;
|
||||||
static char* kwlist[] = {
|
static char* kwlist[] = {
|
||||||
"filename", "size", "index", "encoding", "font_bytes",
|
"filename", "size", "index", "encoding", "font_bytes",
|
||||||
"layout_engine", NULL
|
"layout_engine", NULL
|
||||||
|
@ -332,8 +342,8 @@ font_getchar(PyObject* string, int index, FT_ULong* char_out)
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t
|
static size_t
|
||||||
text_layout_raqm(PyObject* string, FontObject* self, const char* dir,
|
text_layout_raqm(PyObject* string, FontObject* self, const char* dir, PyObject *features,
|
||||||
PyObject *features ,GlyphInfo **glyph_info, int mask)
|
const char* lang, GlyphInfo **glyph_info, int mask)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
raqm_t *rq;
|
raqm_t *rq;
|
||||||
|
@ -341,6 +351,7 @@ text_layout_raqm(PyObject* string, FontObject* self, const char* dir,
|
||||||
raqm_glyph_t *glyphs = NULL;
|
raqm_glyph_t *glyphs = NULL;
|
||||||
raqm_glyph_t_01 *glyphs_01 = NULL;
|
raqm_glyph_t_01 *glyphs_01 = NULL;
|
||||||
raqm_direction_t direction;
|
raqm_direction_t direction;
|
||||||
|
size_t start = 0;
|
||||||
|
|
||||||
rq = (*p_raqm.create)();
|
rq = (*p_raqm.create)();
|
||||||
if (rq == NULL) {
|
if (rq == NULL) {
|
||||||
|
@ -360,6 +371,13 @@ text_layout_raqm(PyObject* string, FontObject* self, const char* dir,
|
||||||
PyErr_SetString(PyExc_ValueError, "raqm_set_text() failed");
|
PyErr_SetString(PyExc_ValueError, "raqm_set_text() failed");
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
|
if (lang) {
|
||||||
|
if (!(*p_raqm.set_language)(rq, lang, start, size)) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "raqm_set_language() failed");
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
#if PY_VERSION_HEX < 0x03000000
|
#if PY_VERSION_HEX < 0x03000000
|
||||||
else if (PyString_Check(string)) {
|
else if (PyString_Check(string)) {
|
||||||
|
@ -372,6 +390,12 @@ text_layout_raqm(PyObject* string, FontObject* self, const char* dir,
|
||||||
PyErr_SetString(PyExc_ValueError, "raqm_set_text_utf8() failed");
|
PyErr_SetString(PyExc_ValueError, "raqm_set_text_utf8() failed");
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
|
if (lang) {
|
||||||
|
if (!(*p_raqm.set_language)(rq, lang, start, size)) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "raqm_set_language() failed");
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
else {
|
else {
|
||||||
|
@ -498,8 +522,8 @@ failed:
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t
|
static size_t
|
||||||
text_layout_fallback(PyObject* string, FontObject* self, const char* dir,
|
text_layout_fallback(PyObject* string, FontObject* self, const char* dir, PyObject *features,
|
||||||
PyObject *features ,GlyphInfo **glyph_info, int mask)
|
const char* lang, GlyphInfo **glyph_info, int mask)
|
||||||
{
|
{
|
||||||
int error, load_flags;
|
int error, load_flags;
|
||||||
FT_ULong ch;
|
FT_ULong ch;
|
||||||
|
@ -509,8 +533,8 @@ text_layout_fallback(PyObject* string, FontObject* self, const char* dir,
|
||||||
FT_UInt last_index = 0;
|
FT_UInt last_index = 0;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (features != Py_None || dir != NULL) {
|
if (features != Py_None || dir != NULL || lang != NULL) {
|
||||||
PyErr_SetString(PyExc_KeyError, "setting text direction or font features is not supported without libraqm");
|
PyErr_SetString(PyExc_KeyError, "setting text direction, language or font features is not supported without libraqm");
|
||||||
}
|
}
|
||||||
#if PY_VERSION_HEX >= 0x03000000
|
#if PY_VERSION_HEX >= 0x03000000
|
||||||
if (!PyUnicode_Check(string)) {
|
if (!PyUnicode_Check(string)) {
|
||||||
|
@ -564,15 +588,15 @@ text_layout_fallback(PyObject* string, FontObject* self, const char* dir,
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t
|
static size_t
|
||||||
text_layout(PyObject* string, FontObject* self, const char* dir,
|
text_layout(PyObject* string, FontObject* self, const char* dir, PyObject *features,
|
||||||
PyObject *features, GlyphInfo **glyph_info, int mask)
|
const char* lang, GlyphInfo **glyph_info, int mask)
|
||||||
{
|
{
|
||||||
size_t count;
|
size_t count;
|
||||||
|
|
||||||
if (p_raqm.raqm && self->layout_engine == LAYOUT_RAQM) {
|
if (p_raqm.raqm && self->layout_engine == LAYOUT_RAQM) {
|
||||||
count = text_layout_raqm(string, self, dir, features, glyph_info, mask);
|
count = text_layout_raqm(string, self, dir, features, lang, glyph_info, mask);
|
||||||
} else {
|
} else {
|
||||||
count = text_layout_fallback(string, self, dir, features, glyph_info, mask);
|
count = text_layout_fallback(string, self, dir, features, lang, glyph_info, mask);
|
||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
@ -584,6 +608,7 @@ font_getsize(FontObject* self, PyObject* args)
|
||||||
FT_Face face;
|
FT_Face face;
|
||||||
int xoffset, yoffset;
|
int xoffset, yoffset;
|
||||||
const char *dir = NULL;
|
const char *dir = NULL;
|
||||||
|
const char *lang = NULL;
|
||||||
size_t count;
|
size_t count;
|
||||||
GlyphInfo *glyph_info = NULL;
|
GlyphInfo *glyph_info = NULL;
|
||||||
PyObject *features = Py_None;
|
PyObject *features = Py_None;
|
||||||
|
@ -591,14 +616,14 @@ font_getsize(FontObject* self, PyObject* args)
|
||||||
/* calculate size and bearing for a given string */
|
/* calculate size and bearing for a given string */
|
||||||
|
|
||||||
PyObject* string;
|
PyObject* string;
|
||||||
if (!PyArg_ParseTuple(args, "O|zO:getsize", &string, &dir, &features))
|
if (!PyArg_ParseTuple(args, "O|zOz:getsize", &string, &dir, &features, &lang))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
face = NULL;
|
face = NULL;
|
||||||
xoffset = yoffset = 0;
|
xoffset = yoffset = 0;
|
||||||
y_max = y_min = 0;
|
y_max = y_min = 0;
|
||||||
|
|
||||||
count = text_layout(string, self, dir, features, &glyph_info, 0);
|
count = text_layout(string, self, dir, features, lang, &glyph_info, 0);
|
||||||
if (PyErr_Occurred()) {
|
if (PyErr_Occurred()) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -691,16 +716,17 @@ font_render(FontObject* self, PyObject* args)
|
||||||
int temp;
|
int temp;
|
||||||
int xx, x0, x1;
|
int xx, x0, x1;
|
||||||
const char *dir = NULL;
|
const char *dir = NULL;
|
||||||
|
const char *lang = NULL;
|
||||||
size_t count;
|
size_t count;
|
||||||
GlyphInfo *glyph_info;
|
GlyphInfo *glyph_info;
|
||||||
PyObject *features = NULL;
|
PyObject *features = NULL;
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "On|izO:render", &string, &id, &mask, &dir, &features)) {
|
if (!PyArg_ParseTuple(args, "On|izOz:render", &string, &id, &mask, &dir, &features, &lang)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
glyph_info = NULL;
|
glyph_info = NULL;
|
||||||
count = text_layout(string, self, dir, features, &glyph_info, mask);
|
count = text_layout(string, self, dir, features, lang, &glyph_info, mask);
|
||||||
if (PyErr_Occurred()) {
|
if (PyErr_Occurred()) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
|
|
||||||
/* FIXME: make these pluggable! */
|
/* FIXME: make these pluggable! */
|
||||||
|
|
||||||
|
#define PY_SSIZE_T_CLEAN
|
||||||
#include "Python.h"
|
#include "Python.h"
|
||||||
|
|
||||||
#include "Imaging.h"
|
#include "Imaging.h"
|
||||||
|
@ -117,7 +118,8 @@ static PyObject*
|
||||||
_decode(ImagingDecoderObject* decoder, PyObject* args)
|
_decode(ImagingDecoderObject* decoder, PyObject* args)
|
||||||
{
|
{
|
||||||
UINT8* buffer;
|
UINT8* buffer;
|
||||||
int bufsize, status;
|
Py_ssize_t bufsize;
|
||||||
|
int status;
|
||||||
ImagingSectionCookie cookie;
|
ImagingSectionCookie cookie;
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, PY_ARG_BYTES_LENGTH, &buffer, &bufsize))
|
if (!PyArg_ParseTuple(args, PY_ARG_BYTES_LENGTH, &buffer, &bufsize))
|
||||||
|
|
53
src/encode.c
|
@ -22,6 +22,7 @@
|
||||||
|
|
||||||
/* FIXME: make these pluggable! */
|
/* FIXME: make these pluggable! */
|
||||||
|
|
||||||
|
#define PY_SSIZE_T_CLEAN
|
||||||
#include "Python.h"
|
#include "Python.h"
|
||||||
|
|
||||||
#include "Imaging.h"
|
#include "Imaging.h"
|
||||||
|
@ -123,7 +124,7 @@ _encode(ImagingEncoderObject* encoder, PyObject* args)
|
||||||
|
|
||||||
/* Encode to a Python string (allocated by this method) */
|
/* Encode to a Python string (allocated by this method) */
|
||||||
|
|
||||||
int bufsize = 16384;
|
Py_ssize_t bufsize = 16384;
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "|i", &bufsize))
|
if (!PyArg_ParseTuple(args, "|i", &bufsize))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -176,8 +177,8 @@ _encode_to_file(ImagingEncoderObject* encoder, PyObject* args)
|
||||||
|
|
||||||
/* Encode to a file handle */
|
/* Encode to a file handle */
|
||||||
|
|
||||||
int fh;
|
Py_ssize_t fh;
|
||||||
int bufsize = 16384;
|
Py_ssize_t bufsize = 16384;
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "i|i", &fh, &bufsize))
|
if (!PyArg_ParseTuple(args, "i|i", &fh, &bufsize))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -221,7 +222,7 @@ _setimage(ImagingEncoderObject* encoder, PyObject* args)
|
||||||
PyObject* op;
|
PyObject* op;
|
||||||
Imaging im;
|
Imaging im;
|
||||||
ImagingCodecState state;
|
ImagingCodecState state;
|
||||||
int x0, y0, x1, y1;
|
Py_ssize_t x0, y0, x1, y1;
|
||||||
|
|
||||||
/* Define where image data should be stored */
|
/* Define where image data should be stored */
|
||||||
|
|
||||||
|
@ -406,8 +407,8 @@ PyImaging_GifEncoderNew(PyObject* self, PyObject* args)
|
||||||
|
|
||||||
char *mode;
|
char *mode;
|
||||||
char *rawmode;
|
char *rawmode;
|
||||||
int bits = 8;
|
Py_ssize_t bits = 8;
|
||||||
int interlace = 0;
|
Py_ssize_t interlace = 0;
|
||||||
if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &bits, &interlace))
|
if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &bits, &interlace))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
@ -438,7 +439,7 @@ PyImaging_PcxEncoderNew(PyObject* self, PyObject* args)
|
||||||
|
|
||||||
char *mode;
|
char *mode;
|
||||||
char *rawmode;
|
char *rawmode;
|
||||||
int bits = 8;
|
Py_ssize_t bits = 8;
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &bits)) {
|
if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &bits)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -470,8 +471,8 @@ PyImaging_RawEncoderNew(PyObject* self, PyObject* args)
|
||||||
|
|
||||||
char *mode;
|
char *mode;
|
||||||
char *rawmode;
|
char *rawmode;
|
||||||
int stride = 0;
|
Py_ssize_t stride = 0;
|
||||||
int ystep = 1;
|
Py_ssize_t ystep = 1;
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &stride, &ystep))
|
if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &stride, &ystep))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -503,7 +504,7 @@ PyImaging_TgaRleEncoderNew(PyObject* self, PyObject* args)
|
||||||
|
|
||||||
char *mode;
|
char *mode;
|
||||||
char *rawmode;
|
char *rawmode;
|
||||||
int ystep = 1;
|
Py_ssize_t ystep = 1;
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "ss|i", &mode, &rawmode, &ystep))
|
if (!PyArg_ParseTuple(args, "ss|i", &mode, &rawmode, &ystep))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -561,11 +562,11 @@ PyImaging_ZipEncoderNew(PyObject* self, PyObject* args)
|
||||||
|
|
||||||
char* mode;
|
char* mode;
|
||||||
char* rawmode;
|
char* rawmode;
|
||||||
int optimize = 0;
|
Py_ssize_t optimize = 0;
|
||||||
int compress_level = -1;
|
Py_ssize_t compress_level = -1;
|
||||||
int compress_type = -1;
|
Py_ssize_t compress_type = -1;
|
||||||
char* dictionary = NULL;
|
char* dictionary = NULL;
|
||||||
int dictionary_size = 0;
|
Py_ssize_t dictionary_size = 0;
|
||||||
if (!PyArg_ParseTuple(args, "ss|iii"PY_ARG_BYTES_LENGTH, &mode, &rawmode,
|
if (!PyArg_ParseTuple(args, "ss|iii"PY_ARG_BYTES_LENGTH, &mode, &rawmode,
|
||||||
&optimize,
|
&optimize,
|
||||||
&compress_level, &compress_type,
|
&compress_level, &compress_type,
|
||||||
|
@ -701,20 +702,20 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args)
|
||||||
|
|
||||||
char *mode;
|
char *mode;
|
||||||
char *rawmode;
|
char *rawmode;
|
||||||
int quality = 0;
|
Py_ssize_t quality = 0;
|
||||||
int progressive = 0;
|
Py_ssize_t progressive = 0;
|
||||||
int smooth = 0;
|
Py_ssize_t smooth = 0;
|
||||||
int optimize = 0;
|
Py_ssize_t optimize = 0;
|
||||||
int streamtype = 0; /* 0=interchange, 1=tables only, 2=image only */
|
Py_ssize_t streamtype = 0; /* 0=interchange, 1=tables only, 2=image only */
|
||||||
int xdpi = 0, ydpi = 0;
|
Py_ssize_t xdpi = 0, ydpi = 0;
|
||||||
int subsampling = -1; /* -1=default, 0=none, 1=medium, 2=high */
|
Py_ssize_t subsampling = -1; /* -1=default, 0=none, 1=medium, 2=high */
|
||||||
PyObject* qtables=NULL;
|
PyObject* qtables=NULL;
|
||||||
unsigned int *qarrays = NULL;
|
unsigned int *qarrays = NULL;
|
||||||
int qtablesLen = 0;
|
int qtablesLen = 0;
|
||||||
char* extra = NULL;
|
char* extra = NULL;
|
||||||
int extra_size;
|
Py_ssize_t extra_size;
|
||||||
char* rawExif = NULL;
|
char* rawExif = NULL;
|
||||||
int rawExifLen = 0;
|
Py_ssize_t rawExifLen = 0;
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "ss|iiiiiiiiO"PY_ARG_BYTES_LENGTH""PY_ARG_BYTES_LENGTH,
|
if (!PyArg_ParseTuple(args, "ss|iiiiiiiiO"PY_ARG_BYTES_LENGTH""PY_ARG_BYTES_LENGTH,
|
||||||
&mode, &rawmode, &quality,
|
&mode, &rawmode, &quality,
|
||||||
|
@ -805,7 +806,7 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
|
||||||
char* rawmode;
|
char* rawmode;
|
||||||
char* compname;
|
char* compname;
|
||||||
char* filename;
|
char* filename;
|
||||||
int fp;
|
Py_ssize_t fp;
|
||||||
|
|
||||||
PyObject *dir;
|
PyObject *dir;
|
||||||
PyObject *key, *value;
|
PyObject *key, *value;
|
||||||
|
@ -985,14 +986,14 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args)
|
||||||
PyObject *offset = NULL, *tile_offset = NULL, *tile_size = NULL;
|
PyObject *offset = NULL, *tile_offset = NULL, *tile_size = NULL;
|
||||||
char *quality_mode = "rates";
|
char *quality_mode = "rates";
|
||||||
PyObject *quality_layers = NULL;
|
PyObject *quality_layers = NULL;
|
||||||
int num_resolutions = 0;
|
Py_ssize_t num_resolutions = 0;
|
||||||
PyObject *cblk_size = NULL, *precinct_size = NULL;
|
PyObject *cblk_size = NULL, *precinct_size = NULL;
|
||||||
PyObject *irreversible = NULL;
|
PyObject *irreversible = NULL;
|
||||||
char *progression = "LRCP";
|
char *progression = "LRCP";
|
||||||
OPJ_PROG_ORDER prog_order;
|
OPJ_PROG_ORDER prog_order;
|
||||||
char *cinema_mode = "no";
|
char *cinema_mode = "no";
|
||||||
OPJ_CINEMA_MODE cine_mode;
|
OPJ_CINEMA_MODE cine_mode;
|
||||||
int fd = -1;
|
Py_ssize_t fd = -1;
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "ss|OOOsOIOOOssi", &mode, &format,
|
if (!PyArg_ParseTuple(args, "ss|OOOsOIOOOssi", &mode, &format,
|
||||||
&offset, &tile_offset, &tile_size,
|
&offset, &tile_offset, &tile_size,
|
||||||
|
|
|
@ -592,6 +592,22 @@ i2f(UINT8* out_, const UINT8* in_, int xsize)
|
||||||
*out++ = (FLOAT32) *in++;
|
*out++ = (FLOAT32) *in++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
i2rgb(UINT8* out, const UINT8* in_, int xsize)
|
||||||
|
{
|
||||||
|
int x;
|
||||||
|
INT32* in = (INT32*) in_;
|
||||||
|
for (x = 0; x < xsize; x++, in++, out+=4) {
|
||||||
|
if (*in <= 0)
|
||||||
|
out[0] = out[1] = out[2] = 0;
|
||||||
|
else if (*in >= 255)
|
||||||
|
out[0] = out[1] = out[2] = 255;
|
||||||
|
else
|
||||||
|
out[0] = out[1] = out[2] = (UINT8) *in;
|
||||||
|
out[3] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ------------- */
|
/* ------------- */
|
||||||
/* F conversions */
|
/* F conversions */
|
||||||
/* ------------- */
|
/* ------------- */
|
||||||
|
@ -807,11 +823,14 @@ static struct {
|
||||||
|
|
||||||
{ "La", "LA", la2lA },
|
{ "La", "LA", la2lA },
|
||||||
|
|
||||||
{ "I", "L", i2l },
|
{ "I", "L", i2l },
|
||||||
{ "I", "F", i2f },
|
{ "I", "F", i2f },
|
||||||
|
{ "I", "RGB", i2rgb },
|
||||||
|
{ "I", "RGBA", i2rgb },
|
||||||
|
{ "I", "RGBX", i2rgb },
|
||||||
|
|
||||||
{ "F", "L", f2l },
|
{ "F", "L", f2l },
|
||||||
{ "F", "I", f2i },
|
{ "F", "I", f2i },
|
||||||
|
|
||||||
{ "RGB", "1", rgb2bit },
|
{ "RGB", "1", rgb2bit },
|
||||||
{ "RGB", "L", rgb2l },
|
{ "RGB", "L", rgb2l },
|
||||||
|
@ -1385,6 +1404,8 @@ ImagingConvertTransparent(Imaging imIn, const char *mode,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!((strcmp(imIn->mode, "RGB") == 0 ||
|
if (!((strcmp(imIn->mode, "RGB") == 0 ||
|
||||||
|
strcmp(imIn->mode, "1") == 0 ||
|
||||||
|
strcmp(imIn->mode, "I") == 0 ||
|
||||||
strcmp(imIn->mode, "L") == 0)
|
strcmp(imIn->mode, "L") == 0)
|
||||||
&& strcmp(mode, "RGBA") == 0))
|
&& strcmp(mode, "RGBA") == 0))
|
||||||
#ifdef notdef
|
#ifdef notdef
|
||||||
|
@ -1403,7 +1424,13 @@ ImagingConvertTransparent(Imaging imIn, const char *mode,
|
||||||
if (strcmp(imIn->mode, "RGB") == 0) {
|
if (strcmp(imIn->mode, "RGB") == 0) {
|
||||||
convert = rgb2rgba;
|
convert = rgb2rgba;
|
||||||
} else {
|
} else {
|
||||||
convert = l2rgb;
|
if (strcmp(imIn->mode, "1") == 0) {
|
||||||
|
convert = bit2rgb;
|
||||||
|
} else if (strcmp(imIn->mode, "I") == 0) {
|
||||||
|
convert = i2rgb;
|
||||||
|
} else {
|
||||||
|
convert = l2rgb;
|
||||||
|
}
|
||||||
g = b = r;
|
g = b = r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,11 @@ ImagingFlipLeftRight(Imaging imOut, Imaging imIn)
|
||||||
ImagingSectionEnter(&cookie);
|
ImagingSectionEnter(&cookie);
|
||||||
|
|
||||||
if (imIn->image8) {
|
if (imIn->image8) {
|
||||||
FLIP_LEFT_RIGHT(UINT8, image8)
|
if (strncmp(imIn->mode, "I;16", 4) == 0) {
|
||||||
|
FLIP_LEFT_RIGHT(UINT16, image8)
|
||||||
|
} else {
|
||||||
|
FLIP_LEFT_RIGHT(UINT8, image8)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
FLIP_LEFT_RIGHT(INT32, image32)
|
FLIP_LEFT_RIGHT(INT32, image32)
|
||||||
}
|
}
|
||||||
|
@ -104,7 +108,8 @@ ImagingRotate90(Imaging imOut, Imaging imIn)
|
||||||
INT* in = imIn->image[yyy]; \
|
INT* in = imIn->image[yyy]; \
|
||||||
xr = imIn->xsize - 1 - xx; \
|
xr = imIn->xsize - 1 - xx; \
|
||||||
for (xxx = xx; xxx < xxxsize; xxx++, xr--) { \
|
for (xxx = xx; xxx < xxxsize; xxx++, xr--) { \
|
||||||
imOut->image[xr][yyy] = in[xxx]; \
|
INT* out = imOut->image[xr]; \
|
||||||
|
out[yyy] = in[xxx]; \
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
|
@ -114,10 +119,15 @@ ImagingRotate90(Imaging imOut, Imaging imIn)
|
||||||
|
|
||||||
ImagingSectionEnter(&cookie);
|
ImagingSectionEnter(&cookie);
|
||||||
|
|
||||||
if (imIn->image8)
|
if (imIn->image8) {
|
||||||
ROTATE_90(UINT8, image8)
|
if (strncmp(imIn->mode, "I;16", 4) == 0) {
|
||||||
else
|
ROTATE_90(UINT16, image8);
|
||||||
ROTATE_90(INT32, image32)
|
} else {
|
||||||
|
ROTATE_90(UINT8, image8);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ROTATE_90(INT32, image32);
|
||||||
|
}
|
||||||
|
|
||||||
ImagingSectionLeave(&cookie);
|
ImagingSectionLeave(&cookie);
|
||||||
|
|
||||||
|
@ -153,7 +163,8 @@ ImagingTranspose(Imaging imOut, Imaging imIn)
|
||||||
for (yyy = yy; yyy < yyysize; yyy++) { \
|
for (yyy = yy; yyy < yyysize; yyy++) { \
|
||||||
INT* in = imIn->image[yyy]; \
|
INT* in = imIn->image[yyy]; \
|
||||||
for (xxx = xx; xxx < xxxsize; xxx++) { \
|
for (xxx = xx; xxx < xxxsize; xxx++) { \
|
||||||
imOut->image[xxx][yyy] = in[xxx]; \
|
INT* out = imOut->image[xxx]; \
|
||||||
|
out[yyy] = in[xxx]; \
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
|
@ -163,10 +174,15 @@ ImagingTranspose(Imaging imOut, Imaging imIn)
|
||||||
|
|
||||||
ImagingSectionEnter(&cookie);
|
ImagingSectionEnter(&cookie);
|
||||||
|
|
||||||
if (imIn->image8)
|
if (imIn->image8) {
|
||||||
TRANSPOSE(UINT8, image8)
|
if (strncmp(imIn->mode, "I;16", 4) == 0) {
|
||||||
else
|
TRANSPOSE(UINT16, image8);
|
||||||
TRANSPOSE(INT32, image32)
|
} else {
|
||||||
|
TRANSPOSE(UINT8, image8);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TRANSPOSE(INT32, image32);
|
||||||
|
}
|
||||||
|
|
||||||
ImagingSectionLeave(&cookie);
|
ImagingSectionLeave(&cookie);
|
||||||
|
|
||||||
|
@ -204,7 +220,8 @@ ImagingTransverse(Imaging imOut, Imaging imIn)
|
||||||
INT* in = imIn->image[yyy]; \
|
INT* in = imIn->image[yyy]; \
|
||||||
xr = imIn->xsize - 1 - xx; \
|
xr = imIn->xsize - 1 - xx; \
|
||||||
for (xxx = xx; xxx < xxxsize; xxx++, xr--) { \
|
for (xxx = xx; xxx < xxxsize; xxx++, xr--) { \
|
||||||
imOut->image[xr][yr] = in[xxx]; \
|
INT* out = imOut->image[xr]; \
|
||||||
|
out[yr] = in[xxx]; \
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
|
@ -214,10 +231,15 @@ ImagingTransverse(Imaging imOut, Imaging imIn)
|
||||||
|
|
||||||
ImagingSectionEnter(&cookie);
|
ImagingSectionEnter(&cookie);
|
||||||
|
|
||||||
if (imIn->image8)
|
if (imIn->image8) {
|
||||||
TRANSVERSE(UINT8, image8)
|
if (strncmp(imIn->mode, "I;16", 4) == 0) {
|
||||||
else
|
TRANSVERSE(UINT16, image8);
|
||||||
TRANSVERSE(INT32, image32)
|
} else {
|
||||||
|
TRANSVERSE(UINT8, image8);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TRANSVERSE(INT32, image32);
|
||||||
|
}
|
||||||
|
|
||||||
ImagingSectionLeave(&cookie);
|
ImagingSectionLeave(&cookie);
|
||||||
|
|
||||||
|
@ -253,7 +275,11 @@ ImagingRotate180(Imaging imOut, Imaging imIn)
|
||||||
|
|
||||||
yr = imIn->ysize-1;
|
yr = imIn->ysize-1;
|
||||||
if (imIn->image8) {
|
if (imIn->image8) {
|
||||||
ROTATE_180(UINT8, image8)
|
if (strncmp(imIn->mode, "I;16", 4) == 0) {
|
||||||
|
ROTATE_180(UINT16, image8)
|
||||||
|
} else {
|
||||||
|
ROTATE_180(UINT8, image8)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ROTATE_180(INT32, image32)
|
ROTATE_180(INT32, image32)
|
||||||
}
|
}
|
||||||
|
@ -293,7 +319,8 @@ ImagingRotate270(Imaging imOut, Imaging imIn)
|
||||||
for (yyy = yy; yyy < yyysize; yyy++, yr--) { \
|
for (yyy = yy; yyy < yyysize; yyy++, yr--) { \
|
||||||
INT* in = imIn->image[yyy]; \
|
INT* in = imIn->image[yyy]; \
|
||||||
for (xxx = xx; xxx < xxxsize; xxx++) { \
|
for (xxx = xx; xxx < xxxsize; xxx++) { \
|
||||||
imOut->image[xxx][yr] = in[xxx]; \
|
INT* out = imOut->image[xxx]; \
|
||||||
|
out[yr] = in[xxx]; \
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
|
@ -303,10 +330,15 @@ ImagingRotate270(Imaging imOut, Imaging imIn)
|
||||||
|
|
||||||
ImagingSectionEnter(&cookie);
|
ImagingSectionEnter(&cookie);
|
||||||
|
|
||||||
if (imIn->image8)
|
if (imIn->image8) {
|
||||||
ROTATE_270(UINT8, image8)
|
if (strncmp(imIn->mode, "I;16", 4) == 0) {
|
||||||
else
|
ROTATE_270(UINT16, image8);
|
||||||
ROTATE_270(INT32, image32)
|
} else {
|
||||||
|
ROTATE_270(UINT8, image8);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ROTATE_270(INT32, image32);
|
||||||
|
}
|
||||||
|
|
||||||
ImagingSectionLeave(&cookie);
|
ImagingSectionLeave(&cookie);
|
||||||
|
|
||||||
|
|
|
@ -639,6 +639,7 @@ static struct {
|
||||||
|
|
||||||
/* storage modes */
|
/* storage modes */
|
||||||
{"I;16", "I;16", 16, copy2},
|
{"I;16", "I;16", 16, copy2},
|
||||||
|
{"I;16", "I;16B", 16, packI16N_I16B},
|
||||||
{"I;16B", "I;16B", 16, copy2},
|
{"I;16B", "I;16B", 16, copy2},
|
||||||
{"I;16L", "I;16L", 16, copy2},
|
{"I;16L", "I;16L", 16, copy2},
|
||||||
{"I;16", "I;16N", 16, packI16N_I16}, // LibTiff native->image endian.
|
{"I;16", "I;16N", 16, packI16N_I16}, // LibTiff native->image endian.
|
||||||
|
|
|
@ -35,8 +35,6 @@ struct _HashTable {
|
||||||
uint32_t count;
|
uint32_t count;
|
||||||
HashFunc hashFunc;
|
HashFunc hashFunc;
|
||||||
HashCmpFunc cmpFunc;
|
HashCmpFunc cmpFunc;
|
||||||
KeyDestroyFunc keyDestroyFunc;
|
|
||||||
ValDestroyFunc valDestroyFunc;
|
|
||||||
void *userData;
|
void *userData;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -51,8 +49,6 @@ HashTable *hashtable_new(HashFunc hf,HashCmpFunc cf) {
|
||||||
if (!h) { return NULL; }
|
if (!h) { return NULL; }
|
||||||
h->hashFunc=hf;
|
h->hashFunc=hf;
|
||||||
h->cmpFunc=cf;
|
h->cmpFunc=cf;
|
||||||
h->keyDestroyFunc=NULL;
|
|
||||||
h->valDestroyFunc=NULL;
|
|
||||||
h->length=MIN_LENGTH;
|
h->length=MIN_LENGTH;
|
||||||
h->count=0;
|
h->count=0;
|
||||||
h->userData=NULL;
|
h->userData=NULL;
|
||||||
|
@ -62,15 +58,6 @@ HashTable *hashtable_new(HashFunc hf,HashCmpFunc cf) {
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _hashtable_destroy(const HashTable *h,const HashKey_t key,const HashVal_t val,void *u) {
|
|
||||||
if (h->keyDestroyFunc) {
|
|
||||||
h->keyDestroyFunc(h,key);
|
|
||||||
}
|
|
||||||
if (h->valDestroyFunc) {
|
|
||||||
h->valDestroyFunc(h,val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t _findPrime(uint32_t start,int dir) {
|
static uint32_t _findPrime(uint32_t start,int dir) {
|
||||||
static int unit[]={0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,0};
|
static int unit[]={0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,0};
|
||||||
uint32_t t;
|
uint32_t t;
|
||||||
|
@ -144,12 +131,6 @@ static int _hashtable_insert_node(HashTable *h,HashNode *node,int resize,int upd
|
||||||
free(node);
|
free(node);
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
if (h->valDestroyFunc) {
|
|
||||||
h->valDestroyFunc(h,nv->value);
|
|
||||||
}
|
|
||||||
if (h->keyDestroyFunc) {
|
|
||||||
h->keyDestroyFunc(h,nv->key);
|
|
||||||
}
|
|
||||||
nv->key=node->key;
|
nv->key=node->key;
|
||||||
nv->value=node->value;
|
nv->value=node->value;
|
||||||
free(node);
|
free(node);
|
||||||
|
@ -180,7 +161,6 @@ static int _hashtable_insert(HashTable *h,HashKey_t key,HashVal_t val,int resize
|
||||||
nv=*n;
|
nv=*n;
|
||||||
i=h->cmpFunc(h,nv->key,key);
|
i=h->cmpFunc(h,nv->key,key);
|
||||||
if (!i) {
|
if (!i) {
|
||||||
if (h->valDestroyFunc) { h->valDestroyFunc(h,nv->value); }
|
|
||||||
nv->value=val;
|
nv->value=val;
|
||||||
return 1;
|
return 1;
|
||||||
} else if (i>0) {
|
} else if (i>0) {
|
||||||
|
@ -202,34 +182,6 @@ static int _hashtable_insert(HashTable *h,HashKey_t key,HashVal_t val,int resize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _hashtable_lookup_or_insert(HashTable *h,HashKey_t key,HashVal_t *retVal,HashVal_t newVal,int resize) {
|
|
||||||
HashNode **n,*nv;
|
|
||||||
HashNode *t;
|
|
||||||
int i;
|
|
||||||
uint32_t hash=h->hashFunc(h,key)%h->length;
|
|
||||||
|
|
||||||
for (n=&(h->table[hash]);*n;n=&((*n)->next)) {
|
|
||||||
nv=*n;
|
|
||||||
i=h->cmpFunc(h,nv->key,key);
|
|
||||||
if (!i) {
|
|
||||||
*retVal=nv->value;
|
|
||||||
return 1;
|
|
||||||
} else if (i>0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t=malloc(sizeof(HashNode));
|
|
||||||
if (!t) return 0;
|
|
||||||
t->next=*n;
|
|
||||||
*n=t;
|
|
||||||
t->key=key;
|
|
||||||
t->value=newVal;
|
|
||||||
*retVal=newVal;
|
|
||||||
h->count++;
|
|
||||||
if (resize) _hashtable_resize(h);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int hashtable_insert_or_update_computed(HashTable *h,
|
int hashtable_insert_or_update_computed(HashTable *h,
|
||||||
HashKey_t key,
|
HashKey_t key,
|
||||||
ComputeFunc newFunc,
|
ComputeFunc newFunc,
|
||||||
|
@ -243,14 +195,8 @@ int hashtable_insert_or_update_computed(HashTable *h,
|
||||||
nv=*n;
|
nv=*n;
|
||||||
i=h->cmpFunc(h,nv->key,key);
|
i=h->cmpFunc(h,nv->key,key);
|
||||||
if (!i) {
|
if (!i) {
|
||||||
HashVal_t old=nv->value;
|
|
||||||
if (existsFunc) {
|
if (existsFunc) {
|
||||||
existsFunc(h,nv->key,&(nv->value));
|
existsFunc(h,nv->key,&(nv->value));
|
||||||
if (nv->value!=old) {
|
|
||||||
if (h->valDestroyFunc) {
|
|
||||||
h->valDestroyFunc(h,old);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -275,10 +221,6 @@ int hashtable_insert_or_update_computed(HashTable *h,
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int hashtable_update(HashTable *h,HashKey_t key,HashVal_t val) {
|
|
||||||
return _hashtable_insert(h,key,val,1,0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int hashtable_insert(HashTable *h,HashKey_t key,HashVal_t val) {
|
int hashtable_insert(HashTable *h,HashKey_t key,HashVal_t val) {
|
||||||
return _hashtable_insert(h,key,val,1,0);
|
return _hashtable_insert(h,key,val,1,0);
|
||||||
}
|
}
|
||||||
|
@ -314,9 +256,6 @@ void hashtable_free(HashTable *h) {
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
|
|
||||||
if (h->table) {
|
if (h->table) {
|
||||||
if (h->keyDestroyFunc || h->keyDestroyFunc) {
|
|
||||||
hashtable_foreach(h,_hashtable_destroy,NULL);
|
|
||||||
}
|
|
||||||
for (i=0;i<h->length;i++) {
|
for (i=0;i<h->length;i++) {
|
||||||
for (n=h->table[i];n;n=nn) {
|
for (n=h->table[i];n;n=nn) {
|
||||||
nn=n->next;
|
nn=n->next;
|
||||||
|
@ -328,84 +267,10 @@ void hashtable_free(HashTable *h) {
|
||||||
free(h);
|
free(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
ValDestroyFunc hashtable_set_value_destroy_func(HashTable *h,ValDestroyFunc d) {
|
|
||||||
ValDestroyFunc r=h->valDestroyFunc;
|
|
||||||
h->valDestroyFunc=d;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
KeyDestroyFunc hashtable_set_key_destroy_func(HashTable *h,KeyDestroyFunc d) {
|
|
||||||
KeyDestroyFunc r=h->keyDestroyFunc;
|
|
||||||
h->keyDestroyFunc=d;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _hashtable_remove(HashTable *h,
|
|
||||||
const HashKey_t key,
|
|
||||||
HashKey_t *keyRet,
|
|
||||||
HashVal_t *valRet,
|
|
||||||
int resize) {
|
|
||||||
uint32_t hash=h->hashFunc(h,key)%h->length;
|
|
||||||
HashNode *n,*p;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (p=NULL,n=h->table[hash];n;p=n,n=n->next) {
|
|
||||||
i=h->cmpFunc(h,n->key,key);
|
|
||||||
if (!i) {
|
|
||||||
if (p) p=n->next; else h->table[hash]=n->next;
|
|
||||||
*keyRet=n->key;
|
|
||||||
*valRet=n->value;
|
|
||||||
free(n);
|
|
||||||
h->count++;
|
|
||||||
return 1;
|
|
||||||
} else if (i>0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _hashtable_delete(HashTable *h,const HashKey_t key,int resize) {
|
|
||||||
uint32_t hash=h->hashFunc(h,key)%h->length;
|
|
||||||
HashNode *n,*p;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (p=NULL,n=h->table[hash];n;p=n,n=n->next) {
|
|
||||||
i=h->cmpFunc(h,n->key,key);
|
|
||||||
if (!i) {
|
|
||||||
if (p) p=n->next; else h->table[hash]=n->next;
|
|
||||||
if (h->valDestroyFunc) { h->valDestroyFunc(h,n->value); }
|
|
||||||
if (h->keyDestroyFunc) { h->keyDestroyFunc(h,n->key); }
|
|
||||||
free(n);
|
|
||||||
h->count++;
|
|
||||||
return 1;
|
|
||||||
} else if (i>0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int hashtable_remove(HashTable *h,const HashKey_t key,HashKey_t *keyRet,HashVal_t *valRet) {
|
|
||||||
return _hashtable_remove(h,key,keyRet,valRet,1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int hashtable_delete(HashTable *h,const HashKey_t key) {
|
|
||||||
return _hashtable_delete(h,key,1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void hashtable_rehash_compute(HashTable *h,CollisionFunc cf) {
|
void hashtable_rehash_compute(HashTable *h,CollisionFunc cf) {
|
||||||
_hashtable_rehash(h,cf,h->length);
|
_hashtable_rehash(h,cf,h->length);
|
||||||
}
|
}
|
||||||
|
|
||||||
void hashtable_rehash(HashTable *h) {
|
|
||||||
_hashtable_rehash(h,NULL,h->length);
|
|
||||||
}
|
|
||||||
|
|
||||||
int hashtable_lookup_or_insert(HashTable *h,HashKey_t key,HashVal_t *valp,HashVal_t val) {
|
|
||||||
return _hashtable_lookup_or_insert(h,key,valp,val,1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int hashtable_lookup(const HashTable *h,const HashKey_t key,HashVal_t *valp) {
|
int hashtable_lookup(const HashTable *h,const HashKey_t key,HashVal_t *valp) {
|
||||||
uint32_t hash=h->hashFunc(h,key)%h->length;
|
uint32_t hash=h->hashFunc(h,key)%h->length;
|
||||||
HashNode *n;
|
HashNode *n;
|
||||||
|
|
|
@ -22,8 +22,6 @@ typedef uint32_t (*HashFunc)(const HashTable *,const HashKey_t);
|
||||||
typedef int (*HashCmpFunc)(const HashTable *,const HashKey_t,const HashKey_t);
|
typedef int (*HashCmpFunc)(const HashTable *,const HashKey_t,const HashKey_t);
|
||||||
typedef void (*IteratorFunc)(const HashTable *,const HashKey_t,const HashVal_t,void *);
|
typedef void (*IteratorFunc)(const HashTable *,const HashKey_t,const HashVal_t,void *);
|
||||||
typedef void (*IteratorUpdateFunc)(const HashTable *,const HashKey_t,HashVal_t *,void *);
|
typedef void (*IteratorUpdateFunc)(const HashTable *,const HashKey_t,HashVal_t *,void *);
|
||||||
typedef void (*KeyDestroyFunc)(const HashTable *,HashKey_t);
|
|
||||||
typedef void (*ValDestroyFunc)(const HashTable *,HashVal_t);
|
|
||||||
typedef void (*ComputeFunc)(const HashTable *,const HashKey_t,HashVal_t *);
|
typedef void (*ComputeFunc)(const HashTable *,const HashKey_t,HashVal_t *);
|
||||||
typedef void (*CollisionFunc)(const HashTable *,HashKey_t *,HashVal_t *,HashKey_t,HashVal_t);
|
typedef void (*CollisionFunc)(const HashTable *,HashKey_t *,HashVal_t *,HashKey_t,HashVal_t);
|
||||||
|
|
||||||
|
@ -32,18 +30,11 @@ void hashtable_free(HashTable *h);
|
||||||
void hashtable_foreach(HashTable *h,IteratorFunc i,void *u);
|
void hashtable_foreach(HashTable *h,IteratorFunc i,void *u);
|
||||||
void hashtable_foreach_update(HashTable *h,IteratorUpdateFunc i,void *u);
|
void hashtable_foreach_update(HashTable *h,IteratorUpdateFunc i,void *u);
|
||||||
int hashtable_insert(HashTable *h,HashKey_t key,HashVal_t val);
|
int hashtable_insert(HashTable *h,HashKey_t key,HashVal_t val);
|
||||||
int hashtable_update(HashTable *h,HashKey_t key,HashVal_t val);
|
|
||||||
int hashtable_lookup(const HashTable *h,const HashKey_t key,HashVal_t *valp);
|
int hashtable_lookup(const HashTable *h,const HashKey_t key,HashVal_t *valp);
|
||||||
int hashtable_lookup_or_insert(HashTable *h,HashKey_t key,HashVal_t *valp,HashVal_t val);
|
|
||||||
int hashtable_insert_or_update_computed(HashTable *h,HashKey_t key,ComputeFunc newFunc,ComputeFunc existsFunc);
|
int hashtable_insert_or_update_computed(HashTable *h,HashKey_t key,ComputeFunc newFunc,ComputeFunc existsFunc);
|
||||||
int hashtable_delete(HashTable *h,const HashKey_t key);
|
|
||||||
int hashtable_remove(HashTable *h,const HashKey_t key,HashKey_t *keyRet,HashVal_t *valRet);
|
|
||||||
void *hashtable_set_user_data(HashTable *h,void *data);
|
void *hashtable_set_user_data(HashTable *h,void *data);
|
||||||
void *hashtable_get_user_data(const HashTable *h);
|
void *hashtable_get_user_data(const HashTable *h);
|
||||||
KeyDestroyFunc hashtable_set_key_destroy_func(HashTable *,KeyDestroyFunc d);
|
|
||||||
ValDestroyFunc hashtable_set_value_destroy_func(HashTable *,ValDestroyFunc d);
|
|
||||||
uint32_t hashtable_get_count(const HashTable *h);
|
uint32_t hashtable_get_count(const HashTable *h);
|
||||||
void hashtable_rehash(HashTable *h);
|
|
||||||
void hashtable_rehash_compute(HashTable *h,CollisionFunc cf);
|
void hashtable_rehash_compute(HashTable *h,CollisionFunc cf);
|
||||||
|
|
||||||
#endif // __QUANTHASH_H__
|
#endif // __QUANTHASH_H__
|
||||||
|
|
|
@ -124,6 +124,7 @@ toff_t _tiffSizeProc(thandle_t hdata) {
|
||||||
|
|
||||||
return (toff_t)state->size;
|
return (toff_t)state->size;
|
||||||
}
|
}
|
||||||
|
|
||||||
int _tiffMapProc(thandle_t hdata, tdata_t* pbase, toff_t* psize) {
|
int _tiffMapProc(thandle_t hdata, tdata_t* pbase, toff_t* psize) {
|
||||||
TIFFSTATE *state = (TIFFSTATE *)hdata;
|
TIFFSTATE *state = (TIFFSTATE *)hdata;
|
||||||
|
|
||||||
|
@ -168,13 +169,117 @@ int ImagingLibTiffInit(ImagingCodecState state, int fp, int offset) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int ReadTile(TIFF* tiff, UINT32 col, UINT32 row, UINT32* buffer) {
|
||||||
|
uint16 photometric;
|
||||||
|
|
||||||
|
TIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &photometric);
|
||||||
|
|
||||||
|
// To avoid dealing with YCbCr subsampling, let libtiff handle it
|
||||||
|
if (photometric == PHOTOMETRIC_YCBCR) {
|
||||||
|
UINT32 tile_width, tile_height, swap_line_size, i_row;
|
||||||
|
UINT32* swap_line;
|
||||||
|
|
||||||
|
TIFFGetField(tiff, TIFFTAG_TILEWIDTH, &tile_width);
|
||||||
|
TIFFGetField(tiff, TIFFTAG_TILELENGTH, &tile_height);
|
||||||
|
|
||||||
|
swap_line_size = tile_width * sizeof(UINT32);
|
||||||
|
if (tile_width != swap_line_size / sizeof(UINT32)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read the tile into an RGBA array */
|
||||||
|
if (!TIFFReadRGBATile(tiff, col, row, buffer)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
swap_line = (UINT32*)malloc(swap_line_size);
|
||||||
|
/*
|
||||||
|
* For some reason the TIFFReadRGBATile() function chooses the
|
||||||
|
* lower left corner as the origin. Vertically mirror scanlines.
|
||||||
|
*/
|
||||||
|
for(i_row = 0; i_row < tile_height / 2; i_row++) {
|
||||||
|
UINT32 *top_line, *bottom_line;
|
||||||
|
|
||||||
|
top_line = buffer + tile_width * i_row;
|
||||||
|
bottom_line = buffer + tile_width * (tile_height - i_row - 1);
|
||||||
|
|
||||||
|
memcpy(swap_line, top_line, 4*tile_width);
|
||||||
|
memcpy(top_line, bottom_line, 4*tile_width);
|
||||||
|
memcpy(bottom_line, swap_line, 4*tile_width);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(swap_line);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TIFFReadTile(tiff, (tdata_t)buffer, col, row, 0, 0) == -1) {
|
||||||
|
TRACE(("Decode Error, Tile at %dx%d\n", col, row));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE(("Successfully read tile at %dx%d; \n\n", col, row));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ReadStrip(TIFF* tiff, UINT32 row, UINT32* buffer) {
|
||||||
|
uint16 photometric;
|
||||||
|
TIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &photometric);
|
||||||
|
|
||||||
|
// To avoid dealing with YCbCr subsampling, let libtiff handle it
|
||||||
|
if (photometric == PHOTOMETRIC_YCBCR) {
|
||||||
|
TIFFRGBAImage img;
|
||||||
|
char emsg[1024] = "";
|
||||||
|
UINT32 rows_per_strip, rows_to_read;
|
||||||
|
int ok;
|
||||||
|
|
||||||
|
|
||||||
|
TIFFGetFieldDefaulted(tiff, TIFFTAG_ROWSPERSTRIP, &rows_per_strip);
|
||||||
|
if ((row % rows_per_strip) != 0) {
|
||||||
|
TRACE(("Row passed to ReadStrip() must be first in a strip."));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TIFFRGBAImageOK(tiff, emsg) && TIFFRGBAImageBegin(&img, tiff, 0, emsg)) {
|
||||||
|
TRACE(("Initialized RGBAImage\n"));
|
||||||
|
|
||||||
|
img.req_orientation = ORIENTATION_TOPLEFT;
|
||||||
|
img.row_offset = row;
|
||||||
|
img.col_offset = 0;
|
||||||
|
|
||||||
|
rows_to_read = min(rows_per_strip, img.height - row);
|
||||||
|
|
||||||
|
TRACE(("rows to read: %d\n", rows_to_read));
|
||||||
|
ok = TIFFRGBAImageGet(&img, buffer, img.width, rows_to_read);
|
||||||
|
|
||||||
|
TIFFRGBAImageEnd(&img);
|
||||||
|
} else {
|
||||||
|
ok = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ok == 0) {
|
||||||
|
TRACE(("Decode Error, row %d; msg: %s\n", row, emsg));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TIFFReadEncodedStrip(tiff, TIFFComputeStrip(tiff, row, 0), (tdata_t)buffer, -1) == -1) {
|
||||||
|
TRACE(("Decode Error, strip %d\n", TIFFComputeStrip(tiff, row, 0)));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes) {
|
int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes) {
|
||||||
TIFFSTATE *clientstate = (TIFFSTATE *)state->context;
|
TIFFSTATE *clientstate = (TIFFSTATE *)state->context;
|
||||||
char *filename = "tempfile.tif";
|
char *filename = "tempfile.tif";
|
||||||
char *mode = "r";
|
char *mode = "r";
|
||||||
TIFF *tiff;
|
TIFF *tiff;
|
||||||
uint16 photometric = 0, compression;
|
|
||||||
|
|
||||||
|
|
||||||
/* buffer is the encoded file, bytes is the length of the encoded file */
|
/* buffer is the encoded file, bytes is the length of the encoded file */
|
||||||
/* it all ends up in state->buffer, which is a uint8* from Imaging.h */
|
/* it all ends up in state->buffer, which is a uint8* from Imaging.h */
|
||||||
|
@ -235,19 +340,17 @@ int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TIFFGetFieldDefaulted(tiff, TIFFTAG_COMPRESSION, &compression);
|
|
||||||
TIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &photometric);
|
|
||||||
if (compression == COMPRESSION_JPEG && photometric == PHOTOMETRIC_YCBCR) {
|
|
||||||
/* Set pseudo-tag to force automatic YCbCr->RGB conversion */
|
|
||||||
TIFFSetField(tiff, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TIFFIsTiled(tiff)) {
|
if (TIFFIsTiled(tiff)) {
|
||||||
uint32 x, y, tile_y;
|
UINT32 x, y, tile_y, row_byte_size;
|
||||||
uint32 tileWidth, tileLength;
|
UINT32 tile_width, tile_length, current_tile_width;
|
||||||
UINT8 *new_data;
|
UINT8 *new_data;
|
||||||
|
|
||||||
state->bytes = TIFFTileSize(tiff);
|
TIFFGetField(tiff, TIFFTAG_TILEWIDTH, &tile_width);
|
||||||
|
TIFFGetField(tiff, TIFFTAG_TILELENGTH, &tile_length);
|
||||||
|
|
||||||
|
// We could use TIFFTileSize, but for YCbCr data it returns subsampled data size
|
||||||
|
row_byte_size = (tile_width * state->bits + 7) / 8;
|
||||||
|
state->bytes = row_byte_size * tile_length;
|
||||||
|
|
||||||
/* overflow check for malloc */
|
/* overflow check for malloc */
|
||||||
if (state->bytes > INT_MAX - 1) {
|
if (state->bytes > INT_MAX - 1) {
|
||||||
|
@ -268,12 +371,9 @@ int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int
|
||||||
|
|
||||||
TRACE(("TIFFTileSize: %d\n", state->bytes));
|
TRACE(("TIFFTileSize: %d\n", state->bytes));
|
||||||
|
|
||||||
TIFFGetField(tiff, TIFFTAG_TILEWIDTH, &tileWidth);
|
for (y = state->yoff; y < state->ysize; y += tile_length) {
|
||||||
TIFFGetField(tiff, TIFFTAG_TILELENGTH, &tileLength);
|
for (x = state->xoff; x < state->xsize; x += tile_width) {
|
||||||
|
if (ReadTile(tiff, x, y, (UINT32*) state->buffer) == -1) {
|
||||||
for (y = state->yoff; y < state->ysize; y += tileLength) {
|
|
||||||
for (x = state->xoff; x < state->xsize; x += tileWidth) {
|
|
||||||
if (TIFFReadTile(tiff, (tdata_t)state->buffer, x, y, 0, 0) == -1) {
|
|
||||||
TRACE(("Decode Error, Tile at %dx%d\n", x, y));
|
TRACE(("Decode Error, Tile at %dx%d\n", x, y));
|
||||||
state->errcode = IMAGING_CODEC_BROKEN;
|
state->errcode = IMAGING_CODEC_BROKEN;
|
||||||
TIFFClose(tiff);
|
TIFFClose(tiff);
|
||||||
|
@ -282,53 +382,68 @@ int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int
|
||||||
|
|
||||||
TRACE(("Read tile at %dx%d; \n\n", x, y));
|
TRACE(("Read tile at %dx%d; \n\n", x, y));
|
||||||
|
|
||||||
|
current_tile_width = min(tile_width, state->xsize - x);
|
||||||
|
|
||||||
// iterate over each line in the tile and stuff data into image
|
// iterate over each line in the tile and stuff data into image
|
||||||
for (tile_y = 0; tile_y < min(tileLength, state->ysize - y); tile_y++) {
|
for (tile_y = 0; tile_y < min(tile_length, state->ysize - y); tile_y++) {
|
||||||
|
TRACE(("Writing tile data at %dx%d using tile_width: %d; \n", tile_y + y, x, current_tile_width));
|
||||||
|
|
||||||
TRACE(("Writing tile data at %dx%d using tilwWidth: %d; \n", tile_y + y, x, min(tileWidth, state->xsize - x)));
|
// UINT8 * bbb = state->buffer + tile_y * row_byte_size;
|
||||||
|
|
||||||
// UINT8 * bbb = state->buffer + tile_y * (state->bytes / tileLength);
|
|
||||||
// TRACE(("chars: %x%x%x%x\n", ((UINT8 *)bbb)[0], ((UINT8 *)bbb)[1], ((UINT8 *)bbb)[2], ((UINT8 *)bbb)[3]));
|
// TRACE(("chars: %x%x%x%x\n", ((UINT8 *)bbb)[0], ((UINT8 *)bbb)[1], ((UINT8 *)bbb)[2], ((UINT8 *)bbb)[3]));
|
||||||
|
|
||||||
state->shuffle((UINT8*) im->image[tile_y + y] + x * im->pixelsize,
|
state->shuffle((UINT8*) im->image[tile_y + y] + x * im->pixelsize,
|
||||||
state->buffer + tile_y * (state->bytes / tileLength),
|
state->buffer + tile_y * row_byte_size,
|
||||||
min(tileWidth, state->xsize - x)
|
current_tile_width
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
tsize_t size;
|
UINT32 strip_row, row_byte_size;
|
||||||
|
UINT8 *new_data;
|
||||||
|
UINT32 rows_per_strip;
|
||||||
|
|
||||||
size = TIFFScanlineSize(tiff);
|
TIFFGetField(tiff, TIFFTAG_ROWSPERSTRIP, &rows_per_strip);
|
||||||
TRACE(("ScanlineSize: %lu \n", size));
|
TRACE(("RowsPerStrip: %u \n", rows_per_strip));
|
||||||
if (size > state->bytes) {
|
|
||||||
TRACE(("Error, scanline size > buffer size\n"));
|
// We could use TIFFStripSize, but for YCbCr data it returns subsampled data size
|
||||||
state->errcode = IMAGING_CODEC_BROKEN;
|
row_byte_size = (state->xsize * state->bits + 7) / 8;
|
||||||
|
state->bytes = rows_per_strip * row_byte_size;
|
||||||
|
|
||||||
|
TRACE(("StripSize: %d \n", state->bytes));
|
||||||
|
|
||||||
|
/* realloc to fit whole strip */
|
||||||
|
new_data = realloc (state->buffer, state->bytes);
|
||||||
|
if (!new_data) {
|
||||||
|
state->errcode = IMAGING_CODEC_MEMORY;
|
||||||
TIFFClose(tiff);
|
TIFFClose(tiff);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Have to do this row by row and shove stuff into the buffer that way,
|
state->buffer = new_data;
|
||||||
// with shuffle. (or, just alloc a buffer myself, then figure out how to get it
|
|
||||||
// back in. Can't use read encoded stripe.
|
|
||||||
|
|
||||||
// This thing pretty much requires that I have the whole image in one shot.
|
for (; state->y < state->ysize; state->y += rows_per_strip) {
|
||||||
// Perhaps a stub version would work better???
|
if (ReadStrip(tiff, state->y, (UINT32 *)state->buffer) == -1) {
|
||||||
while(state->y < state->ysize){
|
TRACE(("Decode Error, strip %d\n", TIFFComputeStrip(tiff, state->y, 0)));
|
||||||
if (TIFFReadScanline(tiff, (tdata_t)state->buffer, (uint32)state->y, 0) == -1) {
|
|
||||||
TRACE(("Decode Error, row %d\n", state->y));
|
|
||||||
state->errcode = IMAGING_CODEC_BROKEN;
|
state->errcode = IMAGING_CODEC_BROKEN;
|
||||||
TIFFClose(tiff);
|
TIFFClose(tiff);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
/* TRACE(("Decoded row %d \n", state->y)); */
|
|
||||||
state->shuffle((UINT8*) im->image[state->y + state->yoff] +
|
|
||||||
state->xoff * im->pixelsize,
|
|
||||||
state->buffer,
|
|
||||||
state->xsize);
|
|
||||||
|
|
||||||
state->y++;
|
TRACE(("Decoded strip for row %d \n", state->y));
|
||||||
|
|
||||||
|
// iterate over each row in the strip and stuff data into image
|
||||||
|
for (strip_row = 0; strip_row < min(rows_per_strip, state->ysize - state->y); strip_row++) {
|
||||||
|
TRACE(("Writing data into line %d ; \n", state->y + strip_row));
|
||||||
|
|
||||||
|
// UINT8 * bbb = state->buffer + strip_row * (state->bytes / rows_per_strip);
|
||||||
|
// TRACE(("chars: %x %x %x %x\n", ((UINT8 *)bbb)[0], ((UINT8 *)bbb)[1], ((UINT8 *)bbb)[2], ((UINT8 *)bbb)[3]));
|
||||||
|
|
||||||
|
state->shuffle((UINT8*) im->image[state->y + state->yoff + strip_row] +
|
||||||
|
state->xoff * im->pixelsize,
|
||||||
|
state->buffer + strip_row * row_byte_size,
|
||||||
|
state->xsize);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
curl -fsSL -o pypy2.zip https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.0.0-win32.zip
|
curl -fsSL -o pypy2.zip https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.1.0-win32.zip
|
||||||
7z x pypy2.zip -oc:\
|
7z x pypy2.zip -oc:\
|
||||||
c:\Python37\Scripts\virtualenv.exe -p c:\pypy2.7-v7.0.0-win32\pypy.exe c:\vp\pypy2
|
c:\Python37\Scripts\virtualenv.exe -p c:\pypy2.7-v7.1.0-win32\pypy.exe c:\vp\pypy2
|
||||||
|
|
|
@ -33,9 +33,9 @@ libs = {
|
||||||
'dir': 'tiff-4.0.10',
|
'dir': 'tiff-4.0.10',
|
||||||
},
|
},
|
||||||
'freetype': {
|
'freetype': {
|
||||||
'url': 'https://download.savannah.gnu.org/releases/freetype/freetype-2.9.1.tar.gz', # noqa: E501
|
'url': 'https://download.savannah.gnu.org/releases/freetype/freetype-2.10.0.tar.gz', # noqa: E501
|
||||||
'filename': PILLOW_DEPENDS_DIR + 'freetype-2.9.1.tar.gz',
|
'filename': PILLOW_DEPENDS_DIR + 'freetype-2.10.0.tar.gz',
|
||||||
'dir': 'freetype-2.9.1',
|
'dir': 'freetype-2.10.0',
|
||||||
},
|
},
|
||||||
'lcms': {
|
'lcms': {
|
||||||
'url': SF_MIRROR+'/project/lcms/lcms/2.7/lcms2-2.7.zip',
|
'url': SF_MIRROR+'/project/lcms/lcms/2.7/lcms2-2.7.zip',
|
||||||
|
|
|
@ -9,7 +9,11 @@ def fetch(url):
|
||||||
|
|
||||||
if not os.path.exists(name):
|
if not os.path.exists(name):
|
||||||
print("Fetching", url)
|
print("Fetching", url)
|
||||||
content = urllib.request.urlopen(url).read()
|
try:
|
||||||
|
r = urllib.request.urlopen(url)
|
||||||
|
except urllib.error.URLError:
|
||||||
|
r = urllib.request.urlopen(url)
|
||||||
|
content = r.read()
|
||||||
with open(name, 'wb') as fd:
|
with open(name, 'wb') as fd:
|
||||||
fd.write(content)
|
fd.write(content)
|
||||||
return name
|
return name
|
||||||
|
|