Merge branch 'master' into add-width-to-shapes
18
CHANGES.rst
|
@ -2,6 +2,24 @@
|
|||
Changelog (Pillow)
|
||||
==================
|
||||
|
||||
5.3.0 (unreleased)
|
||||
------------------
|
||||
|
||||
- Add three-color support to ImageOps.colorize #3242
|
||||
[tsennott]
|
||||
|
||||
- Tests: Add LA to TGA test modes #3222
|
||||
[danpla]
|
||||
|
||||
- Skip outline if the draw operation fills with the same colour #2922
|
||||
[radarhere]
|
||||
|
||||
- Flake8 fixes #3173
|
||||
[radarhere]
|
||||
|
||||
- Avoid deprecated 'U' mode when opening files #2187
|
||||
[jdufresne]
|
||||
|
||||
5.2.0 (2018-07-01)
|
||||
------------------
|
||||
|
||||
|
|
21
RELEASING.md
|
@ -4,7 +4,7 @@
|
|||
|
||||
Released quarterly on the first day of January, April, July, October.
|
||||
|
||||
* [ ] Open a release ticket e.g. https://github.com/python-pillow/Pillow/issues/1174
|
||||
* [ ] Open a release ticket e.g. https://github.com/python-pillow/Pillow/issues/3154
|
||||
* [ ] Develop and prepare release in ``master`` branch.
|
||||
* [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) and [AppVeyor CI](https://ci.appveyor.com/project/python-pillow/Pillow) to confirm passing tests in ``master`` branch.
|
||||
* [ ] Check that all of the wheel builds [Pillow Wheel Builder](https://github.com/python-pillow/pillow-wheels) pass the tests in TravisCI.
|
||||
|
@ -13,8 +13,8 @@ Released quarterly on the first day of January, April, July, October.
|
|||
* [ ] Run pre-release check via `make release-test` in a freshly cloned repo.
|
||||
* [ ] Create branch and tag for release e.g.:
|
||||
```
|
||||
$ git branch 2.9.x
|
||||
$ git tag 2.9.0
|
||||
$ git branch 5.2.x
|
||||
$ git tag 5.2.0
|
||||
$ git push --all
|
||||
$ git push --tags
|
||||
```
|
||||
|
@ -23,8 +23,7 @@ Released quarterly on the first day of January, April, July, October.
|
|||
$ make sdist
|
||||
```
|
||||
* [ ] Create [binary distributions](#binary-distributions)
|
||||
* [ ] Upload all binaries and source distributions with ``twine upload dist/Pillow-4.1.0-*``
|
||||
* [ ] Manually hide old versions on PyPI such that only the latest major release is visible when viewing https://pypi.org/project/Pillow/ (https://pypi.org/manage/project/Pillow/releases/)
|
||||
* [ ] Upload all binaries and source distributions e.g. ``twine upload dist/Pillow-5.2.0-*``
|
||||
|
||||
## Point Release
|
||||
|
||||
|
@ -32,17 +31,17 @@ Released as needed for security, installation or critical bug fixes.
|
|||
|
||||
* [ ] Make necessary changes in ``master`` branch.
|
||||
* [ ] Update `CHANGES.rst`.
|
||||
* [ ] Cherry pick individual commits from ``master`` branch to release branch e.g. ``2.9.x``.
|
||||
* [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) to confirm passing tests in release branch e.g. ``2.9.x``.
|
||||
* [ ] Cherry pick individual commits from ``master`` branch to release branch e.g. ``5.2.x``.
|
||||
* [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) to confirm passing tests in release branch e.g. ``5.2.x``.
|
||||
* [ ] Checkout release branch e.g.:
|
||||
```
|
||||
git checkout -t remotes/origin/2.9.x
|
||||
git checkout -t remotes/origin/5.2.x
|
||||
```
|
||||
* [ ] In compliance with https://www.python.org/dev/peps/pep-0440/, update version identifier in `src/PIL/_version.py`
|
||||
* [ ] Run pre-release check via `make release-test`.
|
||||
* [ ] Create tag for release e.g.:
|
||||
```
|
||||
$ git tag 2.9.1
|
||||
$ git tag 5.2.1
|
||||
$ git push --tags
|
||||
```
|
||||
* [ ] Create source distributions e.g.:
|
||||
|
@ -99,8 +98,8 @@ Released as needed privately to individual vendors for critical security-related
|
|||
|
||||
## Publicize Release
|
||||
|
||||
* [ ] Announce release availability via [Twitter](https://twitter.com/pythonpillow) e.g. https://twitter.com/aclark4life/status/583366798302691328.
|
||||
* [ ] Announce release availability via [Twitter](https://twitter.com/pythonpillow) e.g. https://twitter.com/PythonPillow/status/1013789184354603010
|
||||
|
||||
## Documentation
|
||||
|
||||
* [ ] Make sure the default version for Read the Docs is the latest release version, e.g. ``3.1.x`` rather than ``latest``: https://readthedocs.org/projects/pillow/versions/
|
||||
* [ ] Make sure the default version for Read the Docs is the latest release version, i.e. ``5.2.0`` rather than ``latest`` e.g. https://pillow.readthedocs.io/en/5.2.x/
|
||||
|
|
|
@ -153,7 +153,8 @@ class PillowTestCase(unittest.TestCase):
|
|||
pass
|
||||
raise e
|
||||
|
||||
def assert_image_similar_tofile(self, a, filename, epsilon, msg=None, mode=None):
|
||||
def assert_image_similar_tofile(self, a, filename, epsilon, msg=None,
|
||||
mode=None):
|
||||
with Image.open(filename) as img:
|
||||
if mode:
|
||||
img = img.convert(mode)
|
||||
|
@ -191,6 +192,16 @@ class PillowTestCase(unittest.TestCase):
|
|||
def assert_not_all_same(self, items, msg=None):
|
||||
self.assertFalse(items.count(items[0]) == len(items), msg)
|
||||
|
||||
def assert_tuple_approx_equal(self, actuals, targets, threshold, msg):
|
||||
"""Tests if actuals has values within threshold from targets"""
|
||||
|
||||
value = True
|
||||
for i, target in enumerate(targets):
|
||||
value *= (target - threshold <= actuals[i] <= target + threshold)
|
||||
|
||||
self.assertTrue(value,
|
||||
msg + ': ' + repr(actuals) + ' != ' + repr(targets))
|
||||
|
||||
def skipKnownBadTest(self, msg=None, platform=None,
|
||||
travis=None, interpreter=None):
|
||||
# Skip if platform/travis matches, and
|
||||
|
@ -246,7 +257,8 @@ class PillowLeakTestCase(PillowTestCase):
|
|||
mem = getrusage(RUSAGE_SELF).ru_maxrss
|
||||
if sys.platform == 'darwin':
|
||||
# man 2 getrusage:
|
||||
# ru_maxrss the maximum resident set size utilized (in bytes).
|
||||
# ru_maxrss
|
||||
# This is the maximum resident set size utilized (in bytes).
|
||||
return mem / 1024 # Kb
|
||||
else:
|
||||
# linux
|
||||
|
|
BIN
Tests/images/bw_gradient.png
Normal file
After Width: | Height: | Size: 102 B |
BIN
Tests/images/imagedraw_outline_chord_L.png
Normal file
After Width: | Height: | Size: 212 B |
BIN
Tests/images/imagedraw_outline_chord_RGB.png
Normal file
After Width: | Height: | Size: 259 B |
BIN
Tests/images/imagedraw_outline_ellipse_L.png
Normal file
After Width: | Height: | Size: 290 B |
BIN
Tests/images/imagedraw_outline_ellipse_RGB.png
Normal file
After Width: | Height: | Size: 378 B |
BIN
Tests/images/imagedraw_outline_pieslice_L.png
Normal file
After Width: | Height: | Size: 266 B |
BIN
Tests/images/imagedraw_outline_pieslice_RGB.png
Normal file
After Width: | Height: | Size: 340 B |
BIN
Tests/images/imagedraw_outline_polygon_L.png
Normal file
After Width: | Height: | Size: 309 B |
BIN
Tests/images/imagedraw_outline_polygon_RGB.png
Normal file
After Width: | Height: | Size: 393 B |
BIN
Tests/images/imagedraw_outline_rectangle_L.png
Normal file
After Width: | Height: | Size: 125 B |
BIN
Tests/images/imagedraw_outline_rectangle_RGB.png
Normal file
After Width: | Height: | Size: 212 B |
BIN
Tests/images/imagedraw_outline_shape_L.png
Normal file
After Width: | Height: | Size: 263 B |
BIN
Tests/images/imagedraw_outline_shape_RGB.png
Normal file
After Width: | Height: | Size: 316 B |
|
@ -27,8 +27,8 @@ class TestColorLut3DCoreAPI(PillowTestCase):
|
|||
g / float(size2D-1) if size2D != 1 else 0,
|
||||
][:channels]
|
||||
for b in range(size3D)
|
||||
for g in range(size2D)
|
||||
for r in range(size1D)
|
||||
for g in range(size2D)
|
||||
for r in range(size1D)
|
||||
]
|
||||
return (
|
||||
channels, size1D, size2D, size3D,
|
||||
|
@ -38,112 +38,123 @@ class TestColorLut3DCoreAPI(PillowTestCase):
|
|||
im = Image.new('RGB', (10, 10), 0)
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "filter"):
|
||||
im.im.color_lut_3d('RGB', Image.CUBIC,
|
||||
*self.generate_identity_table(3, 3))
|
||||
im.im.color_lut_3d('RGB',
|
||||
Image.CUBIC,
|
||||
*self.generate_identity_table(3, 3))
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "image mode"):
|
||||
im.im.color_lut_3d('wrong', Image.LINEAR,
|
||||
*self.generate_identity_table(3, 3))
|
||||
im.im.color_lut_3d('wrong',
|
||||
Image.LINEAR,
|
||||
*self.generate_identity_table(3, 3))
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "table_channels"):
|
||||
im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
*self.generate_identity_table(5, 3))
|
||||
im.im.color_lut_3d('RGB',
|
||||
Image.LINEAR,
|
||||
*self.generate_identity_table(5, 3))
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "table_channels"):
|
||||
im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
*self.generate_identity_table(1, 3))
|
||||
im.im.color_lut_3d('RGB',
|
||||
Image.LINEAR,
|
||||
*self.generate_identity_table(1, 3))
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "table_channels"):
|
||||
im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
*self.generate_identity_table(2, 3))
|
||||
im.im.color_lut_3d('RGB',
|
||||
Image.LINEAR,
|
||||
*self.generate_identity_table(2, 3))
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "Table size"):
|
||||
im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
*self.generate_identity_table(3, (1, 3, 3)))
|
||||
im.im.color_lut_3d('RGB',
|
||||
Image.LINEAR,
|
||||
*self.generate_identity_table(3, (1, 3, 3)))
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "Table size"):
|
||||
im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
*self.generate_identity_table(3, (66, 3, 3)))
|
||||
im.im.color_lut_3d('RGB',
|
||||
Image.LINEAR,
|
||||
*self.generate_identity_table(3, (66, 3, 3)))
|
||||
|
||||
with self.assertRaisesRegex(ValueError, r"size1D \* size2D \* size3D"):
|
||||
im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
3, 2, 2, 2, [0, 0, 0] * 7)
|
||||
im.im.color_lut_3d('RGB',
|
||||
Image.LINEAR,
|
||||
3, 2, 2, 2, [0, 0, 0] * 7)
|
||||
|
||||
with self.assertRaisesRegex(ValueError, r"size1D \* size2D \* size3D"):
|
||||
im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
3, 2, 2, 2, [0, 0, 0] * 9)
|
||||
im.im.color_lut_3d('RGB',
|
||||
Image.LINEAR,
|
||||
3, 2, 2, 2, [0, 0, 0] * 9)
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
3, 2, 2, 2, [0, 0, "0"] * 8)
|
||||
im.im.color_lut_3d('RGB',
|
||||
Image.LINEAR,
|
||||
3, 2, 2, 2, [0, 0, "0"] * 8)
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
3, 2, 2, 2, 16)
|
||||
im.im.color_lut_3d('RGB',
|
||||
Image.LINEAR,
|
||||
3, 2, 2, 2, 16)
|
||||
|
||||
def test_correct_args(self):
|
||||
im = Image.new('RGB', (10, 10), 0)
|
||||
|
||||
im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
*self.generate_identity_table(3, 3))
|
||||
*self.generate_identity_table(3, 3))
|
||||
|
||||
im.im.color_lut_3d('CMYK', Image.LINEAR,
|
||||
*self.generate_identity_table(4, 3))
|
||||
*self.generate_identity_table(4, 3))
|
||||
|
||||
im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
*self.generate_identity_table(3, (2, 3, 3)))
|
||||
*self.generate_identity_table(3, (2, 3, 3)))
|
||||
|
||||
im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
*self.generate_identity_table(3, (65, 3, 3)))
|
||||
*self.generate_identity_table(3, (65, 3, 3)))
|
||||
|
||||
im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
*self.generate_identity_table(3, (3, 65, 3)))
|
||||
*self.generate_identity_table(3, (3, 65, 3)))
|
||||
|
||||
im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
*self.generate_identity_table(3, (3, 3, 65)))
|
||||
*self.generate_identity_table(3, (3, 3, 65)))
|
||||
|
||||
def test_wrong_mode(self):
|
||||
with self.assertRaisesRegex(ValueError, "wrong mode"):
|
||||
im = Image.new('L', (10, 10), 0)
|
||||
im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
*self.generate_identity_table(3, 3))
|
||||
*self.generate_identity_table(3, 3))
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "wrong mode"):
|
||||
im = Image.new('RGB', (10, 10), 0)
|
||||
im.im.color_lut_3d('L', Image.LINEAR,
|
||||
*self.generate_identity_table(3, 3))
|
||||
*self.generate_identity_table(3, 3))
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "wrong mode"):
|
||||
im = Image.new('L', (10, 10), 0)
|
||||
im.im.color_lut_3d('L', Image.LINEAR,
|
||||
*self.generate_identity_table(3, 3))
|
||||
*self.generate_identity_table(3, 3))
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "wrong mode"):
|
||||
im = Image.new('RGB', (10, 10), 0)
|
||||
im.im.color_lut_3d('RGBA', Image.LINEAR,
|
||||
*self.generate_identity_table(3, 3))
|
||||
*self.generate_identity_table(3, 3))
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "wrong mode"):
|
||||
im = Image.new('RGB', (10, 10), 0)
|
||||
im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
*self.generate_identity_table(4, 3))
|
||||
*self.generate_identity_table(4, 3))
|
||||
|
||||
def test_correct_mode(self):
|
||||
im = Image.new('RGBA', (10, 10), 0)
|
||||
im.im.color_lut_3d('RGBA', Image.LINEAR,
|
||||
*self.generate_identity_table(3, 3))
|
||||
*self.generate_identity_table(3, 3))
|
||||
|
||||
im = Image.new('RGBA', (10, 10), 0)
|
||||
im.im.color_lut_3d('RGBA', Image.LINEAR,
|
||||
*self.generate_identity_table(4, 3))
|
||||
*self.generate_identity_table(4, 3))
|
||||
|
||||
im = Image.new('RGB', (10, 10), 0)
|
||||
im.im.color_lut_3d('HSV', Image.LINEAR,
|
||||
*self.generate_identity_table(3, 3))
|
||||
*self.generate_identity_table(3, 3))
|
||||
|
||||
im = Image.new('RGB', (10, 10), 0)
|
||||
im.im.color_lut_3d('RGBA', Image.LINEAR,
|
||||
*self.generate_identity_table(4, 3))
|
||||
*self.generate_identity_table(4, 3))
|
||||
|
||||
def test_identities(self):
|
||||
g = Image.linear_gradient('L')
|
||||
|
@ -154,12 +165,12 @@ class TestColorLut3DCoreAPI(PillowTestCase):
|
|||
for size in [2, 3, 5, 7, 11, 16, 17]:
|
||||
self.assert_image_equal(im, im._new(
|
||||
im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
*self.generate_identity_table(3, size))))
|
||||
*self.generate_identity_table(3, size))))
|
||||
|
||||
# Not so fast
|
||||
self.assert_image_equal(im, im._new(
|
||||
im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
*self.generate_identity_table(3, (2, 2, 65)))))
|
||||
*self.generate_identity_table(3, (2, 2, 65)))))
|
||||
|
||||
def test_identities_4_channels(self):
|
||||
g = Image.linear_gradient('L')
|
||||
|
@ -170,7 +181,7 @@ class TestColorLut3DCoreAPI(PillowTestCase):
|
|||
self.assert_image_equal(
|
||||
Image.merge('RGBA', (im.split()*2)[:4]),
|
||||
im._new(im.im.color_lut_3d('RGBA', Image.LINEAR,
|
||||
*self.generate_identity_table(4, 17))))
|
||||
*self.generate_identity_table(4, 17))))
|
||||
|
||||
def test_copy_alpha_channel(self):
|
||||
g = Image.linear_gradient('L')
|
||||
|
@ -180,7 +191,7 @@ class TestColorLut3DCoreAPI(PillowTestCase):
|
|||
|
||||
self.assert_image_equal(im, im._new(
|
||||
im.im.color_lut_3d('RGBA', Image.LINEAR,
|
||||
*self.generate_identity_table(3, 17))))
|
||||
*self.generate_identity_table(3, 17))))
|
||||
|
||||
def test_channels_order(self):
|
||||
g = Image.linear_gradient('L')
|
||||
|
@ -191,13 +202,13 @@ class TestColorLut3DCoreAPI(PillowTestCase):
|
|||
self.assert_image_equal(
|
||||
Image.merge('RGB', im.split()[::-1]),
|
||||
im._new(im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
3, 2, 2, 2, [
|
||||
0, 0, 0, 0, 0, 1,
|
||||
0, 1, 0, 0, 1, 1,
|
||||
3, 2, 2, 2, [
|
||||
0, 0, 0, 0, 0, 1,
|
||||
0, 1, 0, 0, 1, 1,
|
||||
|
||||
1, 0, 0, 1, 0, 1,
|
||||
1, 1, 0, 1, 1, 1,
|
||||
])))
|
||||
1, 0, 0, 1, 0, 1,
|
||||
1, 1, 0, 1, 1, 1,
|
||||
])))
|
||||
|
||||
def test_overflow(self):
|
||||
g = Image.linear_gradient('L')
|
||||
|
@ -205,14 +216,14 @@ class TestColorLut3DCoreAPI(PillowTestCase):
|
|||
g.transpose(Image.ROTATE_180)])
|
||||
|
||||
transformed = im._new(im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
3, 2, 2, 2,
|
||||
[
|
||||
-1, -1, -1, 2, -1, -1,
|
||||
-1, 2, -1, 2, 2, -1,
|
||||
3, 2, 2, 2,
|
||||
[
|
||||
-1, -1, -1, 2, -1, -1,
|
||||
-1, 2, -1, 2, 2, -1,
|
||||
|
||||
-1, -1, 2, 2, -1, 2,
|
||||
-1, 2, 2, 2, 2, 2,
|
||||
])).load()
|
||||
-1, -1, 2, 2, -1, 2,
|
||||
-1, 2, 2, 2, 2, 2,
|
||||
])).load()
|
||||
self.assertEqual(transformed[0, 0], (0, 0, 255))
|
||||
self.assertEqual(transformed[50, 50], (0, 0, 255))
|
||||
self.assertEqual(transformed[255, 0], (0, 255, 255))
|
||||
|
@ -223,14 +234,14 @@ class TestColorLut3DCoreAPI(PillowTestCase):
|
|||
self.assertEqual(transformed[205, 205], (255, 255, 0))
|
||||
|
||||
transformed = im._new(im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
3, 2, 2, 2,
|
||||
[
|
||||
-3, -3, -3, 5, -3, -3,
|
||||
-3, 5, -3, 5, 5, -3,
|
||||
3, 2, 2, 2,
|
||||
[
|
||||
-3, -3, -3, 5, -3, -3,
|
||||
-3, 5, -3, 5, 5, -3,
|
||||
|
||||
-3, -3, 5, 5, -3, 5,
|
||||
-3, 5, 5, 5, 5, 5,
|
||||
])).load()
|
||||
-3, -3, 5, 5, -3, 5,
|
||||
-3, 5, 5, 5, 5, 5,
|
||||
])).load()
|
||||
self.assertEqual(transformed[0, 0], (0, 0, 255))
|
||||
self.assertEqual(transformed[50, 50], (0, 0, 255))
|
||||
self.assertEqual(transformed[255, 0], (0, 255, 255))
|
||||
|
@ -366,22 +377,23 @@ class TestColorLut3DFilter(PillowTestCase):
|
|||
lut = ImageFilter.Color3DLUT(
|
||||
(3, 4, 5), array('f', [0, 0, 0, 0] * (3 * 4 * 5)),
|
||||
channels=4, target_mode='YCbCr', _copy_table=False)
|
||||
self.assertEqual(repr(lut),
|
||||
self.assertEqual(
|
||||
repr(lut),
|
||||
"<Color3DLUT from array size=3x4x5 channels=4 target_mode=YCbCr>")
|
||||
|
||||
|
||||
class TestGenerateColorLut3D(PillowTestCase):
|
||||
def test_wrong_channels_count(self):
|
||||
with self.assertRaisesRegex(ValueError, "3 or 4 output channels"):
|
||||
ImageFilter.Color3DLUT.generate(5, channels=2,
|
||||
callback=lambda r, g, b: (r, g, b))
|
||||
ImageFilter.Color3DLUT.generate(
|
||||
5, channels=2, callback=lambda r, g, b: (r, g, b))
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "should have either channels"):
|
||||
ImageFilter.Color3DLUT.generate(5, lambda r, g, b: (r, g, b, r))
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "should have either channels"):
|
||||
ImageFilter.Color3DLUT.generate(5, channels=4,
|
||||
callback=lambda r, g, b: (r, g, b))
|
||||
ImageFilter.Color3DLUT.generate(
|
||||
5, channels=4, callback=lambda r, g, b: (r, g, b))
|
||||
|
||||
def test_3_channels(self):
|
||||
lut = ImageFilter.Color3DLUT.generate(5, lambda r, g, b: (r, g, b))
|
||||
|
@ -392,13 +404,14 @@ class TestGenerateColorLut3D(PillowTestCase):
|
|||
1.0, 0.0, 0.0, 0.0, 0.25, 0.0, 0.25, 0.25, 0.0, 0.5, 0.25, 0.0])
|
||||
|
||||
def test_4_channels(self):
|
||||
lut = ImageFilter.Color3DLUT.generate(5, channels=4,
|
||||
callback=lambda r, g, b: (b, r, g, (r+g+b) / 2))
|
||||
lut = ImageFilter.Color3DLUT.generate(
|
||||
5, channels=4, callback=lambda r, g, b: (b, r, g, (r+g+b) / 2))
|
||||
self.assertEqual(tuple(lut.size), (5, 5, 5))
|
||||
self.assertEqual(lut.name, "Color 3D LUT")
|
||||
self.assertEqual(lut.table[:24], [
|
||||
0.0, 0.0, 0.0, 0.0, 0.0, 0.25, 0.0, 0.125, 0.0, 0.5, 0.0, 0.25,
|
||||
0.0, 0.75, 0.0, 0.375, 0.0, 1.0, 0.0, 0.5, 0.0, 0.0, 0.25, 0.125])
|
||||
0.0, 0.75, 0.0, 0.375, 0.0, 1.0, 0.0, 0.5, 0.0, 0.0, 0.25, 0.125
|
||||
])
|
||||
|
||||
def test_apply(self):
|
||||
lut = ImageFilter.Color3DLUT.generate(5, lambda r, g, b: (r, g, b))
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
from helper import unittest, PillowTestCase, hopper
|
||||
|
||||
from PIL import Image, EpsImagePlugin
|
||||
from PIL._util import py3
|
||||
import io
|
||||
|
||||
# Our two EPS test files (they are identical except for their bounding boxes)
|
||||
|
@ -196,41 +195,15 @@ class TestFileEps(PillowTestCase):
|
|||
self.assertEqual(t.readline().strip('\r\n'), 'baz', ending)
|
||||
self.assertEqual(t.readline().strip('\r\n'), 'bif', ending)
|
||||
|
||||
def _test_readline_stringio(self, test_string, ending):
|
||||
# check all the freaking line endings possible
|
||||
try:
|
||||
import StringIO
|
||||
except ImportError:
|
||||
# don't skip, it skips everything in the parent test
|
||||
return
|
||||
t = StringIO.StringIO(test_string)
|
||||
def _test_readline_io_psfile(self, test_string, ending):
|
||||
f = io.BytesIO(test_string.encode('latin-1'))
|
||||
t = EpsImagePlugin.PSFile(f)
|
||||
self._test_readline(t, ending)
|
||||
|
||||
def _test_readline_io(self, test_string, ending):
|
||||
if py3:
|
||||
t = io.StringIO(test_string)
|
||||
else:
|
||||
t = io.StringIO(unicode(test_string))
|
||||
self._test_readline(t, ending)
|
||||
|
||||
def _test_readline_file_universal(self, test_string, ending):
|
||||
f = self.tempfile('temp.txt')
|
||||
with open(f, 'wb') as w:
|
||||
if py3:
|
||||
w.write(test_string.encode('UTF-8'))
|
||||
else:
|
||||
w.write(test_string)
|
||||
|
||||
with open(f, 'rU') as t:
|
||||
self._test_readline(t, ending)
|
||||
|
||||
def _test_readline_file_psfile(self, test_string, ending):
|
||||
f = self.tempfile('temp.txt')
|
||||
with open(f, 'wb') as w:
|
||||
if py3:
|
||||
w.write(test_string.encode('UTF-8'))
|
||||
else:
|
||||
w.write(test_string)
|
||||
w.write(test_string.encode('latin-1'))
|
||||
|
||||
with open(f, 'rb') as r:
|
||||
t = EpsImagePlugin.PSFile(r)
|
||||
|
@ -239,29 +212,12 @@ class TestFileEps(PillowTestCase):
|
|||
def test_readline(self):
|
||||
# check all the freaking line endings possible from the spec
|
||||
# test_string = u'something\r\nelse\n\rbaz\rbif\n'
|
||||
line_endings = ['\r\n', '\n']
|
||||
not_working_endings = ['\n\r', '\r']
|
||||
line_endings = ['\r\n', '\n', '\n\r', '\r']
|
||||
strings = ['something', 'else', 'baz', 'bif']
|
||||
|
||||
for ending in line_endings:
|
||||
s = ending.join(strings)
|
||||
# Native Python versions will pass these endings.
|
||||
# self._test_readline_stringio(s, ending)
|
||||
# self._test_readline_io(s, ending)
|
||||
# self._test_readline_file_universal(s, ending)
|
||||
|
||||
self._test_readline_file_psfile(s, ending)
|
||||
|
||||
for ending in not_working_endings:
|
||||
# these only work with the PSFile, while they're in spec,
|
||||
# they're not likely to be used
|
||||
s = ending.join(strings)
|
||||
|
||||
# Native Python versions may fail on these endings.
|
||||
# self._test_readline_stringio(s, ending)
|
||||
# self._test_readline_io(s, ending)
|
||||
# self._test_readline_file_universal(s, ending)
|
||||
|
||||
self._test_readline_io_psfile(s, ending)
|
||||
self._test_readline_file_psfile(s, ending)
|
||||
|
||||
def test_open_eps(self):
|
||||
|
|
|
@ -402,7 +402,8 @@ class TestFileGif(PillowTestCase):
|
|||
|
||||
def test_comment(self):
|
||||
im = Image.open(TEST_GIF)
|
||||
self.assertEqual(im.info['comment'], b"File written by Adobe Photoshop\xa8 4.0")
|
||||
self.assertEqual(im.info['comment'],
|
||||
b"File written by Adobe Photoshop\xa8 4.0")
|
||||
|
||||
out = self.tempfile('temp.gif')
|
||||
im = Image.new('L', (100, 100), '#000')
|
||||
|
|
|
@ -364,7 +364,6 @@ class TestFileJpeg(PillowTestCase):
|
|||
with self.assertRaises(IOError):
|
||||
im.load()
|
||||
|
||||
|
||||
def _n_qtables_helper(self, n, test_file):
|
||||
im = Image.open(test_file)
|
||||
f = self.tempfile('temp.jpg')
|
||||
|
|
|
@ -31,7 +31,7 @@ class LibTiffTestCase(PillowTestCase):
|
|||
|
||||
try:
|
||||
self.assertEqual(im._compression, 'group4')
|
||||
except:
|
||||
except AttributeError:
|
||||
print("No _compression")
|
||||
print(dir(im))
|
||||
|
||||
|
@ -126,7 +126,8 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
im.tile[0][:3], ('tiff_adobe_deflate', (0, 0, 278, 374), 0))
|
||||
im.load()
|
||||
|
||||
self.assert_image_equal_tofile(im, 'Tests/images/tiff_adobe_deflate.png')
|
||||
self.assert_image_equal_tofile(im,
|
||||
'Tests/images/tiff_adobe_deflate.png')
|
||||
|
||||
def test_write_metadata(self):
|
||||
""" Test metadata writing through libtiff """
|
||||
|
@ -194,7 +195,7 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
for tag in im.tag_v2:
|
||||
try:
|
||||
del(core_items[tag])
|
||||
except:
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
# Type codes:
|
||||
|
@ -217,7 +218,8 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
if info.length == 0:
|
||||
new_ifd[tag] = tuple(values[info.type] for _ in range(3))
|
||||
else:
|
||||
new_ifd[tag] = tuple(values[info.type] for _ in range(info.length))
|
||||
new_ifd[tag] = tuple(values[info.type]
|
||||
for _ in range(info.length))
|
||||
|
||||
# Extra samples really doesn't make sense in this application.
|
||||
del(new_ifd[338])
|
||||
|
@ -525,7 +527,7 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
f.write(src.read())
|
||||
|
||||
im = Image.open(tmpfile)
|
||||
count = im.n_frames
|
||||
im.n_frames
|
||||
im.close()
|
||||
try:
|
||||
os.remove(tmpfile) # Windows PermissionError here!
|
||||
|
@ -578,10 +580,14 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
|
||||
self.assertEqual(im.mode, "RGBA")
|
||||
self.assertEqual(im.size, (100, 40))
|
||||
self.assertEqual(im.tile, [('tiff_lzw', (0, 0, 100, 40), 0, ('RGBa;16N', 'tiff_lzw', False))])
|
||||
self.assertEqual(
|
||||
im.tile,
|
||||
[('tiff_lzw', (0, 0, 100, 40), 0, ('RGBa;16N', 'tiff_lzw', False))]
|
||||
)
|
||||
im.load()
|
||||
|
||||
self.assert_image_equal_tofile(im, "Tests/images/tiff_16bit_RGBa_target.png")
|
||||
self.assert_image_equal_tofile(
|
||||
im, "Tests/images/tiff_16bit_RGBa_target.png")
|
||||
|
||||
def test_gimp_tiff(self):
|
||||
# Read TIFF JPEG images from GIMP [@PIL168]
|
||||
|
@ -607,7 +613,8 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
im = Image.open("Tests/images/copyleft.tiff")
|
||||
self.assertEqual(im.mode, 'RGB')
|
||||
|
||||
self.assert_image_equal_tofile(im, "Tests/images/copyleft.png", mode='RGB')
|
||||
self.assert_image_equal_tofile(im, "Tests/images/copyleft.png",
|
||||
mode='RGB')
|
||||
|
||||
def test_lzw(self):
|
||||
im = Image.open("Tests/images/hopper_lzw.tif")
|
||||
|
|
|
@ -20,7 +20,8 @@ class TestFilePdf(PillowTestCase):
|
|||
self.assertTrue(os.path.isfile(outfile))
|
||||
self.assertGreater(os.path.getsize(outfile), 0)
|
||||
with PdfParser.PdfParser(outfile) as pdf:
|
||||
if kwargs.get("append_images", False) or kwargs.get("append", False):
|
||||
if kwargs.get("append_images", False) or \
|
||||
kwargs.get("append", False):
|
||||
self.assertGreater(len(pdf.pages), 1)
|
||||
else:
|
||||
self.assertGreater(len(pdf.pages), 0)
|
||||
|
@ -116,7 +117,9 @@ class TestFilePdf(PillowTestCase):
|
|||
|
||||
def test_pdf_open(self):
|
||||
# fail on a buffer full of null bytes
|
||||
self.assertRaises(PdfParser.PdfFormatError, PdfParser.PdfParser, buf=bytearray(65536))
|
||||
self.assertRaises(
|
||||
PdfParser.PdfFormatError,
|
||||
PdfParser.PdfParser, buf=bytearray(65536))
|
||||
|
||||
# make an empty PDF object
|
||||
with PdfParser.PdfParser() as empty_pdf:
|
||||
|
@ -153,7 +156,10 @@ class TestFilePdf(PillowTestCase):
|
|||
im = hopper("RGB")
|
||||
temp_dir = tempfile.mkdtemp()
|
||||
try:
|
||||
self.assertRaises(IOError, im.save, os.path.join(temp_dir, "nonexistent.pdf"), append=True)
|
||||
self.assertRaises(IOError,
|
||||
im.save,
|
||||
os.path.join(temp_dir, "nonexistent.pdf"),
|
||||
append=True)
|
||||
finally:
|
||||
os.rmdir(temp_dir)
|
||||
|
||||
|
@ -204,7 +210,8 @@ class TestFilePdf(PillowTestCase):
|
|||
# append two images
|
||||
mode_CMYK = hopper("CMYK")
|
||||
mode_P = hopper("P")
|
||||
mode_CMYK.save(pdf_filename, append=True, save_all=True, append_images=[mode_P])
|
||||
mode_CMYK.save(pdf_filename,
|
||||
append=True, save_all=True, append_images=[mode_P])
|
||||
|
||||
# open the PDF again, check pages and info again
|
||||
with PdfParser.PdfParser(pdf_filename) as pdf:
|
||||
|
@ -219,7 +226,9 @@ class TestFilePdf(PillowTestCase):
|
|||
|
||||
def test_pdf_info(self):
|
||||
# make a PDF file
|
||||
pdf_filename = self.helper_save_as_pdf("RGB", title="title", author="author", subject="subject", keywords="keywords", creator="creator", producer="producer")
|
||||
pdf_filename = self.helper_save_as_pdf(
|
||||
"RGB", title="title", author="author", subject="subject",
|
||||
keywords="keywords", creator="creator", producer="producer")
|
||||
|
||||
# open it, check pages and info
|
||||
with PdfParser.PdfParser(pdf_filename) as pdf:
|
||||
|
|
|
@ -355,7 +355,8 @@ class TestFilePng(PillowTestCase):
|
|||
broken_crc_chunk_data = chunk_data[:-1] + b'q' # break CRC
|
||||
|
||||
image_data = HEAD + broken_crc_chunk_data + TAIL
|
||||
self.assertRaises(SyntaxError, PngImagePlugin.PngImageFile, BytesIO(image_data))
|
||||
self.assertRaises(SyntaxError, PngImagePlugin.PngImageFile,
|
||||
BytesIO(image_data))
|
||||
|
||||
ImageFile.LOAD_TRUNCATED_IMAGES = True
|
||||
try:
|
||||
|
@ -371,7 +372,8 @@ class TestFilePng(PillowTestCase):
|
|||
|
||||
ImageFile.LOAD_TRUNCATED_IMAGES = True
|
||||
try:
|
||||
self.assertRaises(SyntaxError, PngImagePlugin.PngImageFile, BytesIO(image_data))
|
||||
self.assertRaises(SyntaxError, PngImagePlugin.PngImageFile,
|
||||
BytesIO(image_data))
|
||||
finally:
|
||||
ImageFile.LOAD_TRUNCATED_IMAGES = False
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ _TGA_DIR_COMMON = os.path.join(_TGA_DIR, "common")
|
|||
|
||||
class TestFileTga(PillowTestCase):
|
||||
|
||||
_MODES = ("L", "P", "RGB", "RGBA")
|
||||
_MODES = ("L", "LA", "P", "RGB", "RGBA")
|
||||
_ORIGINS = ("tl", "bl")
|
||||
|
||||
_ORIGIN_TO_ORIENTATION = {
|
||||
|
|
|
@ -444,7 +444,8 @@ class TestFileTiff(PillowTestCase):
|
|||
for im in ims:
|
||||
yield im
|
||||
mp = io.BytesIO()
|
||||
im.save(mp, format="TIFF", save_all=True, append_images=imGenerator(ims))
|
||||
im.save(mp, format="TIFF", save_all=True,
|
||||
append_images=imGenerator(ims))
|
||||
|
||||
mp.seek(0, os.SEEK_SET)
|
||||
reread = Image.open(mp)
|
||||
|
|
|
@ -83,7 +83,8 @@ class TestFileWebpAnimation(PillowTestCase):
|
|||
|
||||
temp_file1 = self.tempfile("temp.webp")
|
||||
frame1.copy().save(temp_file1,
|
||||
save_all=True, append_images=[frame2], lossless=True)
|
||||
save_all=True, append_images=[frame2],
|
||||
lossless=True)
|
||||
check(temp_file1)
|
||||
|
||||
# Tests appending using a generator
|
||||
|
@ -92,7 +93,8 @@ class TestFileWebpAnimation(PillowTestCase):
|
|||
yield im
|
||||
temp_file2 = self.tempfile("temp_generator.webp")
|
||||
frame1.copy().save(temp_file2,
|
||||
save_all=True, append_images=imGenerator([frame2]), lossless=True)
|
||||
save_all=True, append_images=imGenerator([frame2]),
|
||||
lossless=True)
|
||||
check(temp_file2)
|
||||
|
||||
def test_timestamp_and_duration(self):
|
||||
|
|
|
@ -16,7 +16,8 @@ class TestTTypeFontLeak(PillowLeakTestCase):
|
|||
self._test_leak(lambda: draw.text((0, 0), "some text "*1024, # ~10k
|
||||
font=font, fill="black"))
|
||||
|
||||
@unittest.skipIf(not features.check('freetype2'), "Test requires freetype2")
|
||||
@unittest.skipIf(not features.check('freetype2'),
|
||||
"Test requires freetype2")
|
||||
def test_leak(self):
|
||||
ttype = ImageFont.truetype('Tests/fonts/FreeMono.ttf', 20)
|
||||
self._test_font(ttype)
|
||||
|
|
|
@ -294,7 +294,8 @@ int main(int argc, char* argv[])
|
|||
compiler = ccompiler.new_compiler()
|
||||
compiler.add_include_dir(sysconfig.get_python_inc())
|
||||
|
||||
libdir = sysconfig.get_config_var('LIBDIR') or sysconfig.get_python_inc().replace('include', 'libs')
|
||||
libdir = (sysconfig.get_config_var('LIBDIR') or
|
||||
sysconfig.get_python_inc().replace('include', 'libs'))
|
||||
print(libdir)
|
||||
compiler.add_library_dir(libdir)
|
||||
objects = compiler.compile(['embed_pil.c'])
|
||||
|
|
|
@ -15,9 +15,11 @@ class TestImageArray(PillowTestCase):
|
|||
self.assertEqual(test("L"), (3, (100, 128), '|u1', 12800))
|
||||
|
||||
# FIXME: wrong?
|
||||
self.assertEqual(test("I"), (3, (100, 128), Image._ENDIAN + 'i4', 51200))
|
||||
self.assertEqual(test("I"), (3, (100, 128),
|
||||
Image._ENDIAN + 'i4', 51200))
|
||||
# FIXME: wrong?
|
||||
self.assertEqual(test("F"), (3, (100, 128), Image._ENDIAN + 'f4', 51200))
|
||||
self.assertEqual(test("F"), (3, (100, 128),
|
||||
Image._ENDIAN + 'f4', 51200))
|
||||
|
||||
self.assertEqual(test("LA"), (3, (100, 128, 2), '|u1', 25600))
|
||||
self.assertEqual(test("RGB"), (3, (100, 128, 3), '|u1', 38400))
|
||||
|
|
|
@ -67,13 +67,13 @@ class TestImageConvert(PillowTestCase):
|
|||
|
||||
f = self.tempfile('temp.png')
|
||||
|
||||
l = im.convert('L')
|
||||
self.assertEqual(l.info['transparency'], 0) # undone
|
||||
l.save(f)
|
||||
im_l = im.convert('L')
|
||||
self.assertEqual(im_l.info['transparency'], 0) # undone
|
||||
im_l.save(f)
|
||||
|
||||
rgb = im.convert('RGB')
|
||||
self.assertEqual(rgb.info['transparency'], (0, 0, 0)) # undone
|
||||
rgb.save(f)
|
||||
im_rgb = im.convert('RGB')
|
||||
self.assertEqual(im_rgb.info['transparency'], (0, 0, 0)) # undone
|
||||
im_rgb.save(f)
|
||||
|
||||
# ref https://github.com/python-pillow/Pillow/issues/664
|
||||
|
||||
|
@ -83,12 +83,12 @@ class TestImageConvert(PillowTestCase):
|
|||
im.info['transparency'] = 128
|
||||
|
||||
# Act
|
||||
rgba = im.convert('RGBA')
|
||||
im_rgba = im.convert('RGBA')
|
||||
|
||||
# Assert
|
||||
self.assertNotIn('transparency', rgba.info)
|
||||
self.assertNotIn('transparency', im_rgba.info)
|
||||
# https://github.com/python-pillow/Pillow/issues/2702
|
||||
self.assertEqual(rgba.palette, None)
|
||||
self.assertEqual(im_rgba.palette, None)
|
||||
|
||||
def test_trns_l(self):
|
||||
im = hopper('L')
|
||||
|
@ -96,19 +96,20 @@ class TestImageConvert(PillowTestCase):
|
|||
|
||||
f = self.tempfile('temp.png')
|
||||
|
||||
rgb = im.convert('RGB')
|
||||
self.assertEqual(rgb.info['transparency'], (128, 128, 128)) # undone
|
||||
rgb.save(f)
|
||||
im_rgb = im.convert('RGB')
|
||||
self.assertEqual(im_rgb.info['transparency'],
|
||||
(128, 128, 128)) # undone
|
||||
im_rgb.save(f)
|
||||
|
||||
p = im.convert('P')
|
||||
self.assertIn('transparency', p.info)
|
||||
p.save(f)
|
||||
im_p = im.convert('P')
|
||||
self.assertIn('transparency', im_p.info)
|
||||
im_p.save(f)
|
||||
|
||||
p = self.assert_warning(
|
||||
im_p = self.assert_warning(
|
||||
UserWarning,
|
||||
im.convert, 'P', palette=Image.ADAPTIVE)
|
||||
self.assertNotIn('transparency', p.info)
|
||||
p.save(f)
|
||||
self.assertNotIn('transparency', im_p.info)
|
||||
im_p.save(f)
|
||||
|
||||
def test_trns_RGB(self):
|
||||
im = hopper('RGB')
|
||||
|
@ -116,23 +117,35 @@ class TestImageConvert(PillowTestCase):
|
|||
|
||||
f = self.tempfile('temp.png')
|
||||
|
||||
l = im.convert('L')
|
||||
self.assertEqual(l.info['transparency'], l.getpixel((0, 0))) # undone
|
||||
l.save(f)
|
||||
im_l = im.convert('L')
|
||||
self.assertEqual(im_l.info['transparency'],
|
||||
im_l.getpixel((0, 0))) # undone
|
||||
im_l.save(f)
|
||||
|
||||
p = im.convert('P')
|
||||
self.assertIn('transparency', p.info)
|
||||
p.save(f)
|
||||
im_p = im.convert('P')
|
||||
self.assertIn('transparency', im_p.info)
|
||||
im_p.save(f)
|
||||
|
||||
p = im.convert('RGBA')
|
||||
self.assertNotIn('transparency', p.info)
|
||||
p.save(f)
|
||||
im_rgba = im.convert('RGBA')
|
||||
self.assertNotIn('transparency', im_rgba.info)
|
||||
im_rgba.save(f)
|
||||
|
||||
p = self.assert_warning(
|
||||
im_p = self.assert_warning(
|
||||
UserWarning,
|
||||
im.convert, 'P', palette=Image.ADAPTIVE)
|
||||
self.assertNotIn('transparency', p.info)
|
||||
p.save(f)
|
||||
self.assertNotIn('transparency', im_p.info)
|
||||
im_p.save(f)
|
||||
|
||||
def test_gif_with_rgba_palette_to_p(self):
|
||||
# See https://github.com/python-pillow/Pillow/issues/2433
|
||||
im = Image.open('Tests/images/hopper.gif')
|
||||
im.info['transparency'] = 255
|
||||
im.load()
|
||||
self.assertEqual(im.palette.mode, 'RGBA')
|
||||
im_p = im.convert('P')
|
||||
|
||||
# Should not raise ValueError: unrecognized raw mode
|
||||
im_p.load()
|
||||
|
||||
def test_p_la(self):
|
||||
im = hopper('RGBA')
|
||||
|
@ -191,7 +204,8 @@ class TestImageConvert(PillowTestCase):
|
|||
if converted_im.mode == 'RGB':
|
||||
self.assert_image_similar(converted_im, target, 3)
|
||||
else:
|
||||
self.assert_image_similar(converted_im, target.getchannel(0), 1)
|
||||
self.assert_image_similar(converted_im,
|
||||
target.getchannel(0), 1)
|
||||
|
||||
matrix_convert('RGB')
|
||||
matrix_convert('L')
|
||||
|
|
|
@ -245,8 +245,8 @@ class CoreResampleAlphaCorrectTest(PillowTestCase):
|
|||
for y in range(i.size[1]):
|
||||
used_colors = {px[x, y][0] for x in range(i.size[0])}
|
||||
self.assertEqual(256, len(used_colors),
|
||||
'All colors should present in resized image. '
|
||||
'Only {} on {} line.'.format(len(used_colors), y))
|
||||
'All colors should present in resized image. '
|
||||
'Only {} on {} line.'.format(len(used_colors), y))
|
||||
|
||||
@unittest.skip("current implementation isn't precise enough")
|
||||
def test_levels_rgba(self):
|
||||
|
@ -288,10 +288,14 @@ class CoreResampleAlphaCorrectTest(PillowTestCase):
|
|||
def test_dirty_pixels_rgba(self):
|
||||
case = self.make_dirty_case('RGBA', (255, 255, 0, 128), (0, 0, 255, 0))
|
||||
self.run_dirty_case(case.resize((20, 20), Image.BOX), (255, 255, 0))
|
||||
self.run_dirty_case(case.resize((20, 20), Image.BILINEAR), (255, 255, 0))
|
||||
self.run_dirty_case(case.resize((20, 20), Image.HAMMING), (255, 255, 0))
|
||||
self.run_dirty_case(case.resize((20, 20), Image.BICUBIC), (255, 255, 0))
|
||||
self.run_dirty_case(case.resize((20, 20), Image.LANCZOS), (255, 255, 0))
|
||||
self.run_dirty_case(case.resize((20, 20), Image.BILINEAR),
|
||||
(255, 255, 0))
|
||||
self.run_dirty_case(case.resize((20, 20), Image.HAMMING),
|
||||
(255, 255, 0))
|
||||
self.run_dirty_case(case.resize((20, 20), Image.BICUBIC),
|
||||
(255, 255, 0))
|
||||
self.run_dirty_case(case.resize((20, 20), Image.LANCZOS),
|
||||
(255, 255, 0))
|
||||
|
||||
def test_dirty_pixels_la(self):
|
||||
case = self.make_dirty_case('LA', (255, 128), (0, 0))
|
||||
|
@ -367,23 +371,28 @@ class CoreResampleCoefficientsTest(PillowTestCase):
|
|||
im = Image.new('RGBA', (1280, 1280), (0x20, 0x40, 0x60, 0xff))
|
||||
histogram = im.resize((256, 256), Image.BICUBIC).histogram()
|
||||
|
||||
self.assertEqual(histogram[0x100 * 0 + 0x20], 0x10000) # first channel
|
||||
self.assertEqual(histogram[0x100 * 1 + 0x40], 0x10000) # second channel
|
||||
self.assertEqual(histogram[0x100 * 2 + 0x60], 0x10000) # third channel
|
||||
self.assertEqual(histogram[0x100 * 3 + 0xff], 0x10000) # fourth channel
|
||||
# first channel
|
||||
self.assertEqual(histogram[0x100 * 0 + 0x20], 0x10000)
|
||||
# second channel
|
||||
self.assertEqual(histogram[0x100 * 1 + 0x40], 0x10000)
|
||||
# third channel
|
||||
self.assertEqual(histogram[0x100 * 2 + 0x60], 0x10000)
|
||||
# fourth channel
|
||||
self.assertEqual(histogram[0x100 * 3 + 0xff], 0x10000)
|
||||
|
||||
|
||||
class CoreResampleBoxTest(PillowTestCase):
|
||||
def test_wrong_arguments(self):
|
||||
im = hopper()
|
||||
for resample in (Image.NEAREST, Image.BOX, Image.BILINEAR, Image.HAMMING,
|
||||
Image.BICUBIC, Image.LANCZOS):
|
||||
for resample in (Image.NEAREST, Image.BOX, Image.BILINEAR,
|
||||
Image.HAMMING, Image.BICUBIC, Image.LANCZOS):
|
||||
im.resize((32, 32), resample, (0, 0, im.width, im.height))
|
||||
im.resize((32, 32), resample, (20, 20, im.width, im.height))
|
||||
im.resize((32, 32), resample, (20, 20, 20, 100))
|
||||
im.resize((32, 32), resample, (20, 20, 100, 20))
|
||||
|
||||
with self.assertRaisesRegex(TypeError, "must be sequence of length 4"):
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
"must be sequence of length 4"):
|
||||
im.resize((32, 32), resample, (im.width, im.height))
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "can't be negative"):
|
||||
|
|
|
@ -39,15 +39,15 @@ class TestImagingCoreResize(PillowTestCase):
|
|||
self.assertEqual(r.im.bands, im.im.bands)
|
||||
|
||||
def test_reduce_filters(self):
|
||||
for f in [Image.NEAREST, Image.BOX, Image.BILINEAR, Image.HAMMING,
|
||||
Image.BICUBIC, Image.LANCZOS]:
|
||||
for f in [Image.NEAREST, Image.BOX, Image.BILINEAR,
|
||||
Image.HAMMING, Image.BICUBIC, Image.LANCZOS]:
|
||||
r = self.resize(hopper("RGB"), (15, 12), f)
|
||||
self.assertEqual(r.mode, "RGB")
|
||||
self.assertEqual(r.size, (15, 12))
|
||||
|
||||
def test_enlarge_filters(self):
|
||||
for f in [Image.NEAREST, Image.BOX, Image.BILINEAR, Image.HAMMING,
|
||||
Image.BICUBIC, Image.LANCZOS]:
|
||||
for f in [Image.NEAREST, Image.BOX, Image.BILINEAR,
|
||||
Image.HAMMING, Image.BICUBIC, Image.LANCZOS]:
|
||||
r = self.resize(hopper("RGB"), (212, 195), f)
|
||||
self.assertEqual(r.mode, "RGB")
|
||||
self.assertEqual(r.size, (212, 195))
|
||||
|
@ -66,8 +66,8 @@ class TestImagingCoreResize(PillowTestCase):
|
|||
}
|
||||
samples['dirty'].putpixel((1, 1), 128)
|
||||
|
||||
for f in [Image.NEAREST, Image.BOX, Image.BILINEAR, Image.HAMMING,
|
||||
Image.BICUBIC, Image.LANCZOS]:
|
||||
for f in [Image.NEAREST, Image.BOX, Image.BILINEAR,
|
||||
Image.HAMMING, Image.BICUBIC, Image.LANCZOS]:
|
||||
# samples resized with current filter
|
||||
references = {
|
||||
name: self.resize(ch, (4, 4), f)
|
||||
|
@ -90,8 +90,8 @@ class TestImagingCoreResize(PillowTestCase):
|
|||
self.assert_image_equal(ch, references[channels[i]])
|
||||
|
||||
def test_enlarge_zero(self):
|
||||
for f in [Image.NEAREST, Image.BOX, Image.BILINEAR, Image.HAMMING,
|
||||
Image.BICUBIC, Image.LANCZOS]:
|
||||
for f in [Image.NEAREST, Image.BOX, Image.BILINEAR,
|
||||
Image.HAMMING, Image.BICUBIC, Image.LANCZOS]:
|
||||
r = self.resize(Image.new('RGB', (0, 0), "white"), (212, 195), f)
|
||||
self.assertEqual(r.mode, "RGB")
|
||||
self.assertEqual(r.size, (212, 195))
|
||||
|
|
|
@ -95,8 +95,9 @@ class TestImageRotate(PillowTestCase):
|
|||
im = hopper()
|
||||
self.rotate(im, im.mode, 45, center=(0, 0))
|
||||
self.rotate(im, im.mode, 45, translate=(im.size[0]/2, 0))
|
||||
self.rotate(im, im.mode, 45, center=(0, 0), translate=(im.size[0]/2, 0))
|
||||
|
||||
self.rotate(im, im.mode, 45, center=(0, 0),
|
||||
translate=(im.size[0]/2, 0))
|
||||
|
||||
def test_rotate_no_fill(self):
|
||||
im = Image.new('RGB', (100, 100), 'green')
|
||||
target = Image.open('Tests/images/rotate_45_no_fill.png')
|
||||
|
|
|
@ -43,8 +43,9 @@ class TestToQImage(PillowQtTestCase, PillowTestCase):
|
|||
if mode == '1':
|
||||
# BW appears to not save correctly on QT4 and QT5
|
||||
# kicks out errors on console:
|
||||
# libpng warning: Invalid color type/bit depth combination in IHDR
|
||||
# libpng error: Invalid IHDR data
|
||||
# libpng warning: Invalid color type/bit depth combination
|
||||
# in IHDR
|
||||
# libpng error: Invalid IHDR data
|
||||
continue
|
||||
|
||||
# Test saving the file
|
||||
|
|
|
@ -196,13 +196,13 @@ class TestImageCms(PillowTestCase):
|
|||
# not a linear luminance map. so L != 128:
|
||||
self.assertEqual(k, (137, 128, 128))
|
||||
|
||||
l = i_lab.getdata(0)
|
||||
a = i_lab.getdata(1)
|
||||
b = i_lab.getdata(2)
|
||||
l_data = i_lab.getdata(0)
|
||||
a_data = i_lab.getdata(1)
|
||||
b_data = i_lab.getdata(2)
|
||||
|
||||
self.assertEqual(list(l), [137] * 100)
|
||||
self.assertEqual(list(a), [128] * 100)
|
||||
self.assertEqual(list(b), [128] * 100)
|
||||
self.assertEqual(list(l_data), [137] * 100)
|
||||
self.assertEqual(list(a_data), [128] * 100)
|
||||
self.assertEqual(list(b_data), [128] * 100)
|
||||
|
||||
def test_lab_color(self):
|
||||
psRGB = ImageCms.createProfile("sRGB")
|
||||
|
@ -286,53 +286,104 @@ class TestImageCms(PillowTestCase):
|
|||
self.assertEqual(truncate_tuple(tup1), truncate_tuple(tup2))
|
||||
|
||||
self.assertEqual(p.attributes, 4294967296)
|
||||
assert_truncated_tuple_equal(p.blue_colorant, ((0.14306640625, 0.06060791015625, 0.7140960693359375), (0.1558847490315394, 0.06603820639433387, 0.06060791015625)))
|
||||
assert_truncated_tuple_equal(p.blue_primary, ((0.14306641366715667, 0.06060790921083026, 0.7140960805782015), (0.15588475410450106, 0.06603820408959558, 0.06060790921083026)))
|
||||
assert_truncated_tuple_equal(
|
||||
p.blue_colorant,
|
||||
((0.14306640625, 0.06060791015625, 0.7140960693359375),
|
||||
(0.1558847490315394, 0.06603820639433387, 0.06060791015625)))
|
||||
assert_truncated_tuple_equal(
|
||||
p.blue_primary,
|
||||
((0.14306641366715667, 0.06060790921083026, 0.7140960805782015),
|
||||
(0.15588475410450106, 0.06603820408959558, 0.06060790921083026)))
|
||||
assert_truncated_tuple_equal(p.chromatic_adaptation, (((1.04791259765625, 0.0229339599609375, -0.050201416015625), (0.02960205078125, 0.9904632568359375, -0.0170745849609375), (-0.009246826171875, 0.0150604248046875, 0.7517852783203125)), ((1.0267159024652783, 0.022470062342089134, 0.0229339599609375), (0.02951378324103937, 0.9875098886387147, 0.9904632568359375), (-0.012205438066465256, 0.01987915407854985, 0.0150604248046875))))
|
||||
self.assertIsNone(p.chromaticity)
|
||||
self.assertEqual(p.clut, {0: (False, False, True), 1: (False, False, True), 2: (False, False, True), 3: (False, False, True)})
|
||||
self.assertEqual(p.clut, {
|
||||
0: (False, False, True),
|
||||
1: (False, False, True),
|
||||
2: (False, False, True),
|
||||
3: (False, False, True)
|
||||
})
|
||||
self.assertEqual(p.color_space, 'RGB')
|
||||
self.assertIsNone(p.colorant_table)
|
||||
self.assertIsNone(p.colorant_table_out)
|
||||
self.assertIsNone(p.colorimetric_intent)
|
||||
self.assertEqual(p.connection_space, 'XYZ ')
|
||||
self.assertEqual(p.copyright, 'Copyright International Color Consortium, 2009')
|
||||
self.assertEqual(p.creation_date, datetime.datetime(2009, 2, 27, 21, 36, 31))
|
||||
self.assertEqual(p.copyright,
|
||||
'Copyright International Color Consortium, 2009')
|
||||
self.assertEqual(p.creation_date,
|
||||
datetime.datetime(2009, 2, 27, 21, 36, 31))
|
||||
self.assertEqual(p.device_class, 'mntr')
|
||||
assert_truncated_tuple_equal(p.green_colorant, ((0.3851470947265625, 0.7168731689453125, 0.097076416015625), (0.32119769927720654, 0.5978443449048152, 0.7168731689453125)))
|
||||
assert_truncated_tuple_equal(p.green_primary, ((0.3851470888162112, 0.7168731974161346, 0.09707641738998518), (0.32119768793686687, 0.5978443567149709, 0.7168731974161346)))
|
||||
assert_truncated_tuple_equal(
|
||||
p.green_colorant,
|
||||
((0.3851470947265625, 0.7168731689453125, 0.097076416015625),
|
||||
(0.32119769927720654, 0.5978443449048152, 0.7168731689453125)))
|
||||
assert_truncated_tuple_equal(
|
||||
p.green_primary,
|
||||
((0.3851470888162112, 0.7168731974161346, 0.09707641738998518),
|
||||
(0.32119768793686687, 0.5978443567149709, 0.7168731974161346)))
|
||||
self.assertEqual(p.header_flags, 0)
|
||||
self.assertEqual(p.header_manufacturer, '\x00\x00\x00\x00')
|
||||
self.assertEqual(p.header_model, '\x00\x00\x00\x00')
|
||||
self.assertEqual(p.icc_measurement_condition, {'backing': (0.0, 0.0, 0.0), 'flare': 0.0, 'geo': 'unknown', 'observer': 1, 'illuminant_type': 'D65'})
|
||||
self.assertEqual(p.icc_measurement_condition, {
|
||||
'backing': (0.0, 0.0, 0.0),
|
||||
'flare': 0.0,
|
||||
'geo': 'unknown',
|
||||
'observer': 1,
|
||||
'illuminant_type': 'D65'
|
||||
})
|
||||
self.assertEqual(p.icc_version, 33554432)
|
||||
self.assertIsNone(p.icc_viewing_condition)
|
||||
self.assertEqual(p.intent_supported, {0: (True, True, True), 1: (True, True, True), 2: (True, True, True), 3: (True, True, True)})
|
||||
self.assertEqual(p.intent_supported, {
|
||||
0: (True, True, True),
|
||||
1: (True, True, True),
|
||||
2: (True, True, True),
|
||||
3: (True, True, True)
|
||||
})
|
||||
self.assertTrue(p.is_matrix_shaper)
|
||||
self.assertEqual(p.luminance, ((0.0, 80.0, 0.0), (0.0, 1.0, 80.0)))
|
||||
self.assertIsNone(p.manufacturer)
|
||||
assert_truncated_tuple_equal(p.media_black_point, ((0.012054443359375, 0.0124969482421875, 0.01031494140625), (0.34573304157549234, 0.35842450765864337, 0.0124969482421875)))
|
||||
assert_truncated_tuple_equal(p.media_white_point, ((0.964202880859375, 1.0, 0.8249053955078125), (0.3457029219802284, 0.3585375327567059, 1.0)))
|
||||
assert_truncated_tuple_equal((p.media_white_point_temperature,), (5000.722328847392,))
|
||||
self.assertEqual(p.model, 'IEC 61966-2-1 Default RGB Colour Space - sRGB')
|
||||
assert_truncated_tuple_equal(
|
||||
p.media_black_point,
|
||||
((0.012054443359375, 0.0124969482421875, 0.01031494140625),
|
||||
(0.34573304157549234, 0.35842450765864337, 0.0124969482421875)))
|
||||
assert_truncated_tuple_equal(
|
||||
p.media_white_point,
|
||||
((0.964202880859375, 1.0, 0.8249053955078125),
|
||||
(0.3457029219802284, 0.3585375327567059, 1.0)))
|
||||
assert_truncated_tuple_equal(
|
||||
(p.media_white_point_temperature,),
|
||||
(5000.722328847392,))
|
||||
self.assertEqual(p.model,
|
||||
'IEC 61966-2-1 Default RGB Colour Space - sRGB')
|
||||
self.assertEqual(p.pcs, 'XYZ')
|
||||
self.assertIsNone(p.perceptual_rendering_intent_gamut)
|
||||
self.assertEqual(p.product_copyright, 'Copyright International Color Consortium, 2009')
|
||||
self.assertEqual(p.product_copyright,
|
||||
'Copyright International Color Consortium, 2009')
|
||||
self.assertEqual(p.product_desc, 'sRGB IEC61966-2-1 black scaled')
|
||||
self.assertEqual(p.product_description, 'sRGB IEC61966-2-1 black scaled')
|
||||
self.assertEqual(p.product_description,
|
||||
'sRGB IEC61966-2-1 black scaled')
|
||||
self.assertEqual(p.product_manufacturer, '')
|
||||
self.assertEqual(p.product_model, 'IEC 61966-2-1 Default RGB Colour Space - sRGB')
|
||||
self.assertEqual(p.profile_description, 'sRGB IEC61966-2-1 black scaled')
|
||||
self.assertEqual(p.profile_id, b')\xf8=\xde\xaf\xf2U\xaexB\xfa\xe4\xca\x839\r')
|
||||
assert_truncated_tuple_equal(p.red_colorant, ((0.436065673828125, 0.2224884033203125, 0.013916015625), (0.6484536316398539, 0.3308524880306778, 0.2224884033203125)))
|
||||
assert_truncated_tuple_equal(p.red_primary, ((0.43606566581047446, 0.22248840582960838, 0.013916015621759925), (0.6484536250319214, 0.3308524944738204, 0.22248840582960838)))
|
||||
self.assertEqual(
|
||||
p.product_model, 'IEC 61966-2-1 Default RGB Colour Space - sRGB')
|
||||
self.assertEqual(
|
||||
p.profile_description, 'sRGB IEC61966-2-1 black scaled')
|
||||
self.assertEqual(
|
||||
p.profile_id, b')\xf8=\xde\xaf\xf2U\xaexB\xfa\xe4\xca\x839\r')
|
||||
assert_truncated_tuple_equal(
|
||||
p.red_colorant,
|
||||
((0.436065673828125, 0.2224884033203125, 0.013916015625),
|
||||
(0.6484536316398539, 0.3308524880306778, 0.2224884033203125)))
|
||||
assert_truncated_tuple_equal(
|
||||
p.red_primary,
|
||||
((0.43606566581047446, 0.22248840582960838, 0.013916015621759925),
|
||||
(0.6484536250319214, 0.3308524944738204, 0.22248840582960838)))
|
||||
self.assertEqual(p.rendering_intent, 0)
|
||||
self.assertIsNone(p.saturation_rendering_intent_gamut)
|
||||
self.assertIsNone(p.screening_description)
|
||||
self.assertIsNone(p.target)
|
||||
self.assertEqual(p.technology, 'CRT ')
|
||||
self.assertEqual(p.version, 2.0)
|
||||
self.assertEqual(p.viewing_condition, 'Reference Viewing Condition in IEC 61966-2-1')
|
||||
self.assertEqual(p.viewing_condition,
|
||||
'Reference Viewing Condition in IEC 61966-2-1')
|
||||
self.assertEqual(p.xcolor_space, 'RGB ')
|
||||
|
||||
def test_profile_typesafety(self):
|
||||
|
@ -346,7 +397,8 @@ class TestImageCms(PillowTestCase):
|
|||
with self.assertRaises(TypeError):
|
||||
ImageCms.ImageCmsProfile(1).tobytes()
|
||||
|
||||
def assert_aux_channel_preserved(self, mode, transform_in_place, preserved_channel):
|
||||
def assert_aux_channel_preserved(self, mode,
|
||||
transform_in_place, preserved_channel):
|
||||
def create_test_image():
|
||||
# set up test image with something interesting in the tested aux
|
||||
# channel.
|
||||
|
@ -379,29 +431,35 @@ class TestImageCms(PillowTestCase):
|
|||
# create some transform, it doesn't matter which one
|
||||
source_profile = ImageCms.createProfile("sRGB")
|
||||
destination_profile = ImageCms.createProfile("sRGB")
|
||||
t = ImageCms.buildTransform(source_profile, destination_profile, inMode=mode, outMode=mode)
|
||||
t = ImageCms.buildTransform(
|
||||
source_profile, destination_profile, inMode=mode, outMode=mode)
|
||||
|
||||
# apply transform
|
||||
if transform_in_place:
|
||||
ImageCms.applyTransform(source_image, t, inPlace=True)
|
||||
result_image = source_image
|
||||
else:
|
||||
result_image = ImageCms.applyTransform(source_image, t, inPlace=False)
|
||||
result_image = ImageCms.applyTransform(
|
||||
source_image, t, inPlace=False)
|
||||
result_image_aux = result_image.getchannel(preserved_channel)
|
||||
|
||||
self.assert_image_equal(source_image_aux, result_image_aux)
|
||||
|
||||
def test_preserve_auxiliary_channels_rgba(self):
|
||||
self.assert_aux_channel_preserved(mode='RGBA', transform_in_place=False, preserved_channel='A')
|
||||
self.assert_aux_channel_preserved(mode='RGBA',
|
||||
transform_in_place=False, preserved_channel='A')
|
||||
|
||||
def test_preserve_auxiliary_channels_rgba_in_place(self):
|
||||
self.assert_aux_channel_preserved(mode='RGBA', transform_in_place=True, preserved_channel='A')
|
||||
self.assert_aux_channel_preserved(mode='RGBA',
|
||||
transform_in_place=True, preserved_channel='A')
|
||||
|
||||
def test_preserve_auxiliary_channels_rgbx(self):
|
||||
self.assert_aux_channel_preserved(mode='RGBX', transform_in_place=False, preserved_channel='X')
|
||||
self.assert_aux_channel_preserved(mode='RGBX',
|
||||
transform_in_place=False, preserved_channel='X')
|
||||
|
||||
def test_preserve_auxiliary_channels_rgbx_in_place(self):
|
||||
self.assert_aux_channel_preserved(mode='RGBX', transform_in_place=True, preserved_channel='X')
|
||||
self.assert_aux_channel_preserved(mode='RGBX',
|
||||
transform_in_place=True, preserved_channel='X')
|
||||
|
||||
def test_auxiliary_channels_isolated(self):
|
||||
# test data in aux channels does not affect non-aux channels
|
||||
|
@ -422,20 +480,29 @@ class TestImageCms(PillowTestCase):
|
|||
source_profile = ImageCms.createProfile(src_format[1])
|
||||
destination_profile = ImageCms.createProfile(dst_format[1])
|
||||
source_image = src_format[3]
|
||||
test_transform = ImageCms.buildTransform(source_profile, destination_profile, inMode=src_format[0], outMode=dst_format[0])
|
||||
test_transform = ImageCms.buildTransform(
|
||||
source_profile, destination_profile,
|
||||
inMode=src_format[0], outMode=dst_format[0])
|
||||
|
||||
# test conversion from aux-ful source
|
||||
if transform_in_place:
|
||||
test_image = source_image.copy()
|
||||
ImageCms.applyTransform(test_image, test_transform, inPlace=True)
|
||||
ImageCms.applyTransform(
|
||||
test_image, test_transform, inPlace=True)
|
||||
else:
|
||||
test_image = ImageCms.applyTransform(source_image, test_transform, inPlace=False)
|
||||
test_image = ImageCms.applyTransform(
|
||||
source_image, test_transform, inPlace=False)
|
||||
|
||||
# reference conversion from aux-less source
|
||||
reference_transform = ImageCms.buildTransform(source_profile, destination_profile, inMode=src_format[2], outMode=dst_format[2])
|
||||
reference_image = ImageCms.applyTransform(source_image.convert(src_format[2]), reference_transform)
|
||||
reference_transform = ImageCms.buildTransform(
|
||||
source_profile, destination_profile,
|
||||
inMode=src_format[2], outMode=dst_format[2])
|
||||
reference_image = ImageCms.applyTransform(
|
||||
source_image.convert(src_format[2]),
|
||||
reference_transform)
|
||||
|
||||
self.assert_image_equal(test_image.convert(dst_format[2]), reference_image)
|
||||
self.assert_image_equal(test_image.convert(dst_format[2]),
|
||||
reference_image)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -31,7 +31,8 @@ class TestImageColor(PillowTestCase):
|
|||
|
||||
# case insensitivity
|
||||
self.assertEqual(ImageColor.getrgb("#DEF"), ImageColor.getrgb("#def"))
|
||||
self.assertEqual(ImageColor.getrgb("#CDEF"), ImageColor.getrgb("#cdef"))
|
||||
self.assertEqual(ImageColor.getrgb("#CDEF"),
|
||||
ImageColor.getrgb("#cdef"))
|
||||
self.assertEqual(ImageColor.getrgb("#DEFDEF"),
|
||||
ImageColor.getrgb("#defdef"))
|
||||
self.assertEqual(ImageColor.getrgb("#CDEFCDEF"),
|
||||
|
@ -80,18 +81,23 @@ class TestImageColor(PillowTestCase):
|
|||
|
||||
self.assertEqual((255, 0, 0), ImageColor.getrgb("hsv(0,100%,100%)"))
|
||||
self.assertEqual((255, 0, 0), ImageColor.getrgb("hsv(360,100%,100%)"))
|
||||
self.assertEqual((0, 255, 255), ImageColor.getrgb("hsv(180,100%,100%)"))
|
||||
self.assertEqual((0, 255, 255),
|
||||
ImageColor.getrgb("hsv(180,100%,100%)"))
|
||||
|
||||
# alternate format
|
||||
self.assertEqual(ImageColor.getrgb("hsb(0,100%,50%)"),
|
||||
ImageColor.getrgb("hsv(0,100%,50%)"))
|
||||
|
||||
# floats
|
||||
self.assertEqual((254, 3, 3), ImageColor.getrgb("hsl(0.1,99.2%,50.3%)"))
|
||||
self.assertEqual((255, 0, 0), ImageColor.getrgb("hsl(360.,100.0%,50%)"))
|
||||
self.assertEqual((254, 3, 3),
|
||||
ImageColor.getrgb("hsl(0.1,99.2%,50.3%)"))
|
||||
self.assertEqual((255, 0, 0),
|
||||
ImageColor.getrgb("hsl(360.,100.0%,50%)"))
|
||||
|
||||
self.assertEqual((253, 2, 2), ImageColor.getrgb("hsv(0.1,99.2%,99.3%)"))
|
||||
self.assertEqual((255, 0, 0), ImageColor.getrgb("hsv(360.,100.0%,100%)"))
|
||||
self.assertEqual((253, 2, 2),
|
||||
ImageColor.getrgb("hsv(0.1,99.2%,99.3%)"))
|
||||
self.assertEqual((255, 0, 0),
|
||||
ImageColor.getrgb("hsv(360.,100.0%,100%)"))
|
||||
|
||||
# case insensitivity
|
||||
self.assertEqual(ImageColor.getrgb("RGB(255,0,0)"),
|
||||
|
|
|
@ -696,6 +696,47 @@ class TestImageDraw(PillowTestCase):
|
|||
draw.textsize("\n")
|
||||
draw.textsize("test\n")
|
||||
|
||||
def test_same_color_outline(self):
|
||||
# Prepare shape
|
||||
x0, y0 = 5, 5
|
||||
x1, y1 = 5, 50
|
||||
x2, y2 = 95, 50
|
||||
x3, y3 = 95, 5
|
||||
|
||||
s = ImageDraw.Outline()
|
||||
s.move(x0, y0)
|
||||
s.curve(x1, y1, x2, y2, x3, y3)
|
||||
s.line(x0, y0)
|
||||
|
||||
# Begin
|
||||
for mode in ["RGB", "L"]:
|
||||
for fill, outline in [
|
||||
["red", None],
|
||||
["red", "red"],
|
||||
["red", "#f00"]
|
||||
]:
|
||||
for operation, args in {
|
||||
'chord':[BBOX1, 0, 180],
|
||||
'ellipse':[BBOX1],
|
||||
'shape':[s],
|
||||
'pieslice':[BBOX1, -90, 45],
|
||||
'polygon':[[(18, 30), (85, 30), (60, 72)]],
|
||||
'rectangle':[BBOX1]
|
||||
}.items():
|
||||
# Arrange
|
||||
im = Image.new(mode, (W, H))
|
||||
draw = ImageDraw.Draw(im)
|
||||
|
||||
# Act
|
||||
draw_method = getattr(draw, operation)
|
||||
args += [fill, outline]
|
||||
draw_method(*args)
|
||||
|
||||
# Assert
|
||||
expected = ("Tests/images/imagedraw_outline"
|
||||
"_{}_{}.png".format(operation, mode))
|
||||
self.assert_image_similar(im, Image.open(expected), 1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
@ -45,8 +45,9 @@ class TestImageEnhance(PillowTestCase):
|
|||
|
||||
for op in ['Color', 'Brightness', 'Contrast', 'Sharpness']:
|
||||
for amount in [0, 0.5, 1.0]:
|
||||
self._check_alpha(getattr(ImageEnhance, op)(original).enhance(amount),
|
||||
original, op, amount)
|
||||
self._check_alpha(
|
||||
getattr(ImageEnhance, op)(original).enhance(amount),
|
||||
original, op, amount)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -215,12 +215,16 @@ class TestPyDecoder(PillowTestCase):
|
|||
buf = BytesIO(b'\x00'*255)
|
||||
|
||||
im = MockImageFile(buf)
|
||||
im.tile = [("MOCK", (xoff, yoff, xoff+xsize + 100, yoff+ysize), 32, None)]
|
||||
im.tile = [
|
||||
("MOCK", (xoff, yoff, xoff+xsize + 100, yoff+ysize), 32, None)
|
||||
]
|
||||
d = self.get_decoder()
|
||||
|
||||
self.assertRaises(ValueError, im.load)
|
||||
|
||||
im.tile = [("MOCK", (xoff, yoff, xoff+xsize, yoff+ysize + 100), 32, None)]
|
||||
im.tile = [
|
||||
("MOCK", (xoff, yoff, xoff+xsize, yoff+ysize + 100), 32, None)
|
||||
]
|
||||
self.assertRaises(ValueError, im.load)
|
||||
|
||||
def test_no_format(self):
|
||||
|
|
|
@ -67,8 +67,11 @@ class TestImageFont(PillowTestCase):
|
|||
}
|
||||
|
||||
def setUp(self):
|
||||
freetype_version = tuple(ImageFont.core.freetype2_version.split('.'))[:2]
|
||||
self.metrics = self.METRICS.get(freetype_version, self.METRICS['Default'])
|
||||
freetype_version = tuple(
|
||||
ImageFont.core.freetype2_version.split('.')
|
||||
)[:2]
|
||||
self.metrics = self.METRICS.get(freetype_version,
|
||||
self.METRICS['Default'])
|
||||
|
||||
def get_font(self):
|
||||
return ImageFont.truetype(FONT_PATH, FONT_SIZE,
|
||||
|
@ -202,7 +205,8 @@ class TestImageFont(PillowTestCase):
|
|||
target_img = Image.open(target)
|
||||
|
||||
# Epsilon ~.5 fails with FreeType 2.7
|
||||
self.assert_image_similar(im, target_img, self.metrics['multiline'])
|
||||
self.assert_image_similar(im, target_img,
|
||||
self.metrics['multiline'])
|
||||
|
||||
def test_unknown_align(self):
|
||||
im = Image.new(mode='RGB', size=(300, 100))
|
||||
|
@ -210,10 +214,9 @@ class TestImageFont(PillowTestCase):
|
|||
ttf = self.get_font()
|
||||
|
||||
# Act/Assert
|
||||
self.assertRaises(AssertionError,
|
||||
draw.multiline_text, (0, 0), TEST_TEXT,
|
||||
font=ttf,
|
||||
align="unknown")
|
||||
self.assertRaises(
|
||||
AssertionError,
|
||||
draw.multiline_text, (0, 0), TEST_TEXT, font=ttf, align="unknown")
|
||||
|
||||
def test_draw_align(self):
|
||||
im = Image.new('RGB', (300, 100), 'white')
|
||||
|
@ -413,7 +416,7 @@ class TestImageFont(PillowTestCase):
|
|||
im = Image.new(mode='RGB', size=(300, 100))
|
||||
target = im.copy()
|
||||
draw = ImageDraw.Draw(im)
|
||||
#should not crash here.
|
||||
# should not crash here.
|
||||
draw.text((10, 10), '', font=font)
|
||||
self.assert_image_equal(im, target)
|
||||
|
||||
|
@ -428,7 +431,8 @@ class TestImageFont(PillowTestCase):
|
|||
# Make a copy of FreeTypeFont so we can patch the original
|
||||
free_type_font = copy.deepcopy(ImageFont.FreeTypeFont)
|
||||
with SimplePatcher(ImageFont, '_FreeTypeFont', free_type_font):
|
||||
def loadable_font(filepath, size, index, encoding, *args, **kwargs):
|
||||
def loadable_font(filepath, size, index, encoding,
|
||||
*args, **kwargs):
|
||||
if filepath == path_to_fake:
|
||||
return ImageFont._FreeTypeFont(FONT_PATH, size, index,
|
||||
encoding, *args, **kwargs)
|
||||
|
|
|
@ -19,7 +19,8 @@ class TestImageFontBitmap(PillowTestCase):
|
|||
font='Tests/fonts/DejaVuSans-bitmap.ttf', size=24)
|
||||
size_outline = font_outline.getsize(text)
|
||||
size_bitmap = font_bitmap.getsize(text)
|
||||
size_final = max(size_outline[0], size_bitmap[0]), max(size_outline[1], size_bitmap[1])
|
||||
size_final = (max(size_outline[0], size_bitmap[0]),
|
||||
max(size_outline[1], size_bitmap[1]))
|
||||
im_bitmap = Image.new('RGB', size_final, (255, 255, 255))
|
||||
im_outline = im_bitmap.copy()
|
||||
draw_bitmap = ImageDraw.Draw(im_bitmap)
|
||||
|
|
|
@ -11,7 +11,7 @@ FONT_PATH = "Tests/fonts/DejaVuSans.ttf"
|
|||
class TestImagecomplextext(PillowTestCase):
|
||||
|
||||
def test_english(self):
|
||||
#smoke test, this should not fail
|
||||
# smoke test, this should not fail
|
||||
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
|
||||
im = Image.new(mode='RGB', size=(300, 100))
|
||||
draw = ImageDraw.Draw(im)
|
||||
|
@ -30,7 +30,8 @@ class TestImagecomplextext(PillowTestCase):
|
|||
self.assert_image_similar(im, target_img, .5)
|
||||
|
||||
def test_y_offset(self):
|
||||
ttf = ImageFont.truetype("Tests/fonts/NotoNastaliqUrdu-Regular.ttf", FONT_SIZE)
|
||||
ttf = ImageFont.truetype("Tests/fonts/NotoNastaliqUrdu-Regular.ttf",
|
||||
FONT_SIZE)
|
||||
|
||||
im = Image.new(mode='RGB', size=(300, 100))
|
||||
draw = ImageDraw.Draw(im)
|
||||
|
@ -70,7 +71,8 @@ class TestImagecomplextext(PillowTestCase):
|
|||
|
||||
im = Image.new(mode='RGB', size=(300, 100))
|
||||
draw = ImageDraw.Draw(im)
|
||||
draw.text((0, 0), 'سلطنة عمان Oman', font=ttf, fill=500, direction='ltr')
|
||||
draw.text((0, 0), 'سلطنة عمان Oman',
|
||||
font=ttf, fill=500, direction='ltr')
|
||||
|
||||
target = 'Tests/images/test_direction_ltr.png'
|
||||
target_img = Image.open(target)
|
||||
|
@ -82,7 +84,8 @@ class TestImagecomplextext(PillowTestCase):
|
|||
|
||||
im = Image.new(mode='RGB', size=(300, 100))
|
||||
draw = ImageDraw.Draw(im)
|
||||
draw.text((0, 0), 'Oman سلطنة عمان', font=ttf, fill=500, direction='rtl')
|
||||
draw.text((0, 0), 'Oman سلطنة عمان',
|
||||
font=ttf, fill=500, direction='rtl')
|
||||
|
||||
target = 'Tests/images/test_direction_ltr.png'
|
||||
target_img = Image.open(target)
|
||||
|
@ -120,7 +123,8 @@ class TestImagecomplextext(PillowTestCase):
|
|||
|
||||
im = Image.new(mode='RGB', size=(300, 100))
|
||||
draw = ImageDraw.Draw(im)
|
||||
draw.text((0, 0), 'اللغة العربية', font=ttf, fill=500, features=['-fina', '-init', '-medi'])
|
||||
draw.text((0, 0), 'اللغة العربية', font=ttf, fill=500,
|
||||
features=['-fina', '-init', '-medi'])
|
||||
|
||||
target = 'Tests/images/test_arabictext_features.png'
|
||||
target_img = Image.open(target)
|
||||
|
|
|
@ -265,8 +265,9 @@ class MorphTests(PillowTestCase):
|
|||
# Act / Assert
|
||||
with self.assertRaises(Exception) as e:
|
||||
lb.build_lut()
|
||||
self.assertEqual(str(e.exception),
|
||||
'Syntax error in pattern "a pattern with a syntax error"')
|
||||
self.assertEqual(
|
||||
str(e.exception),
|
||||
'Syntax error in pattern "a pattern with a syntax error"')
|
||||
|
||||
def test_load_invalid_mrl(self):
|
||||
# Arrange
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from helper import unittest, PillowTestCase, hopper
|
||||
|
||||
from PIL import ImageOps
|
||||
from PIL import Image
|
||||
|
||||
|
||||
class TestImageOps(PillowTestCase):
|
||||
|
@ -94,6 +95,107 @@ class TestImageOps(PillowTestCase):
|
|||
newimg = ImageOps.scale(i, 0.5)
|
||||
self.assertEqual(newimg.size, (25, 25))
|
||||
|
||||
def test_colorize_2color(self):
|
||||
# Test the colorizing function with 2-color functionality
|
||||
|
||||
# Open test image (256px by 10px, black to white)
|
||||
im = Image.open("Tests/images/bw_gradient.png")
|
||||
im = im.convert("L")
|
||||
|
||||
# Create image with original 2-color functionality
|
||||
im_test = ImageOps.colorize(im, 'red', 'green')
|
||||
|
||||
# Test output image (2-color)
|
||||
left = (0, 1)
|
||||
middle = (127, 1)
|
||||
right = (255, 1)
|
||||
self.assert_tuple_approx_equal(im_test.getpixel(left),
|
||||
(255, 0, 0),
|
||||
threshold=1,
|
||||
msg='black test pixel incorrect')
|
||||
self.assert_tuple_approx_equal(im_test.getpixel(middle),
|
||||
(127, 63, 0),
|
||||
threshold=1,
|
||||
msg='mid test pixel incorrect')
|
||||
self.assert_tuple_approx_equal(im_test.getpixel(right),
|
||||
(0, 127, 0),
|
||||
threshold=1,
|
||||
msg='white test pixel incorrect')
|
||||
|
||||
def test_colorize_2color_offset(self):
|
||||
# Test the colorizing function with 2-color functionality and offset
|
||||
|
||||
# Open test image (256px by 10px, black to white)
|
||||
im = Image.open("Tests/images/bw_gradient.png")
|
||||
im = im.convert("L")
|
||||
|
||||
# Create image with original 2-color functionality with offsets
|
||||
im_test = ImageOps.colorize(im,
|
||||
black='red',
|
||||
white='green',
|
||||
blackpoint=50,
|
||||
whitepoint=100)
|
||||
|
||||
# Test output image (2-color) with offsets
|
||||
left = (25, 1)
|
||||
middle = (75, 1)
|
||||
right = (125, 1)
|
||||
self.assert_tuple_approx_equal(im_test.getpixel(left),
|
||||
(255, 0, 0),
|
||||
threshold=1,
|
||||
msg='black test pixel incorrect')
|
||||
self.assert_tuple_approx_equal(im_test.getpixel(middle),
|
||||
(127, 63, 0),
|
||||
threshold=1,
|
||||
msg='mid test pixel incorrect')
|
||||
self.assert_tuple_approx_equal(im_test.getpixel(right),
|
||||
(0, 127, 0),
|
||||
threshold=1,
|
||||
msg='white test pixel incorrect')
|
||||
|
||||
def test_colorize_3color_offset(self):
|
||||
# Test the colorizing function with 3-color functionality and offset
|
||||
|
||||
# Open test image (256px by 10px, black to white)
|
||||
im = Image.open("Tests/images/bw_gradient.png")
|
||||
im = im.convert("L")
|
||||
|
||||
# Create image with new three color functionality with offsets
|
||||
im_test = ImageOps.colorize(im,
|
||||
black='red',
|
||||
white='green',
|
||||
mid='blue',
|
||||
blackpoint=50,
|
||||
whitepoint=200,
|
||||
midpoint=100)
|
||||
|
||||
# Test output image (3-color) with offsets
|
||||
left = (25, 1)
|
||||
left_middle = (75, 1)
|
||||
middle = (100, 1)
|
||||
right_middle = (150, 1)
|
||||
right = (225, 1)
|
||||
self.assert_tuple_approx_equal(im_test.getpixel(left),
|
||||
(255, 0, 0),
|
||||
threshold=1,
|
||||
msg='black test pixel incorrect')
|
||||
self.assert_tuple_approx_equal(im_test.getpixel(left_middle),
|
||||
(127, 0, 127),
|
||||
threshold=1,
|
||||
msg='low-mid test pixel incorrect')
|
||||
self.assert_tuple_approx_equal(im_test.getpixel(middle),
|
||||
(0, 0, 255),
|
||||
threshold=1,
|
||||
msg='mid incorrect')
|
||||
self.assert_tuple_approx_equal(im_test.getpixel(right_middle),
|
||||
(0, 63, 127),
|
||||
threshold=1,
|
||||
msg='high-mid test pixel incorrect')
|
||||
self.assert_tuple_approx_equal(im_test.getpixel(right),
|
||||
(0, 127, 0),
|
||||
threshold=1,
|
||||
msg='white test pixel incorrect')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
@ -13,27 +13,25 @@ class TestImageOpsUsm(PillowTestCase):
|
|||
def test_ops_api(self):
|
||||
|
||||
i = self.assert_warning(DeprecationWarning,
|
||||
ImageOps.gaussian_blur, im, 2.0)
|
||||
ImageOps.gaussian_blur, im, 2.0)
|
||||
self.assertEqual(i.mode, "RGB")
|
||||
self.assertEqual(i.size, (128, 128))
|
||||
|
||||
i = self.assert_warning(DeprecationWarning, ImageOps.box_blur, im, 1)
|
||||
self.assertEqual(i.mode, "RGB")
|
||||
self.assertEqual(i.size, (128, 128))
|
||||
|
||||
i = self.assert_warning(DeprecationWarning, ImageOps.gblur, im, 2.0)
|
||||
self.assertEqual(i.mode, "RGB")
|
||||
self.assertEqual(i.size, (128, 128))
|
||||
|
||||
i = self.assert_warning(DeprecationWarning,
|
||||
ImageOps.box_blur, im, 1)
|
||||
ImageOps.unsharp_mask, im, 2.0, 125, 8)
|
||||
self.assertEqual(i.mode, "RGB")
|
||||
self.assertEqual(i.size, (128, 128))
|
||||
|
||||
i = self.assert_warning(DeprecationWarning,
|
||||
ImageOps.gblur, im, 2.0)
|
||||
self.assertEqual(i.mode, "RGB")
|
||||
self.assertEqual(i.size, (128, 128))
|
||||
|
||||
i = self.assert_warning(DeprecationWarning,
|
||||
ImageOps.unsharp_mask, im, 2.0, 125, 8)
|
||||
self.assertEqual(i.mode, "RGB")
|
||||
self.assertEqual(i.size, (128, 128))
|
||||
|
||||
i = self.assert_warning(DeprecationWarning,
|
||||
ImageOps.usm, im, 2.0, 125, 8)
|
||||
ImageOps.usm, im, 2.0, 125, 8)
|
||||
self.assertEqual(i.mode, "RGB")
|
||||
self.assertEqual(i.size, (128, 128))
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ class TestImagePalette(PillowTestCase):
|
|||
|
||||
ImagePalette.ImagePalette("RGB", list(range(256))*3)
|
||||
self.assertRaises(ValueError,
|
||||
ImagePalette.ImagePalette, "RGB", list(range(256))*2)
|
||||
ImagePalette.ImagePalette, "RGB", list(range(256))*2)
|
||||
|
||||
def test_getcolor(self):
|
||||
|
||||
|
@ -66,7 +66,7 @@ class TestImagePalette(PillowTestCase):
|
|||
|
||||
# Act
|
||||
self.assertRaises(NotImplementedError,
|
||||
ImagePalette.make_linear_lut, black, white)
|
||||
ImagePalette.make_linear_lut, black, white)
|
||||
|
||||
def test_make_gamma_lut(self):
|
||||
# Arrange
|
||||
|
@ -133,7 +133,7 @@ class TestImagePalette(PillowTestCase):
|
|||
|
||||
def test_invalid_palette(self):
|
||||
self.assertRaises(IOError,
|
||||
ImagePalette.load, "Tests/images/hopper.jpg")
|
||||
ImagePalette.load, "Tests/images/hopper.jpg")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -34,7 +34,8 @@ class TestLibPack(PillowTestCase):
|
|||
self.assert_pack("1", "1;R", b'\xaa', 0, X, 0, X, 0, X, 0, X)
|
||||
self.assert_pack("1", "1;IR", b'\xaa', X, 0, X, 0, X, 0, X, 0)
|
||||
|
||||
self.assert_pack("1", "L", b'\xff\x00\x00\xff\x00\x00', X, 0, 0, X, 0, 0)
|
||||
self.assert_pack(
|
||||
"1", "L", b'\xff\x00\x00\xff\x00\x00', X, 0, 0, X, 0, 0)
|
||||
|
||||
def test_L(self):
|
||||
self.assert_pack("L", "L", 1, 1, 2, 3, 4)
|
||||
|
@ -57,14 +58,18 @@ class TestLibPack(PillowTestCase):
|
|||
|
||||
def test_RGB(self):
|
||||
self.assert_pack("RGB", "RGB", 3, (1, 2, 3), (4, 5, 6), (7, 8, 9))
|
||||
self.assert_pack("RGB", "RGBX",
|
||||
self.assert_pack(
|
||||
"RGB", "RGBX",
|
||||
b'\x01\x02\x03\xff\x05\x06\x07\xff', (1, 2, 3), (5, 6, 7))
|
||||
self.assert_pack("RGB", "XRGB",
|
||||
self.assert_pack(
|
||||
"RGB", "XRGB",
|
||||
b'\x00\x02\x03\x04\x00\x06\x07\x08', (2, 3, 4), (6, 7, 8))
|
||||
self.assert_pack("RGB", "BGR", 3, (3, 2, 1), (6, 5, 4), (9, 8, 7))
|
||||
self.assert_pack("RGB", "BGRX",
|
||||
self.assert_pack(
|
||||
"RGB", "BGRX",
|
||||
b'\x01\x02\x03\x00\x05\x06\x07\x00', (3, 2, 1), (7, 6, 5))
|
||||
self.assert_pack("RGB", "XBGR",
|
||||
self.assert_pack(
|
||||
"RGB", "XBGR",
|
||||
b'\x00\x02\x03\x04\x00\x06\x07\x08', (4, 3, 2), (8, 7, 6))
|
||||
self.assert_pack("RGB", "RGB;L", 3, (1, 4, 7), (2, 5, 8), (3, 6, 9))
|
||||
self.assert_pack("RGB", "R", 1, (1, 9, 9), (2, 9, 9), (3, 9, 9))
|
||||
|
@ -72,71 +77,98 @@ class TestLibPack(PillowTestCase):
|
|||
self.assert_pack("RGB", "B", 1, (9, 9, 1), (9, 9, 2), (9, 9, 3))
|
||||
|
||||
def test_RGBA(self):
|
||||
self.assert_pack("RGBA", "RGBA", 4,
|
||||
(1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12))
|
||||
self.assert_pack("RGBA", "RGBA;L", 4,
|
||||
(1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12))
|
||||
self.assert_pack("RGBA", "RGB", 3, (1, 2, 3, 14), (4, 5, 6, 15), (7, 8, 9, 16))
|
||||
self.assert_pack("RGBA", "BGR", 3, (3, 2, 1, 14), (6, 5, 4, 15), (9, 8, 7, 16))
|
||||
self.assert_pack("RGBA", "BGRA", 4,
|
||||
self.assert_pack(
|
||||
"RGBA", "RGBA", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12))
|
||||
self.assert_pack(
|
||||
"RGBA", "RGBA;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12))
|
||||
self.assert_pack(
|
||||
"RGBA", "RGB", 3, (1, 2, 3, 14), (4, 5, 6, 15), (7, 8, 9, 16))
|
||||
self.assert_pack(
|
||||
"RGBA", "BGR", 3, (3, 2, 1, 14), (6, 5, 4, 15), (9, 8, 7, 16))
|
||||
self.assert_pack(
|
||||
"RGBA", "BGRA", 4,
|
||||
(3, 2, 1, 4), (7, 6, 5, 8), (11, 10, 9, 12))
|
||||
self.assert_pack("RGBA", "ABGR", 4,
|
||||
(4, 3, 2, 1), (8, 7, 6, 5), (12, 11, 10, 9))
|
||||
self.assert_pack("RGBA", "BGRa", 4,
|
||||
self.assert_pack(
|
||||
"RGBA", "ABGR", 4, (4, 3, 2, 1), (8, 7, 6, 5), (12, 11, 10, 9))
|
||||
self.assert_pack(
|
||||
"RGBA", "BGRa", 4,
|
||||
(191, 127, 63, 4), (223, 191, 159, 8), (233, 212, 191, 12))
|
||||
self.assert_pack("RGBA", "R", 1, (1, 0, 8, 9), (2, 0, 8, 9), (3, 0, 8, 0))
|
||||
self.assert_pack("RGBA", "G", 1, (6, 1, 8, 9), (6, 2, 8, 9), (6, 3, 8, 9))
|
||||
self.assert_pack("RGBA", "B", 1, (6, 7, 1, 9), (6, 7, 2, 0), (6, 7, 3, 9))
|
||||
self.assert_pack("RGBA", "A", 1, (6, 7, 0, 1), (6, 7, 0, 2), (0, 7, 0, 3))
|
||||
self.assert_pack(
|
||||
"RGBA", "R", 1, (1, 0, 8, 9), (2, 0, 8, 9), (3, 0, 8, 0))
|
||||
self.assert_pack(
|
||||
"RGBA", "G", 1, (6, 1, 8, 9), (6, 2, 8, 9), (6, 3, 8, 9))
|
||||
self.assert_pack(
|
||||
"RGBA", "B", 1, (6, 7, 1, 9), (6, 7, 2, 0), (6, 7, 3, 9))
|
||||
self.assert_pack(
|
||||
"RGBA", "A", 1, (6, 7, 0, 1), (6, 7, 0, 2), (0, 7, 0, 3))
|
||||
|
||||
def test_RGBa(self):
|
||||
self.assert_pack("RGBa", "RGBa", 4,
|
||||
(1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12))
|
||||
self.assert_pack("RGBa", "BGRa", 4,
|
||||
(3, 2, 1, 4), (7, 6, 5, 8), (11, 10, 9, 12))
|
||||
self.assert_pack("RGBa", "aBGR", 4,
|
||||
(4, 3, 2, 1), (8, 7, 6, 5), (12, 11, 10, 9))
|
||||
self.assert_pack(
|
||||
"RGBa", "RGBa", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12))
|
||||
self.assert_pack(
|
||||
"RGBa", "BGRa", 4, (3, 2, 1, 4), (7, 6, 5, 8), (11, 10, 9, 12))
|
||||
self.assert_pack(
|
||||
"RGBa", "aBGR", 4, (4, 3, 2, 1), (8, 7, 6, 5), (12, 11, 10, 9))
|
||||
|
||||
def test_RGBX(self):
|
||||
self.assert_pack("RGBX", "RGBX", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12))
|
||||
self.assert_pack("RGBX", "RGBX;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12))
|
||||
self.assert_pack("RGBX", "RGB", 3, (1, 2, 3, X), (4, 5, 6, X), (7, 8, 9, X))
|
||||
self.assert_pack("RGBX", "BGR", 3, (3, 2, 1, X), (6, 5, 4, X), (9, 8, 7, X))
|
||||
self.assert_pack("RGBX", "BGRX",
|
||||
self.assert_pack(
|
||||
"RGBX", "RGBX", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12))
|
||||
self.assert_pack(
|
||||
"RGBX", "RGBX;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12))
|
||||
self.assert_pack(
|
||||
"RGBX", "RGB", 3, (1, 2, 3, X), (4, 5, 6, X), (7, 8, 9, X))
|
||||
self.assert_pack(
|
||||
"RGBX", "BGR", 3, (3, 2, 1, X), (6, 5, 4, X), (9, 8, 7, X))
|
||||
self.assert_pack(
|
||||
"RGBX", "BGRX",
|
||||
b'\x01\x02\x03\x00\x05\x06\x07\x00\t\n\x0b\x00',
|
||||
(3, 2, 1, X), (7, 6, 5, X), (11, 10, 9, X))
|
||||
self.assert_pack("RGBX", "XBGR",
|
||||
self.assert_pack(
|
||||
"RGBX", "XBGR",
|
||||
b'\x00\x02\x03\x04\x00\x06\x07\x08\x00\n\x0b\x0c',
|
||||
(4, 3, 2, X), (8, 7, 6, X), (12, 11, 10, X))
|
||||
self.assert_pack("RGBX", "R", 1, (1, 0, 8, 9), (2, 0, 8, 9), (3, 0, 8, 0))
|
||||
self.assert_pack("RGBX", "G", 1, (6, 1, 8, 9), (6, 2, 8, 9), (6, 3, 8, 9))
|
||||
self.assert_pack("RGBX", "B", 1, (6, 7, 1, 9), (6, 7, 2, 0), (6, 7, 3, 9))
|
||||
self.assert_pack("RGBX", "X", 1, (6, 7, 0, 1), (6, 7, 0, 2), (0, 7, 0, 3))
|
||||
self.assert_pack("RGBX", "R", 1,
|
||||
(1, 0, 8, 9), (2, 0, 8, 9), (3, 0, 8, 0))
|
||||
self.assert_pack("RGBX", "G", 1,
|
||||
(6, 1, 8, 9), (6, 2, 8, 9), (6, 3, 8, 9))
|
||||
self.assert_pack("RGBX", "B", 1,
|
||||
(6, 7, 1, 9), (6, 7, 2, 0), (6, 7, 3, 9))
|
||||
self.assert_pack("RGBX", "X", 1,
|
||||
(6, 7, 0, 1), (6, 7, 0, 2), (0, 7, 0, 3))
|
||||
|
||||
def test_CMYK(self):
|
||||
self.assert_pack("CMYK", "CMYK", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12))
|
||||
self.assert_pack("CMYK", "CMYK;I", 4,
|
||||
self.assert_pack("CMYK", "CMYK", 4,
|
||||
(1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12))
|
||||
self.assert_pack(
|
||||
"CMYK", "CMYK;I", 4,
|
||||
(254, 253, 252, 251), (250, 249, 248, 247), (246, 245, 244, 243))
|
||||
self.assert_pack("CMYK", "CMYK;L", 4,
|
||||
(1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12))
|
||||
self.assert_pack("CMYK", "K", 1, (6, 7, 0, 1), (6, 7, 0, 2), (0, 7, 0, 3))
|
||||
self.assert_pack(
|
||||
"CMYK", "CMYK;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12))
|
||||
self.assert_pack("CMYK", "K", 1,
|
||||
(6, 7, 0, 1), (6, 7, 0, 2), (0, 7, 0, 3))
|
||||
|
||||
def test_YCbCr(self):
|
||||
self.assert_pack("YCbCr", "YCbCr", 3, (1, 2, 3), (4, 5, 6), (7, 8, 9))
|
||||
self.assert_pack("YCbCr", "YCbCr;L", 3, (1, 4, 7), (2, 5, 8), (3, 6, 9))
|
||||
self.assert_pack("YCbCr", "YCbCrX",
|
||||
self.assert_pack("YCbCr", "YCbCr;L", 3,
|
||||
(1, 4, 7), (2, 5, 8), (3, 6, 9))
|
||||
self.assert_pack(
|
||||
"YCbCr", "YCbCrX",
|
||||
b'\x01\x02\x03\xff\x05\x06\x07\xff\t\n\x0b\xff',
|
||||
(1, 2, 3), (5, 6, 7), (9, 10, 11))
|
||||
self.assert_pack("YCbCr", "YCbCrK",
|
||||
self.assert_pack(
|
||||
"YCbCr", "YCbCrK",
|
||||
b'\x01\x02\x03\xff\x05\x06\x07\xff\t\n\x0b\xff',
|
||||
(1, 2, 3), (5, 6, 7), (9, 10, 11))
|
||||
self.assert_pack("YCbCr", "Y", 1, (1, 0, 8, 9), (2, 0, 8, 9), (3, 0, 8, 0))
|
||||
self.assert_pack("YCbCr", "Cb", 1, (6, 1, 8, 9), (6, 2, 8, 9), (6, 3, 8, 9))
|
||||
self.assert_pack("YCbCr", "Cr", 1, (6, 7, 1, 9), (6, 7, 2, 0), (6, 7, 3, 9))
|
||||
self.assert_pack("YCbCr", "Y", 1,
|
||||
(1, 0, 8, 9), (2, 0, 8, 9), (3, 0, 8, 0))
|
||||
self.assert_pack("YCbCr", "Cb", 1,
|
||||
(6, 1, 8, 9), (6, 2, 8, 9), (6, 3, 8, 9))
|
||||
self.assert_pack("YCbCr", "Cr", 1,
|
||||
(6, 7, 1, 9), (6, 7, 2, 0), (6, 7, 3, 9))
|
||||
|
||||
def test_LAB(self):
|
||||
self.assert_pack("LAB", "LAB", 3,
|
||||
(1, 130, 131), (4, 133, 134), (7, 136, 137))
|
||||
self.assert_pack(
|
||||
"LAB", "LAB", 3, (1, 130, 131), (4, 133, 134), (7, 136, 137))
|
||||
self.assert_pack("LAB", "L", 1, (1, 9, 9), (2, 9, 9), (3, 9, 9))
|
||||
self.assert_pack("LAB", "A", 1, (9, 1, 9), (9, 2, 9), (9, 3, 9))
|
||||
self.assert_pack("LAB", "B", 1, (9, 9, 1), (9, 9, 2), (9, 9, 3))
|
||||
|
@ -149,35 +181,35 @@ class TestLibPack(PillowTestCase):
|
|||
|
||||
def test_I(self):
|
||||
self.assert_pack("I", "I;16B", 2, 0x0102, 0x0304)
|
||||
self.assert_pack("I", "I;32S",
|
||||
b'\x83\x00\x00\x01\x01\x00\x00\x83',
|
||||
0x01000083, -2097151999)
|
||||
self.assert_pack(
|
||||
"I", "I;32S",
|
||||
b'\x83\x00\x00\x01\x01\x00\x00\x83', 0x01000083, -2097151999)
|
||||
|
||||
if sys.byteorder == 'little':
|
||||
self.assert_pack("I", "I", 4, 0x04030201, 0x08070605)
|
||||
self.assert_pack("I", "I;32NS",
|
||||
b'\x83\x00\x00\x01\x01\x00\x00\x83',
|
||||
0x01000083, -2097151999)
|
||||
self.assert_pack(
|
||||
"I", "I;32NS",
|
||||
b'\x83\x00\x00\x01\x01\x00\x00\x83', 0x01000083, -2097151999)
|
||||
else:
|
||||
self.assert_pack("I", "I", 4, 0x01020304, 0x05060708)
|
||||
self.assert_pack("I", "I;32NS",
|
||||
b'\x83\x00\x00\x01\x01\x00\x00\x83',
|
||||
-2097151999, 0x01000083)
|
||||
self.assert_pack(
|
||||
"I", "I;32NS",
|
||||
b'\x83\x00\x00\x01\x01\x00\x00\x83', -2097151999, 0x01000083)
|
||||
|
||||
def test_F_float(self):
|
||||
self.assert_pack("F", "F;32F", 4,
|
||||
1.539989614439558e-36, 4.063216068939723e-34)
|
||||
self.assert_pack(
|
||||
"F", "F;32F", 4, 1.539989614439558e-36, 4.063216068939723e-34)
|
||||
|
||||
if sys.byteorder == 'little':
|
||||
self.assert_pack("F", "F", 4,
|
||||
1.539989614439558e-36, 4.063216068939723e-34)
|
||||
self.assert_pack("F", "F;32NF", 4,
|
||||
1.539989614439558e-36, 4.063216068939723e-34)
|
||||
self.assert_pack(
|
||||
"F", "F", 4, 1.539989614439558e-36, 4.063216068939723e-34)
|
||||
self.assert_pack(
|
||||
"F", "F;32NF", 4, 1.539989614439558e-36, 4.063216068939723e-34)
|
||||
else:
|
||||
self.assert_pack("F", "F", 4,
|
||||
2.387939260590663e-38, 6.301941157072183e-36)
|
||||
self.assert_pack("F", "F;32NF", 4,
|
||||
2.387939260590663e-38, 6.301941157072183e-36)
|
||||
self.assert_pack(
|
||||
"F", "F", 4, 2.387939260590663e-38, 6.301941157072183e-36)
|
||||
self.assert_pack(
|
||||
"F", "F;32NF", 4, 2.387939260590663e-38, 6.301941157072183e-36)
|
||||
|
||||
|
||||
class TestLibUnpack(PillowTestCase):
|
||||
|
@ -258,9 +290,12 @@ class TestLibUnpack(PillowTestCase):
|
|||
self.assert_unpack("RGB", "RGBX", 4, (1, 2, 3), (5, 6, 7), (9, 10, 11))
|
||||
self.assert_unpack("RGB", "RGBX;L", 4, (1, 4, 7), (2, 5, 8), (3, 6, 9))
|
||||
self.assert_unpack("RGB", "BGRX", 4, (3, 2, 1), (7, 6, 5), (11, 10, 9))
|
||||
self.assert_unpack("RGB", "XRGB", 4, (2, 3, 4), (6, 7, 8), (10, 11, 12))
|
||||
self.assert_unpack("RGB", "XBGR", 4, (4, 3, 2), (8, 7, 6), (12, 11, 10))
|
||||
self.assert_unpack("RGB", "YCC;P",
|
||||
self.assert_unpack(
|
||||
"RGB", "XRGB", 4, (2, 3, 4), (6, 7, 8), (10, 11, 12))
|
||||
self.assert_unpack(
|
||||
"RGB", "XBGR", 4, (4, 3, 2), (8, 7, 6), (12, 11, 10))
|
||||
self.assert_unpack(
|
||||
"RGB", "YCC;P",
|
||||
b'D]\x9c\x82\x1a\x91\xfaOC\xe7J\x12', # random data
|
||||
(127, 102, 0), (192, 227, 0), (213, 255, 170), (98, 255, 133))
|
||||
self.assert_unpack("RGB", "R", 1, (1, 0, 0), (2, 0, 0), (3, 0, 0))
|
||||
|
@ -268,130 +303,172 @@ class TestLibUnpack(PillowTestCase):
|
|||
self.assert_unpack("RGB", "B", 1, (0, 0, 1), (0, 0, 2), (0, 0, 3))
|
||||
|
||||
def test_RGBA(self):
|
||||
self.assert_unpack("RGBA", "LA", 2, (1, 1, 1, 2), (3, 3, 3, 4), (5, 5, 5, 6))
|
||||
self.assert_unpack("RGBA", "LA;16B", 4,
|
||||
(1, 1, 1, 3), (5, 5, 5, 7), (9, 9, 9, 11))
|
||||
self.assert_unpack("RGBA", "RGBA", 4,
|
||||
(1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12))
|
||||
self.assert_unpack("RGBA", "RGBa", 4,
|
||||
self.assert_unpack(
|
||||
"RGBA", "LA", 2, (1, 1, 1, 2), (3, 3, 3, 4), (5, 5, 5, 6))
|
||||
self.assert_unpack(
|
||||
"RGBA", "LA;16B", 4, (1, 1, 1, 3), (5, 5, 5, 7), (9, 9, 9, 11))
|
||||
self.assert_unpack(
|
||||
"RGBA", "RGBA", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12))
|
||||
self.assert_unpack(
|
||||
"RGBA", "RGBa", 4,
|
||||
(63, 127, 191, 4), (159, 191, 223, 8), (191, 212, 233, 12))
|
||||
self.assert_unpack("RGBA", "RGBa",
|
||||
self.assert_unpack(
|
||||
"RGBA", "RGBa",
|
||||
b'\x01\x02\x03\x00\x10\x20\x30\xff',
|
||||
(0, 0, 0, 0), (16, 32, 48, 255))
|
||||
self.assert_unpack("RGBA", "RGBa;16L", 8,
|
||||
self.assert_unpack(
|
||||
"RGBA", "RGBa;16L", 8,
|
||||
(63, 127, 191, 8), (159, 191, 223, 16), (191, 212, 233, 24))
|
||||
self.assert_unpack("RGBA", "RGBa;16L",
|
||||
self.assert_unpack(
|
||||
"RGBA", "RGBa;16L",
|
||||
b'\x88\x01\x88\x02\x88\x03\x88\x00'
|
||||
b'\x88\x10\x88\x20\x88\x30\x88\xff',
|
||||
(0, 0, 0, 0), (16, 32, 48, 255))
|
||||
self.assert_unpack("RGBA", "RGBa;16B", 8,
|
||||
self.assert_unpack(
|
||||
"RGBA", "RGBa;16B", 8,
|
||||
(36, 109, 182, 7), (153, 187, 221, 15), (188, 210, 232, 23))
|
||||
self.assert_unpack("RGBA", "RGBa;16B",
|
||||
self.assert_unpack(
|
||||
"RGBA", "RGBa;16B",
|
||||
b'\x01\x88\x02\x88\x03\x88\x00\x88'
|
||||
b'\x10\x88\x20\x88\x30\x88\xff\x88',
|
||||
(0, 0, 0, 0), (16, 32, 48, 255))
|
||||
self.assert_unpack("RGBA", "BGRa", 4,
|
||||
self.assert_unpack(
|
||||
"RGBA", "BGRa", 4,
|
||||
(191, 127, 63, 4), (223, 191, 159, 8), (233, 212, 191, 12))
|
||||
self.assert_unpack("RGBA", "BGRa",
|
||||
self.assert_unpack(
|
||||
"RGBA", "BGRa",
|
||||
b'\x01\x02\x03\x00\x10\x20\x30\xff',
|
||||
(0, 0, 0, 0), (48, 32, 16, 255))
|
||||
self.assert_unpack("RGBA", "RGBA;I", 4,
|
||||
self.assert_unpack(
|
||||
"RGBA", "RGBA;I", 4,
|
||||
(254, 253, 252, 4), (250, 249, 248, 8), (246, 245, 244, 12))
|
||||
self.assert_unpack("RGBA", "RGBA;L", 4,
|
||||
(1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12))
|
||||
self.assert_unpack(
|
||||
"RGBA", "RGBA;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12))
|
||||
self.assert_unpack("RGBA", "RGBA;15", 2, (8, 131, 0, 0), (24, 0, 8, 0))
|
||||
self.assert_unpack("RGBA", "BGRA;15", 2, (0, 131, 8, 0), (8, 0, 24, 0))
|
||||
self.assert_unpack("RGBA", "RGBA;4B", 2, (17, 0, 34, 0), (51, 0, 68, 0))
|
||||
self.assert_unpack("RGBA", "RGBA;16L", 8, (2, 4, 6, 8), (10, 12, 14, 16))
|
||||
self.assert_unpack("RGBA", "RGBA;16B", 8, (1, 3, 5, 7), (9, 11, 13, 15))
|
||||
self.assert_unpack("RGBA", "BGRA", 4,
|
||||
(3, 2, 1, 4), (7, 6, 5, 8), (11, 10, 9, 12))
|
||||
self.assert_unpack("RGBA", "ARGB", 4,
|
||||
(2, 3, 4, 1), (6, 7, 8, 5), (10, 11, 12, 9))
|
||||
self.assert_unpack("RGBA", "ABGR", 4,
|
||||
(4, 3, 2, 1), (8, 7, 6, 5), (12, 11, 10, 9))
|
||||
self.assert_unpack("RGBA", "YCCA;P",
|
||||
self.assert_unpack(
|
||||
"RGBA", "RGBA;4B", 2, (17, 0, 34, 0), (51, 0, 68, 0))
|
||||
self.assert_unpack(
|
||||
"RGBA", "RGBA;16L", 8, (2, 4, 6, 8), (10, 12, 14, 16))
|
||||
self.assert_unpack(
|
||||
"RGBA", "RGBA;16B", 8, (1, 3, 5, 7), (9, 11, 13, 15))
|
||||
self.assert_unpack(
|
||||
"RGBA", "BGRA", 4, (3, 2, 1, 4), (7, 6, 5, 8), (11, 10, 9, 12))
|
||||
self.assert_unpack(
|
||||
"RGBA", "ARGB", 4, (2, 3, 4, 1), (6, 7, 8, 5), (10, 11, 12, 9))
|
||||
self.assert_unpack(
|
||||
"RGBA", "ABGR", 4, (4, 3, 2, 1), (8, 7, 6, 5), (12, 11, 10, 9))
|
||||
self.assert_unpack(
|
||||
"RGBA", "YCCA;P",
|
||||
b']bE\x04\xdd\xbej\xed57T\xce\xac\xce:\x11', # random data
|
||||
(0, 161, 0, 4), (255, 255, 255, 237), (27, 158, 0, 206), (0, 118, 0, 17))
|
||||
self.assert_unpack("RGBA", "R", 1, (1, 0, 0, 0), (2, 0, 0, 0), (3, 0, 0, 0))
|
||||
self.assert_unpack("RGBA", "G", 1, (0, 1, 0, 0), (0, 2, 0, 0), (0, 3, 0, 0))
|
||||
self.assert_unpack("RGBA", "B", 1, (0, 0, 1, 0), (0, 0, 2, 0), (0, 0, 3, 0))
|
||||
self.assert_unpack("RGBA", "A", 1, (0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3))
|
||||
self.assert_unpack(
|
||||
"RGBA", "R", 1, (1, 0, 0, 0), (2, 0, 0, 0), (3, 0, 0, 0))
|
||||
self.assert_unpack(
|
||||
"RGBA", "G", 1, (0, 1, 0, 0), (0, 2, 0, 0), (0, 3, 0, 0))
|
||||
self.assert_unpack(
|
||||
"RGBA", "B", 1, (0, 0, 1, 0), (0, 0, 2, 0), (0, 0, 3, 0))
|
||||
self.assert_unpack(
|
||||
"RGBA", "A", 1, (0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3))
|
||||
|
||||
def test_RGBa(self):
|
||||
self.assert_unpack("RGBa", "RGBa", 4,
|
||||
(1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12))
|
||||
self.assert_unpack("RGBa", "BGRa", 4,
|
||||
(3, 2, 1, 4), (7, 6, 5, 8), (11, 10, 9, 12))
|
||||
self.assert_unpack("RGBa", "aRGB", 4,
|
||||
(2, 3, 4, 1), (6, 7, 8, 5), (10, 11, 12, 9))
|
||||
self.assert_unpack("RGBa", "aBGR", 4,
|
||||
(4, 3, 2, 1), (8, 7, 6, 5), (12, 11, 10, 9))
|
||||
self.assert_unpack(
|
||||
"RGBa", "RGBa", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12))
|
||||
self.assert_unpack(
|
||||
"RGBa", "BGRa", 4, (3, 2, 1, 4), (7, 6, 5, 8), (11, 10, 9, 12))
|
||||
self.assert_unpack(
|
||||
"RGBa", "aRGB", 4, (2, 3, 4, 1), (6, 7, 8, 5), (10, 11, 12, 9))
|
||||
self.assert_unpack(
|
||||
"RGBa", "aBGR", 4, (4, 3, 2, 1), (8, 7, 6, 5), (12, 11, 10, 9))
|
||||
|
||||
def test_RGBX(self):
|
||||
self.assert_unpack("RGBX", "RGB", 3, (1, 2, 3, X), (4, 5, 6, X), (7, 8, 9, X))
|
||||
self.assert_unpack("RGBX", "RGB;L", 3, (1, 4, 7, X), (2, 5, 8, X), (3, 6, 9, X))
|
||||
self.assert_unpack("RGBX", "RGB", 3,
|
||||
(1, 2, 3, X), (4, 5, 6, X), (7, 8, 9, X))
|
||||
self.assert_unpack("RGBX", "RGB;L", 3,
|
||||
(1, 4, 7, X), (2, 5, 8, X), (3, 6, 9, X))
|
||||
self.assert_unpack("RGBX", "RGB;16B", 6, (1, 3, 5, X), (7, 9, 11, X))
|
||||
self.assert_unpack("RGBX", "BGR", 3, (3, 2, 1, X), (6, 5, 4, X), (9, 8, 7, X))
|
||||
self.assert_unpack("RGBX", "BGR", 3,
|
||||
(3, 2, 1, X), (6, 5, 4, X), (9, 8, 7, X))
|
||||
self.assert_unpack("RGBX", "RGB;15", 2, (8, 131, 0, X), (24, 0, 8, X))
|
||||
self.assert_unpack("RGBX", "BGR;15", 2, (0, 131, 8, X), (8, 0, 24, X))
|
||||
self.assert_unpack("RGBX", "RGB;4B", 2, (17, 0, 34, X), (51, 0, 68, X))
|
||||
self.assert_unpack("RGBX", "RGBX", 4,
|
||||
(1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12))
|
||||
self.assert_unpack("RGBX", "RGBXX", 5,
|
||||
(1, 2, 3, 4), (6, 7, 8, 9), (11, 12, 13, 14))
|
||||
self.assert_unpack("RGBX", "RGBXXX", 6,
|
||||
(1, 2, 3, 4), (7, 8, 9, 10), (13, 14, 15, 16))
|
||||
self.assert_unpack("RGBX", "RGBX;L", 4,
|
||||
(1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12))
|
||||
self.assert_unpack("RGBX", "RGBX;16L", 8, (2, 4, 6, 8), (10, 12, 14, 16))
|
||||
self.assert_unpack("RGBX", "RGBX;16B", 8, (1, 3, 5, 7), (9, 11, 13, 15))
|
||||
self.assert_unpack("RGBX", "BGRX", 4, (3, 2, 1, X), (7, 6, 5, X), (11, 10, 9, X))
|
||||
self.assert_unpack("RGBX", "XRGB", 4, (2, 3, 4, X), (6, 7, 8, X), (10, 11, 12, X))
|
||||
self.assert_unpack("RGBX", "XBGR", 4, (4, 3, 2, X), (8, 7, 6, X), (12, 11, 10, X))
|
||||
self.assert_unpack("RGBX", "YCC;P",
|
||||
self.assert_unpack(
|
||||
"RGBX", "RGBX", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12))
|
||||
self.assert_unpack(
|
||||
"RGBX", "RGBXX", 5, (1, 2, 3, 4), (6, 7, 8, 9), (11, 12, 13, 14))
|
||||
self.assert_unpack(
|
||||
"RGBX", "RGBXXX", 6, (1, 2, 3, 4), (7, 8, 9, 10), (13, 14, 15, 16))
|
||||
self.assert_unpack(
|
||||
"RGBX", "RGBX;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12))
|
||||
self.assert_unpack("RGBX", "RGBX;16L", 8,
|
||||
(2, 4, 6, 8), (10, 12, 14, 16))
|
||||
self.assert_unpack("RGBX", "RGBX;16B", 8,
|
||||
(1, 3, 5, 7), (9, 11, 13, 15))
|
||||
self.assert_unpack("RGBX", "BGRX", 4,
|
||||
(3, 2, 1, X), (7, 6, 5, X), (11, 10, 9, X))
|
||||
self.assert_unpack("RGBX", "XRGB", 4,
|
||||
(2, 3, 4, X), (6, 7, 8, X), (10, 11, 12, X))
|
||||
self.assert_unpack("RGBX", "XBGR", 4,
|
||||
(4, 3, 2, X), (8, 7, 6, X), (12, 11, 10, X))
|
||||
self.assert_unpack(
|
||||
"RGBX", "YCC;P",
|
||||
b'D]\x9c\x82\x1a\x91\xfaOC\xe7J\x12', # random data
|
||||
(127, 102, 0, X), (192, 227, 0, X), (213, 255, 170, X), (98, 255, 133, X))
|
||||
self.assert_unpack("RGBX", "R", 1, (1, 0, 0, 0), (2, 0, 0, 0), (3, 0, 0, 0))
|
||||
self.assert_unpack("RGBX", "G", 1, (0, 1, 0, 0), (0, 2, 0, 0), (0, 3, 0, 0))
|
||||
self.assert_unpack("RGBX", "B", 1, (0, 0, 1, 0), (0, 0, 2, 0), (0, 0, 3, 0))
|
||||
self.assert_unpack("RGBX", "X", 1, (0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3))
|
||||
self.assert_unpack("RGBX", "R", 1,
|
||||
(1, 0, 0, 0), (2, 0, 0, 0), (3, 0, 0, 0))
|
||||
self.assert_unpack("RGBX", "G", 1,
|
||||
(0, 1, 0, 0), (0, 2, 0, 0), (0, 3, 0, 0))
|
||||
self.assert_unpack("RGBX", "B", 1,
|
||||
(0, 0, 1, 0), (0, 0, 2, 0), (0, 0, 3, 0))
|
||||
self.assert_unpack("RGBX", "X", 1,
|
||||
(0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3))
|
||||
|
||||
def test_CMYK(self):
|
||||
self.assert_unpack("CMYK", "CMYK", 4,
|
||||
(1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12))
|
||||
self.assert_unpack("CMYK", "CMYKX", 5,
|
||||
(1, 2, 3, 4), (6, 7, 8, 9), (11, 12, 13, 14))
|
||||
self.assert_unpack("CMYK", "CMYKXX", 6,
|
||||
(1, 2, 3, 4), (7, 8, 9, 10), (13, 14, 15, 16))
|
||||
self.assert_unpack("CMYK", "CMYK;I", 4,
|
||||
self.assert_unpack(
|
||||
"CMYK", "CMYK", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12))
|
||||
self.assert_unpack(
|
||||
"CMYK", "CMYKX", 5, (1, 2, 3, 4), (6, 7, 8, 9), (11, 12, 13, 14))
|
||||
self.assert_unpack(
|
||||
"CMYK", "CMYKXX", 6, (1, 2, 3, 4), (7, 8, 9, 10), (13, 14, 15, 16))
|
||||
self.assert_unpack(
|
||||
"CMYK", "CMYK;I", 4,
|
||||
(254, 253, 252, 251), (250, 249, 248, 247), (246, 245, 244, 243))
|
||||
self.assert_unpack("CMYK", "CMYK;L", 4,
|
||||
(1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12))
|
||||
self.assert_unpack("CMYK", "C", 1, (1, 0, 0, 0), (2, 0, 0, 0), (3, 0, 0, 0))
|
||||
self.assert_unpack("CMYK", "M", 1, (0, 1, 0, 0), (0, 2, 0, 0), (0, 3, 0, 0))
|
||||
self.assert_unpack("CMYK", "Y", 1, (0, 0, 1, 0), (0, 0, 2, 0), (0, 0, 3, 0))
|
||||
self.assert_unpack("CMYK", "K", 1, (0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3))
|
||||
self.assert_unpack("CMYK", "C;I", 1,
|
||||
(254, 0, 0, 0), (253, 0, 0, 0), (252, 0, 0, 0))
|
||||
self.assert_unpack("CMYK", "M;I", 1,
|
||||
(0, 254, 0, 0), (0, 253, 0, 0), (0, 252, 0, 0))
|
||||
self.assert_unpack("CMYK", "Y;I", 1,
|
||||
(0, 0, 254, 0), (0, 0, 253, 0), (0, 0, 252, 0))
|
||||
self.assert_unpack("CMYK", "K;I", 1,
|
||||
(0, 0, 0, 254), (0, 0, 0, 253), (0, 0, 0, 252))
|
||||
self.assert_unpack(
|
||||
"CMYK", "CMYK;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12))
|
||||
self.assert_unpack("CMYK", "C", 1,
|
||||
(1, 0, 0, 0), (2, 0, 0, 0), (3, 0, 0, 0))
|
||||
self.assert_unpack("CMYK", "M", 1,
|
||||
(0, 1, 0, 0), (0, 2, 0, 0), (0, 3, 0, 0))
|
||||
self.assert_unpack("CMYK", "Y", 1,
|
||||
(0, 0, 1, 0), (0, 0, 2, 0), (0, 0, 3, 0))
|
||||
self.assert_unpack("CMYK", "K", 1,
|
||||
(0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3))
|
||||
self.assert_unpack(
|
||||
"CMYK", "C;I", 1, (254, 0, 0, 0), (253, 0, 0, 0), (252, 0, 0, 0))
|
||||
self.assert_unpack(
|
||||
"CMYK", "M;I", 1, (0, 254, 0, 0), (0, 253, 0, 0), (0, 252, 0, 0))
|
||||
self.assert_unpack(
|
||||
"CMYK", "Y;I", 1, (0, 0, 254, 0), (0, 0, 253, 0), (0, 0, 252, 0))
|
||||
self.assert_unpack(
|
||||
"CMYK", "K;I", 1, (0, 0, 0, 254), (0, 0, 0, 253), (0, 0, 0, 252))
|
||||
|
||||
def test_YCbCr(self):
|
||||
self.assert_unpack("YCbCr", "YCbCr", 3, (1, 2, 3), (4, 5, 6), (7, 8, 9))
|
||||
self.assert_unpack("YCbCr", "YCbCr;L", 3, (1, 4, 7), (2, 5, 8), (3, 6, 9))
|
||||
self.assert_unpack("YCbCr", "YCbCrK", 4, (1, 2, 3), (5, 6, 7), (9, 10, 11))
|
||||
self.assert_unpack("YCbCr", "YCbCrX", 4, (1, 2, 3), (5, 6, 7), (9, 10, 11))
|
||||
self.assert_unpack("YCbCr", "YCbCrXX", 5, (1, 2, 3), (6, 7, 8), (11, 12, 13))
|
||||
self.assert_unpack("YCbCr", "YCbCrXXX", 6, (1, 2, 3), (7, 8, 9), (13, 14, 15))
|
||||
self.assert_unpack(
|
||||
"YCbCr", "YCbCr", 3, (1, 2, 3), (4, 5, 6), (7, 8, 9))
|
||||
self.assert_unpack(
|
||||
"YCbCr", "YCbCr;L", 3, (1, 4, 7), (2, 5, 8), (3, 6, 9))
|
||||
self.assert_unpack(
|
||||
"YCbCr", "YCbCrK", 4, (1, 2, 3), (5, 6, 7), (9, 10, 11))
|
||||
self.assert_unpack(
|
||||
"YCbCr", "YCbCrX", 4, (1, 2, 3), (5, 6, 7), (9, 10, 11))
|
||||
self.assert_unpack(
|
||||
"YCbCr", "YCbCrXX", 5, (1, 2, 3), (6, 7, 8), (11, 12, 13))
|
||||
self.assert_unpack(
|
||||
"YCbCr", "YCbCrXXX", 6, (1, 2, 3), (7, 8, 9), (13, 14, 15))
|
||||
|
||||
def test_LAB(self):
|
||||
self.assert_unpack("LAB", "LAB", 3,
|
||||
(1, 130, 131), (4, 133, 134), (7, 136, 137))
|
||||
self.assert_unpack(
|
||||
"LAB", "LAB", 3, (1, 130, 131), (4, 133, 134), (7, 136, 137))
|
||||
self.assert_unpack("LAB", "L", 1, (1, 0, 0), (2, 0, 0), (3, 0, 0))
|
||||
self.assert_unpack("LAB", "A", 1, (0, 1, 0), (0, 2, 0), (0, 3, 0))
|
||||
self.assert_unpack("LAB", "B", 1, (0, 0, 1), (0, 0, 2), (0, 0, 3))
|
||||
|
@ -410,30 +487,32 @@ class TestLibUnpack(PillowTestCase):
|
|||
self.assert_unpack("I", "I;16B", 2, 0x0102, 0x0304)
|
||||
self.assert_unpack("I", "I;16BS", b'\x83\x01\x01\x83', -31999, 0x0183)
|
||||
self.assert_unpack("I", "I;32", 4, 0x04030201, 0x08070605)
|
||||
self.assert_unpack("I", "I;32S",
|
||||
b'\x83\x00\x00\x01\x01\x00\x00\x83',
|
||||
0x01000083, -2097151999)
|
||||
self.assert_unpack(
|
||||
"I", "I;32S",
|
||||
b'\x83\x00\x00\x01\x01\x00\x00\x83', 0x01000083, -2097151999)
|
||||
self.assert_unpack("I", "I;32B", 4, 0x01020304, 0x05060708)
|
||||
self.assert_unpack("I", "I;32BS",
|
||||
b'\x83\x00\x00\x01\x01\x00\x00\x83',
|
||||
-2097151999, 0x01000083)
|
||||
self.assert_unpack(
|
||||
"I", "I;32BS",
|
||||
b'\x83\x00\x00\x01\x01\x00\x00\x83', -2097151999, 0x01000083)
|
||||
|
||||
if sys.byteorder == 'little':
|
||||
self.assert_unpack("I", "I", 4, 0x04030201, 0x08070605)
|
||||
self.assert_unpack("I", "I;16N", 2, 0x0201, 0x0403)
|
||||
self.assert_unpack("I", "I;16NS", b'\x83\x01\x01\x83', 0x0183, -31999)
|
||||
self.assert_unpack("I", "I;16NS",
|
||||
b'\x83\x01\x01\x83', 0x0183, -31999)
|
||||
self.assert_unpack("I", "I;32N", 4, 0x04030201, 0x08070605)
|
||||
self.assert_unpack("I", "I;32NS",
|
||||
b'\x83\x00\x00\x01\x01\x00\x00\x83',
|
||||
0x01000083, -2097151999)
|
||||
self.assert_unpack(
|
||||
"I", "I;32NS",
|
||||
b'\x83\x00\x00\x01\x01\x00\x00\x83', 0x01000083, -2097151999)
|
||||
else:
|
||||
self.assert_unpack("I", "I", 4, 0x01020304, 0x05060708)
|
||||
self.assert_unpack("I", "I;16N", 2, 0x0102, 0x0304)
|
||||
self.assert_unpack("I", "I;16NS", b'\x83\x01\x01\x83', -31999, 0x0183)
|
||||
self.assert_unpack("I", "I;16NS",
|
||||
b'\x83\x01\x01\x83', -31999, 0x0183)
|
||||
self.assert_unpack("I", "I;32N", 4, 0x01020304, 0x05060708)
|
||||
self.assert_unpack("I", "I;32NS",
|
||||
b'\x83\x00\x00\x01\x01\x00\x00\x83',
|
||||
-2097151999, 0x01000083)
|
||||
self.assert_unpack(
|
||||
"I", "I;32NS",
|
||||
b'\x83\x00\x00\x01\x01\x00\x00\x83', -2097151999, 0x01000083)
|
||||
|
||||
def test_F_int(self):
|
||||
self.assert_unpack("F", "F;8", 1, 0x01, 0x02, 0x03, 0x04)
|
||||
|
@ -443,55 +522,70 @@ class TestLibUnpack(PillowTestCase):
|
|||
self.assert_unpack("F", "F;16B", 2, 0x0102, 0x0304)
|
||||
self.assert_unpack("F", "F;16BS", b'\x83\x01\x01\x83', -31999, 0x0183)
|
||||
self.assert_unpack("F", "F;32", 4, 67305984, 134678016)
|
||||
self.assert_unpack("F", "F;32S",
|
||||
b'\x83\x00\x00\x01\x01\x00\x00\x83',
|
||||
16777348, -2097152000)
|
||||
self.assert_unpack(
|
||||
"F", "F;32S",
|
||||
b'\x83\x00\x00\x01\x01\x00\x00\x83', 16777348, -2097152000)
|
||||
self.assert_unpack("F", "F;32B", 4, 0x01020304, 0x05060708)
|
||||
self.assert_unpack("F", "F;32BS",
|
||||
b'\x83\x00\x00\x01\x01\x00\x00\x83',
|
||||
-2097152000, 16777348)
|
||||
self.assert_unpack(
|
||||
"F", "F;32BS",
|
||||
b'\x83\x00\x00\x01\x01\x00\x00\x83', -2097152000, 16777348)
|
||||
|
||||
if sys.byteorder == 'little':
|
||||
self.assert_unpack("F", "F;16N", 2, 0x0201, 0x0403)
|
||||
self.assert_unpack("F", "F;16NS", b'\x83\x01\x01\x83', 0x0183, -31999)
|
||||
self.assert_unpack(
|
||||
"F", "F;16NS",
|
||||
b'\x83\x01\x01\x83', 0x0183, -31999)
|
||||
self.assert_unpack("F", "F;32N", 4, 67305984, 134678016)
|
||||
self.assert_unpack("F", "F;32NS",
|
||||
b'\x83\x00\x00\x01\x01\x00\x00\x83',
|
||||
16777348, -2097152000)
|
||||
self.assert_unpack(
|
||||
"F", "F;32NS",
|
||||
b'\x83\x00\x00\x01\x01\x00\x00\x83', 16777348, -2097152000)
|
||||
else:
|
||||
self.assert_unpack("F", "F;16N", 2, 0x0102, 0x0304)
|
||||
self.assert_unpack("F", "F;16NS", b'\x83\x01\x01\x83', -31999, 0x0183)
|
||||
self.assert_unpack(
|
||||
"F", "F;16NS",
|
||||
b'\x83\x01\x01\x83', -31999, 0x0183)
|
||||
self.assert_unpack("F", "F;32N", 4, 0x01020304, 0x05060708)
|
||||
self.assert_unpack("F", "F;32NS",
|
||||
self.assert_unpack(
|
||||
"F", "F;32NS",
|
||||
b'\x83\x00\x00\x01\x01\x00\x00\x83',
|
||||
-2097152000, 16777348)
|
||||
|
||||
def test_F_float(self):
|
||||
self.assert_unpack("F", "F;32F", 4,
|
||||
self.assert_unpack(
|
||||
"F", "F;32F", 4,
|
||||
1.539989614439558e-36, 4.063216068939723e-34)
|
||||
self.assert_unpack("F", "F;32BF", 4,
|
||||
self.assert_unpack(
|
||||
"F", "F;32BF", 4,
|
||||
2.387939260590663e-38, 6.301941157072183e-36)
|
||||
self.assert_unpack("F", "F;64F",
|
||||
self.assert_unpack(
|
||||
"F", "F;64F",
|
||||
b'333333\xc3?\x00\x00\x00\x00\x00J\x93\xc0', # by struct.pack
|
||||
0.15000000596046448, -1234.5)
|
||||
self.assert_unpack("F", "F;64BF",
|
||||
self.assert_unpack(
|
||||
"F", "F;64BF",
|
||||
b'?\xc3333333\xc0\x93J\x00\x00\x00\x00\x00', # by struct.pack
|
||||
0.15000000596046448, -1234.5)
|
||||
|
||||
if sys.byteorder == 'little':
|
||||
self.assert_unpack("F", "F", 4,
|
||||
self.assert_unpack(
|
||||
"F", "F", 4,
|
||||
1.539989614439558e-36, 4.063216068939723e-34)
|
||||
self.assert_unpack("F", "F;32NF", 4,
|
||||
self.assert_unpack(
|
||||
"F", "F;32NF", 4,
|
||||
1.539989614439558e-36, 4.063216068939723e-34)
|
||||
self.assert_unpack("F", "F;64NF",
|
||||
self.assert_unpack(
|
||||
"F", "F;64NF",
|
||||
b'333333\xc3?\x00\x00\x00\x00\x00J\x93\xc0',
|
||||
0.15000000596046448, -1234.5)
|
||||
else:
|
||||
self.assert_unpack("F", "F", 4,
|
||||
self.assert_unpack(
|
||||
"F", "F", 4,
|
||||
2.387939260590663e-38, 6.301941157072183e-36)
|
||||
self.assert_unpack("F", "F;32NF", 4,
|
||||
self.assert_unpack(
|
||||
"F", "F;32NF", 4,
|
||||
2.387939260590663e-38, 6.301941157072183e-36)
|
||||
self.assert_unpack("F", "F;64NF",
|
||||
self.assert_unpack(
|
||||
"F", "F;64NF",
|
||||
b'?\xc3333333\xc0\x93J\x00\x00\x00\x00\x00',
|
||||
0.15000000596046448, -1234.5)
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ class TestLocale(PillowTestCase):
|
|||
Image.open(path)
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, "polish")
|
||||
except:
|
||||
except locale.Error:
|
||||
unittest.skip('Polish locale not available')
|
||||
Image.open(path)
|
||||
|
||||
|
|
|
@ -4,7 +4,8 @@ import sys
|
|||
from PIL import Image
|
||||
|
||||
|
||||
@unittest.skipIf(sys.platform.startswith('win32'), "Win32 does not call map_buffer")
|
||||
@unittest.skipIf(sys.platform.startswith('win32'),
|
||||
"Win32 does not call map_buffer")
|
||||
class TestMap(PillowTestCase):
|
||||
def test_overflow(self):
|
||||
# There is the potential to overflow comparisons in map.c
|
||||
|
|
|
@ -124,7 +124,9 @@ class TestNumpy(PillowTestCase):
|
|||
def test_save_tiff_uint16(self):
|
||||
# Tests that we're getting the pixel value in the right byte order.
|
||||
pixel_value = 0x1234
|
||||
a = numpy.array([pixel_value] * TEST_IMAGE_SIZE[0] * TEST_IMAGE_SIZE[1], dtype=numpy.uint16)
|
||||
a = numpy.array(
|
||||
[pixel_value] * TEST_IMAGE_SIZE[0] * TEST_IMAGE_SIZE[1],
|
||||
dtype=numpy.uint16)
|
||||
a.shape = TEST_IMAGE_SIZE
|
||||
img = Image.fromarray(a)
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
from helper import unittest, PillowTestCase
|
||||
|
||||
from PIL.PdfParser import IndirectObjectDef, IndirectReference, PdfBinary, PdfDict, PdfFormatError, PdfName, PdfParser, PdfStream, decode_text, encode_text, pdf_repr
|
||||
from PIL.PdfParser import IndirectObjectDef, IndirectReference, PdfBinary, \
|
||||
PdfDict, PdfFormatError, PdfName, PdfParser, \
|
||||
PdfStream, decode_text, encode_text, pdf_repr
|
||||
|
||||
|
||||
class TestPdfParser(PillowTestCase):
|
||||
|
@ -22,23 +24,35 @@ class TestPdfParser(PillowTestCase):
|
|||
self.assertNotEqual(IndirectObjectDef(1, 2), (1, 2))
|
||||
|
||||
def test_parsing(self):
|
||||
self.assertEqual(PdfParser.interpret_name(b"Name#23Hash"), b"Name#Hash")
|
||||
self.assertEqual(PdfParser.interpret_name(b"Name#23Hash"),
|
||||
b"Name#Hash")
|
||||
self.assertEqual(PdfParser.interpret_name(b"Name#23Hash", as_text=True), "Name#Hash")
|
||||
self.assertEqual(PdfParser.get_value(b"1 2 R ", 0), (IndirectReference(1, 2), 5))
|
||||
self.assertEqual(PdfParser.get_value(b"1 2 R ", 0),
|
||||
(IndirectReference(1, 2), 5))
|
||||
self.assertEqual(PdfParser.get_value(b"true[", 0), (True, 4))
|
||||
self.assertEqual(PdfParser.get_value(b"false%", 0), (False, 5))
|
||||
self.assertEqual(PdfParser.get_value(b"null<", 0), (None, 4))
|
||||
self.assertEqual(PdfParser.get_value(b"%cmt\n %cmt\n 123\n", 0), (123, 15))
|
||||
self.assertEqual(PdfParser.get_value(b"<901FA3>", 0), (b"\x90\x1F\xA3", 8))
|
||||
self.assertEqual(PdfParser.get_value(b"asd < 9 0 1 f A > qwe", 3), (b"\x90\x1F\xA0", 17))
|
||||
self.assertEqual(PdfParser.get_value(b"%cmt\n %cmt\n 123\n", 0),
|
||||
(123, 15))
|
||||
self.assertEqual(PdfParser.get_value(b"<901FA3>", 0),
|
||||
(b"\x90\x1F\xA3", 8))
|
||||
self.assertEqual(PdfParser.get_value(b"asd < 9 0 1 f A > qwe", 3),
|
||||
(b"\x90\x1F\xA0", 17))
|
||||
self.assertEqual(PdfParser.get_value(b"(asd)", 0), (b"asd", 5))
|
||||
self.assertEqual(PdfParser.get_value(b"(asd(qwe)zxc)zzz(aaa)", 0), (b"asd(qwe)zxc", 13))
|
||||
self.assertEqual(PdfParser.get_value(b"(Two \\\nwords.)", 0), (b"Two words.", 14))
|
||||
self.assertEqual(PdfParser.get_value(b"(Two\nlines.)", 0), (b"Two\nlines.", 12))
|
||||
self.assertEqual(PdfParser.get_value(b"(Two\r\nlines.)", 0), (b"Two\nlines.", 13))
|
||||
self.assertEqual(PdfParser.get_value(b"(Two\\nlines.)", 0), (b"Two\nlines.", 13))
|
||||
self.assertEqual(PdfParser.get_value(b"(One\\(paren).", 0), (b"One(paren", 12))
|
||||
self.assertEqual(PdfParser.get_value(b"(One\\)paren).", 0), (b"One)paren", 12))
|
||||
self.assertEqual(PdfParser.get_value(b"(asd(qwe)zxc)zzz(aaa)", 0),
|
||||
(b"asd(qwe)zxc", 13))
|
||||
self.assertEqual(PdfParser.get_value(b"(Two \\\nwords.)", 0),
|
||||
(b"Two words.", 14))
|
||||
self.assertEqual(PdfParser.get_value(b"(Two\nlines.)", 0),
|
||||
(b"Two\nlines.", 12))
|
||||
self.assertEqual(PdfParser.get_value(b"(Two\r\nlines.)", 0),
|
||||
(b"Two\nlines.", 13))
|
||||
self.assertEqual(PdfParser.get_value(b"(Two\\nlines.)", 0),
|
||||
(b"Two\nlines.", 13))
|
||||
self.assertEqual(PdfParser.get_value(b"(One\\(paren).", 0),
|
||||
(b"One(paren", 12))
|
||||
self.assertEqual(PdfParser.get_value(b"(One\\)paren).", 0),
|
||||
(b"One)paren", 12))
|
||||
self.assertEqual(PdfParser.get_value(b"(\\0053)", 0), (b"\x053", 7))
|
||||
self.assertEqual(PdfParser.get_value(b"(\\053)", 0), (b"\x2B", 6))
|
||||
self.assertEqual(PdfParser.get_value(b"(\\53)", 0), (b"\x2B", 5))
|
||||
|
@ -65,23 +79,30 @@ class TestPdfParser(PillowTestCase):
|
|||
|
||||
def test_pdf_repr(self):
|
||||
self.assertEqual(bytes(IndirectReference(1, 2)), b"1 2 R")
|
||||
self.assertEqual(bytes(IndirectObjectDef(*IndirectReference(1, 2))), b"1 2 obj")
|
||||
self.assertEqual(bytes(IndirectObjectDef(*IndirectReference(1, 2))),
|
||||
b"1 2 obj")
|
||||
self.assertEqual(bytes(PdfName(b"Name#Hash")), b"/Name#23Hash")
|
||||
self.assertEqual(bytes(PdfName("Name#Hash")), b"/Name#23Hash")
|
||||
self.assertEqual(bytes(PdfDict({b"Name": IndirectReference(1, 2)})), b"<<\n/Name 1 2 R\n>>")
|
||||
self.assertEqual(bytes(PdfDict({"Name": IndirectReference(1, 2)})), b"<<\n/Name 1 2 R\n>>")
|
||||
self.assertEqual(bytes(PdfDict({b"Name": IndirectReference(1, 2)})),
|
||||
b"<<\n/Name 1 2 R\n>>")
|
||||
self.assertEqual(bytes(PdfDict({"Name": IndirectReference(1, 2)})),
|
||||
b"<<\n/Name 1 2 R\n>>")
|
||||
self.assertEqual(pdf_repr(IndirectReference(1, 2)), b"1 2 R")
|
||||
self.assertEqual(pdf_repr(IndirectObjectDef(*IndirectReference(1, 2))), b"1 2 obj")
|
||||
self.assertEqual(pdf_repr(IndirectObjectDef(*IndirectReference(1, 2))),
|
||||
b"1 2 obj")
|
||||
self.assertEqual(pdf_repr(PdfName(b"Name#Hash")), b"/Name#23Hash")
|
||||
self.assertEqual(pdf_repr(PdfName("Name#Hash")), b"/Name#23Hash")
|
||||
self.assertEqual(pdf_repr(PdfDict({b"Name": IndirectReference(1, 2)})), b"<<\n/Name 1 2 R\n>>")
|
||||
self.assertEqual(pdf_repr(PdfDict({"Name": IndirectReference(1, 2)})), b"<<\n/Name 1 2 R\n>>")
|
||||
self.assertEqual(pdf_repr(PdfDict({b"Name": IndirectReference(1, 2)})),
|
||||
b"<<\n/Name 1 2 R\n>>")
|
||||
self.assertEqual(pdf_repr(PdfDict({"Name": IndirectReference(1, 2)})),
|
||||
b"<<\n/Name 1 2 R\n>>")
|
||||
self.assertEqual(pdf_repr(123), b"123")
|
||||
self.assertEqual(pdf_repr(True), b"true")
|
||||
self.assertEqual(pdf_repr(False), b"false")
|
||||
self.assertEqual(pdf_repr(None), b"null")
|
||||
self.assertEqual(pdf_repr(b"a)/b\\(c"), br"(a\)/b\\\(c)")
|
||||
self.assertEqual(pdf_repr([123, True, {"a": PdfName(b"b")}]), b"[ 123 true <<\n/a /b\n>> ]")
|
||||
self.assertEqual(pdf_repr([123, True, {"a": PdfName(b"b")}]),
|
||||
b"[ 123 true <<\n/a /b\n>> ]")
|
||||
self.assertEqual(pdf_repr(PdfBinary(b"\x90\x1F\xA0")), b"<901FA0>")
|
||||
|
||||
|
||||
|
|
39
docs/releasenotes/5.3.0.rst
Normal file
|
@ -0,0 +1,39 @@
|
|||
5.3.0
|
||||
-----
|
||||
|
||||
API Changes
|
||||
===========
|
||||
|
||||
Deprecations
|
||||
^^^^^^^^^^^^
|
||||
|
||||
These version constants have been deprecated. ``VERSION`` will be removed in
|
||||
Pillow 6.0.0, and ``PILLOW_VERSION`` will be removed after that.
|
||||
|
||||
* ``PIL.VERSION`` (old PIL version 1.1.7)
|
||||
* ``PIL.PILLOW_VERSION``
|
||||
* ``PIL.Image.VERSION``
|
||||
* ``PIL.Image.PILLOW_VERSION``
|
||||
|
||||
Use ``PIL.__version__`` instead.
|
||||
|
||||
API Additions
|
||||
=============
|
||||
|
||||
ImageOps.colorize
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
Previously ``ImageOps.colorize`` only supported two-color mapping with
|
||||
``black`` and ``white`` arguments being mapped to 0 and 255 respectively.
|
||||
Now it supports three-color mapping with the optional ``mid`` parameter, and
|
||||
the positions for all three color arguments can each be optionally specified
|
||||
(``blackpoint``, ``whitepoint`` and ``midpoint``).
|
||||
For example, with all optional arguments::
|
||||
ImageOps.colorize(im, black=(32, 37, 79), white='white', mid=(59, 101, 175),
|
||||
blackpoint=15, whitepoint=240, midpoint=100)
|
||||
|
||||
|
||||
|
||||
Other Changes
|
||||
=============
|
||||
|
|
@ -6,6 +6,7 @@ Release Notes
|
|||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
5.3.0
|
||||
5.2.0
|
||||
5.1.0
|
||||
5.0.0
|
||||
|
|
15
setup.py
|
@ -424,7 +424,8 @@ class pil_build_ext(build_ext):
|
|||
best_path = None
|
||||
for name in os.listdir(program_files):
|
||||
if name.startswith('OpenJPEG '):
|
||||
version = tuple(int(x) for x in name[9:].strip().split('.'))
|
||||
version = tuple(int(x) for x in
|
||||
name[9:].strip().split('.'))
|
||||
if version > best_version:
|
||||
best_version = version
|
||||
best_path = os.path.join(program_files, name)
|
||||
|
@ -501,7 +502,8 @@ class pil_build_ext(build_ext):
|
|||
# possible.
|
||||
_add_directory(self.compiler.include_dirs, best_path, 0)
|
||||
feature.jpeg2000 = 'openjp2'
|
||||
feature.openjpeg_version = '.'.join(str(x) for x in best_version)
|
||||
feature.openjpeg_version = '.'.join(str(x) for x in
|
||||
best_version)
|
||||
|
||||
if feature.want('imagequant'):
|
||||
_dbg('Looking for imagequant')
|
||||
|
@ -516,7 +518,8 @@ class pil_build_ext(build_ext):
|
|||
if _find_include_file(self, 'tiff.h'):
|
||||
if _find_library_file(self, "tiff"):
|
||||
feature.tiff = "tiff"
|
||||
if sys.platform == "win32" and _find_library_file(self, "libtiff"):
|
||||
if (sys.platform == "win32" and
|
||||
_find_library_file(self, "libtiff")):
|
||||
feature.tiff = "libtiff"
|
||||
if (sys.platform == "darwin" and
|
||||
_find_library_file(self, "libtiff")):
|
||||
|
@ -528,14 +531,16 @@ class pil_build_ext(build_ext):
|
|||
# look for freetype2 include files
|
||||
freetype_version = 0
|
||||
for subdir in self.compiler.include_dirs:
|
||||
_dbg('Checking for include file %s in %s', ("ft2build.h", subdir))
|
||||
_dbg('Checking for include file %s in %s',
|
||||
("ft2build.h", subdir))
|
||||
if os.path.isfile(os.path.join(subdir, "ft2build.h")):
|
||||
_dbg('Found %s in %s', ("ft2build.h", subdir))
|
||||
freetype_version = 21
|
||||
subdir = os.path.join(subdir, "freetype2")
|
||||
break
|
||||
subdir = os.path.join(subdir, "freetype2")
|
||||
_dbg('Checking for include file %s in %s', ("ft2build.h", subdir))
|
||||
_dbg('Checking for include file %s in %s',
|
||||
("ft2build.h", subdir))
|
||||
if os.path.isfile(os.path.join(subdir, "ft2build.h")):
|
||||
_dbg('Found %s in %s', ("ft2build.h", subdir))
|
||||
freetype_version = 21
|
||||
|
|
|
@ -60,7 +60,14 @@ class BmpImageFile(ImageFile.ImageFile):
|
|||
format_description = "Windows Bitmap"
|
||||
format = "BMP"
|
||||
# --------------------------------------------------- BMP Compression values
|
||||
COMPRESSIONS = {'RAW': 0, 'RLE8': 1, 'RLE4': 2, 'BITFIELDS': 3, 'JPEG': 4, 'PNG': 5}
|
||||
COMPRESSIONS = {
|
||||
'RAW': 0,
|
||||
'RLE8': 1,
|
||||
'RLE4': 2,
|
||||
'BITFIELDS': 3,
|
||||
'JPEG': 4,
|
||||
'PNG': 5
|
||||
}
|
||||
RAW, RLE8, RLE4, BITFIELDS, JPEG, PNG = 0, 1, 2, 3, 4, 5
|
||||
|
||||
def _bitmap(self, header=0, offset=0):
|
||||
|
@ -69,10 +76,13 @@ class BmpImageFile(ImageFile.ImageFile):
|
|||
if header:
|
||||
seek(header)
|
||||
file_info = {}
|
||||
file_info['header_size'] = i32(read(4)) # read bmp header size @offset 14 (this is part of the header size)
|
||||
# read bmp header size @offset 14 (this is part of the header size)
|
||||
file_info['header_size'] = i32(read(4))
|
||||
file_info['direction'] = -1
|
||||
# --------------------- If requested, read header at a specific position
|
||||
header_data = ImageFile._safe_read(self.fp, file_info['header_size'] - 4) # read the rest of the bmp header, without its size
|
||||
# read the rest of the bmp header, without its size
|
||||
header_data = ImageFile._safe_read(self.fp,
|
||||
file_info['header_size'] - 4)
|
||||
# --------------------------------------------------- IBM OS/2 Bitmap v1
|
||||
# ------ This format has different offsets because of width/height types
|
||||
if file_info['header_size'] == 12:
|
||||
|
@ -88,12 +98,16 @@ class BmpImageFile(ImageFile.ImageFile):
|
|||
file_info['y_flip'] = i8(header_data[7]) == 0xff
|
||||
file_info['direction'] = 1 if file_info['y_flip'] else -1
|
||||
file_info['width'] = i32(header_data[0:4])
|
||||
file_info['height'] = i32(header_data[4:8]) if not file_info['y_flip'] else 2**32 - i32(header_data[4:8])
|
||||
file_info['height'] = (i32(header_data[4:8])
|
||||
if not file_info['y_flip']
|
||||
else 2**32 - i32(header_data[4:8]))
|
||||
file_info['planes'] = i16(header_data[8:10])
|
||||
file_info['bits'] = i16(header_data[10:12])
|
||||
file_info['compression'] = i32(header_data[12:16])
|
||||
file_info['data_size'] = i32(header_data[16:20]) # byte size of pixel data
|
||||
file_info['pixels_per_meter'] = (i32(header_data[20:24]), i32(header_data[24:28]))
|
||||
# byte size of pixel data
|
||||
file_info['data_size'] = i32(header_data[16:20])
|
||||
file_info['pixels_per_meter'] = (i32(header_data[20:24]),
|
||||
i32(header_data[24:28]))
|
||||
file_info['colors'] = i32(header_data[28:32])
|
||||
file_info['palette_padding'] = 4
|
||||
self.info["dpi"] = tuple(
|
||||
|
@ -101,21 +115,32 @@ class BmpImageFile(ImageFile.ImageFile):
|
|||
file_info['pixels_per_meter']))
|
||||
if file_info['compression'] == self.BITFIELDS:
|
||||
if len(header_data) >= 52:
|
||||
for idx, mask in enumerate(['r_mask', 'g_mask', 'b_mask', 'a_mask']):
|
||||
for idx, mask in enumerate(['r_mask',
|
||||
'g_mask',
|
||||
'b_mask',
|
||||
'a_mask']):
|
||||
file_info[mask] = i32(header_data[36+idx*4:40+idx*4])
|
||||
else:
|
||||
# 40 byte headers only have the three components in the bitfields masks,
|
||||
# 40 byte headers only have the three components in the
|
||||
# bitfields masks,
|
||||
# ref: https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
|
||||
# See also https://github.com/python-pillow/Pillow/issues/1293
|
||||
# There is a 4th component in the RGBQuad, in the alpha location, but it
|
||||
# is listed as a reserved component, and it is not generally an alpha channel
|
||||
# There is a 4th component in the RGBQuad, in the alpha
|
||||
# location, but it is listed as a reserved component,
|
||||
# and it is not generally an alpha channel
|
||||
file_info['a_mask'] = 0x0
|
||||
for mask in ['r_mask', 'g_mask', 'b_mask']:
|
||||
file_info[mask] = i32(read(4))
|
||||
file_info['rgb_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask'])
|
||||
file_info['rgba_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask'], file_info['a_mask'])
|
||||
file_info['rgb_mask'] = (file_info['r_mask'],
|
||||
file_info['g_mask'],
|
||||
file_info['b_mask'])
|
||||
file_info['rgba_mask'] = (file_info['r_mask'],
|
||||
file_info['g_mask'],
|
||||
file_info['b_mask'],
|
||||
file_info['a_mask'])
|
||||
else:
|
||||
raise IOError("Unsupported BMP header type (%d)" % file_info['header_size'])
|
||||
raise IOError("Unsupported BMP header type (%d)" %
|
||||
file_info['header_size'])
|
||||
# ------------------ Special case : header is reported 40, which
|
||||
# ---------------------- is shorter than real size for bpp >= 16
|
||||
self.size = file_info['width'], file_info['height']
|
||||
|
@ -127,11 +152,15 @@ class BmpImageFile(ImageFile.ImageFile):
|
|||
# ----------------------- Check bit depth for unusual unsupported values
|
||||
self.mode, raw_mode = BIT2MODE.get(file_info['bits'], (None, None))
|
||||
if self.mode is None:
|
||||
raise IOError("Unsupported BMP pixel depth (%d)" % file_info['bits'])
|
||||
raise IOError("Unsupported BMP pixel depth (%d)"
|
||||
% file_info['bits'])
|
||||
# ----------------- Process BMP with Bitfields compression (not palette)
|
||||
if file_info['compression'] == self.BITFIELDS:
|
||||
SUPPORTED = {
|
||||
32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000), (0x0, 0x0, 0x0, 0x0), (0xff000000, 0xff0000, 0xff00, 0x0)],
|
||||
32: [(0xff0000, 0xff00, 0xff, 0x0),
|
||||
(0xff0000, 0xff00, 0xff, 0xff000000),
|
||||
(0x0, 0x0, 0x0, 0x0),
|
||||
(0xff000000, 0xff0000, 0xff00, 0x0)],
|
||||
24: [(0xff0000, 0xff00, 0xff)],
|
||||
16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)]
|
||||
}
|
||||
|
@ -145,11 +174,15 @@ class BmpImageFile(ImageFile.ImageFile):
|
|||
(16, (0x7c00, 0x3e0, 0x1f)): "BGR;15"
|
||||
}
|
||||
if file_info['bits'] in SUPPORTED:
|
||||
if file_info['bits'] == 32 and file_info['rgba_mask'] in SUPPORTED[file_info['bits']]:
|
||||
if file_info['bits'] == 32 and \
|
||||
file_info['rgba_mask'] in SUPPORTED[file_info['bits']]:
|
||||
raw_mode = MASK_MODES[(file_info['bits'], file_info['rgba_mask'])]
|
||||
self.mode = "RGBA" if raw_mode in ("BGRA",) else self.mode
|
||||
elif file_info['bits'] in (24, 16) and file_info['rgb_mask'] in SUPPORTED[file_info['bits']]:
|
||||
raw_mode = MASK_MODES[(file_info['bits'], file_info['rgb_mask'])]
|
||||
elif (file_info['bits'] in (24, 16) and
|
||||
file_info['rgb_mask'] in SUPPORTED[file_info['bits']]):
|
||||
raw_mode = MASK_MODES[
|
||||
(file_info['bits'], file_info['rgb_mask'])
|
||||
]
|
||||
else:
|
||||
raise IOError("Unsupported BMP bitfields layout")
|
||||
else:
|
||||
|
@ -158,17 +191,20 @@ class BmpImageFile(ImageFile.ImageFile):
|
|||
if file_info['bits'] == 32 and header == 22: # 32-bit .cur offset
|
||||
raw_mode, self.mode = "BGRA", "RGBA"
|
||||
else:
|
||||
raise IOError("Unsupported BMP compression (%d)" % file_info['compression'])
|
||||
raise IOError("Unsupported BMP compression (%d)" %
|
||||
file_info['compression'])
|
||||
# ---------------- Once the header is processed, process the palette/LUT
|
||||
if self.mode == "P": # Paletted for 1, 4 and 8 bit images
|
||||
# ----------------------------------------------------- 1-bit images
|
||||
if not (0 < file_info['colors'] <= 65536):
|
||||
raise IOError("Unsupported BMP Palette size (%d)" % file_info['colors'])
|
||||
raise IOError("Unsupported BMP Palette size (%d)" %
|
||||
file_info['colors'])
|
||||
else:
|
||||
padding = file_info['palette_padding']
|
||||
palette = read(padding * file_info['colors'])
|
||||
greyscale = True
|
||||
indices = (0, 255) if file_info['colors'] == 2 else list(range(file_info['colors']))
|
||||
indices = (0, 255) if file_info['colors'] == 2 else \
|
||||
list(range(file_info['colors']))
|
||||
# ------------------ Check if greyscale and ignore palette if so
|
||||
for ind, val in enumerate(indices):
|
||||
rgb = palette[ind*padding:ind*padding + 3]
|
||||
|
@ -180,13 +216,19 @@ class BmpImageFile(ImageFile.ImageFile):
|
|||
raw_mode = self.mode
|
||||
else:
|
||||
self.mode = "P"
|
||||
self.palette = ImagePalette.raw("BGRX" if padding == 4 else "BGR", palette)
|
||||
self.palette = ImagePalette.raw(
|
||||
"BGRX" if padding == 4 else "BGR", palette)
|
||||
|
||||
# ----------------------------- Finally set the tile data for the plugin
|
||||
self.info['compression'] = file_info['compression']
|
||||
self.tile = [('raw', (0, 0, file_info['width'], file_info['height']), offset or self.fp.tell(),
|
||||
(raw_mode, ((file_info['width'] * file_info['bits'] + 31) >> 3) & (~3), file_info['direction'])
|
||||
)]
|
||||
self.tile = [
|
||||
('raw',
|
||||
(0, 0, file_info['width'], file_info['height']),
|
||||
offset or self.fp.tell(),
|
||||
(raw_mode,
|
||||
((file_info['width'] * file_info['bits'] + 31) >> 3) & (~3),
|
||||
file_info['direction']))
|
||||
]
|
||||
|
||||
def _open(self):
|
||||
""" Open file, check magic number and read header """
|
||||
|
|
|
@ -107,10 +107,10 @@ class ContainerIO(object):
|
|||
|
||||
:returns: A list of 8-bit strings.
|
||||
"""
|
||||
l = []
|
||||
lines = []
|
||||
while True:
|
||||
s = self.readline()
|
||||
if not s:
|
||||
break
|
||||
l.append(s)
|
||||
return l
|
||||
lines.append(s)
|
||||
return lines
|
||||
|
|
|
@ -142,7 +142,8 @@ class DdsImageFile(ImageFile.ImageFile):
|
|||
# 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):
|
||||
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:
|
||||
|
|
|
@ -26,7 +26,6 @@ import os
|
|||
import sys
|
||||
from . import Image, ImageFile
|
||||
from ._binary import i32le as i32
|
||||
from ._util import py3
|
||||
|
||||
__version__ = "0.5"
|
||||
|
||||
|
@ -206,16 +205,7 @@ class EpsImageFile(ImageFile.ImageFile):
|
|||
|
||||
# Rewrap the open file pointer in something that will
|
||||
# convert line endings and decode to latin-1.
|
||||
try:
|
||||
if py3:
|
||||
# Python3, can use bare open command.
|
||||
fp = open(self.fp.name, "Ur", encoding='latin-1')
|
||||
else:
|
||||
# Python2, no encoding conversion necessary
|
||||
fp = open(self.fp.name, "Ur")
|
||||
except:
|
||||
# Expect this for bytesio/stringio
|
||||
fp = PSFile(self.fp)
|
||||
fp = PSFile(self.fp)
|
||||
|
||||
# go to offset - start of "%!PS"
|
||||
fp.seek(offset)
|
||||
|
|
|
@ -9,7 +9,8 @@ Full text of the CC0 license:
|
|||
Independence War 2: Edge Of Chaos - Texture File Format - 16 October 2001
|
||||
|
||||
The textures used for 3D objects in Independence War 2: Edge Of Chaos are in a
|
||||
packed custom format called FTEX. This file format uses file extensions FTC and FTU.
|
||||
packed custom format called FTEX. This file format uses file extensions FTC
|
||||
and FTU.
|
||||
* FTC files are compressed textures (using standard texture compression).
|
||||
* FTU files are not compressed.
|
||||
Texture File Format
|
||||
|
@ -24,18 +25,21 @@ Where:
|
|||
* The "magic" number is "FTEX".
|
||||
* "width" and "height" are the dimensions of the texture.
|
||||
* "mipmap_count" is the number of mipmaps in the texture.
|
||||
* "format_count" is the number of texture formats (different versions of the same texture) in this file.
|
||||
* "format_count" is the number of texture formats (different versions of the
|
||||
same texture) in this file.
|
||||
|
||||
{format_directory} = format_count * { u32:format, u32:where }
|
||||
|
||||
The format value is 0 for DXT1 compressed textures and 1 for 24-bit RGB uncompressed textures.
|
||||
The format value is 0 for DXT1 compressed textures and 1 for 24-bit RGB
|
||||
uncompressed textures.
|
||||
The texture data for a format starts at the position "where" in the file.
|
||||
|
||||
Each set of texture data in the file has the following structure:
|
||||
{data} = format_count * { u32:mipmap_size, mipmap_size * { u8 } }
|
||||
* "mipmap_size" is the number of bytes in that mip level. For compressed textures this is the
|
||||
size of the texture data compressed with DXT1. For 24 bit uncompressed textures, this is 3 * width * height.
|
||||
Following this are the image bytes for that mipmap level.
|
||||
* "mipmap_size" is the number of bytes in that mip level. For compressed
|
||||
textures this is the size of the texture data compressed with DXT1. For 24 bit
|
||||
uncompressed textures, this is 3 * width * height. Following this are the image
|
||||
bytes for that mipmap level.
|
||||
|
||||
Note: All data is stored in little-Endian (Intel) byte order.
|
||||
"""
|
||||
|
@ -62,7 +66,8 @@ class FtexImageFile(ImageFile.ImageFile):
|
|||
|
||||
self.mode = "RGB"
|
||||
|
||||
# Only support single-format files. I don't know of any multi-format file.
|
||||
# Only support single-format files.
|
||||
# I don't know of any multi-format file.
|
||||
assert format_count == 1
|
||||
|
||||
format, where = struct.unpack("<2i", self.fp.read(8))
|
||||
|
@ -77,7 +82,8 @@ class FtexImageFile(ImageFile.ImageFile):
|
|||
elif format == FORMAT_UNCOMPRESSED:
|
||||
self.tile = [("raw", (0, 0) + self.size, 0, ('RGB', 0, 1))]
|
||||
else:
|
||||
raise ValueError("Invalid texture compression format: %r" % (format))
|
||||
raise ValueError(
|
||||
"Invalid texture compression format: %r" % (format))
|
||||
|
||||
self.fp.close()
|
||||
self.fp = BytesIO(data)
|
||||
|
|
|
@ -29,7 +29,8 @@ from ._binary import i32be as i32
|
|||
|
||||
|
||||
def _accept(prefix):
|
||||
return len(prefix) >= 8 and i32(prefix[:4]) >= 20 and i32(prefix[4:8]) in (1, 2)
|
||||
return len(prefix) >= 8 and \
|
||||
i32(prefix[:4]) >= 20 and i32(prefix[4:8]) in (1, 2)
|
||||
|
||||
|
||||
##
|
||||
|
@ -54,7 +55,8 @@ class GbrImageFile(ImageFile.ImageFile):
|
|||
if width <= 0 or height <= 0:
|
||||
raise SyntaxError("not a GIMP brush")
|
||||
if color_depth not in (1, 4):
|
||||
raise SyntaxError("Unsupported GIMP brush color depth: %s" % color_depth)
|
||||
raise SyntaxError(
|
||||
"Unsupported GIMP brush color depth: %s" % color_depth)
|
||||
|
||||
if version == 1:
|
||||
comment_length = header_size-20
|
||||
|
|
|
@ -397,7 +397,8 @@ def _write_multiple_frames(im, fp, palette):
|
|||
|
||||
im_frames = []
|
||||
frame_count = 0
|
||||
for imSequence in itertools.chain([im], im.encoderinfo.get("append_images", [])):
|
||||
for imSequence in itertools.chain([im],
|
||||
im.encoderinfo.get("append_images", [])):
|
||||
for im_frame in ImageSequence.Iterator(imSequence):
|
||||
# a copy is required here since seek can still mutate the image
|
||||
im_frame = _normalize_mode(im_frame.copy())
|
||||
|
@ -413,17 +414,19 @@ def _write_multiple_frames(im, fp, palette):
|
|||
if im_frames:
|
||||
# delta frame
|
||||
previous = im_frames[-1]
|
||||
if _get_palette_bytes(im_frame) == _get_palette_bytes(previous['im']):
|
||||
if _get_palette_bytes(im_frame) == \
|
||||
_get_palette_bytes(previous['im']):
|
||||
delta = ImageChops.subtract_modulo(im_frame,
|
||||
previous['im'])
|
||||
else:
|
||||
delta = ImageChops.subtract_modulo(im_frame.convert('RGB'),
|
||||
previous['im'].convert('RGB'))
|
||||
delta = ImageChops.subtract_modulo(
|
||||
im_frame.convert('RGB'), previous['im'].convert('RGB'))
|
||||
bbox = delta.getbbox()
|
||||
if not bbox:
|
||||
# This frame is identical to the previous frame
|
||||
if duration:
|
||||
previous['encoderinfo']['duration'] += encoderinfo['duration']
|
||||
previous['encoderinfo']['duration'] += \
|
||||
encoderinfo['duration']
|
||||
continue
|
||||
else:
|
||||
bbox = None
|
||||
|
@ -525,7 +528,8 @@ def _write_local_header(fp, im, offset, flags):
|
|||
o8(transparency) + # transparency index
|
||||
o8(0))
|
||||
|
||||
if "comment" in im.encoderinfo and 1 <= len(im.encoderinfo["comment"]) <= 255:
|
||||
if "comment" in im.encoderinfo and \
|
||||
1 <= len(im.encoderinfo["comment"]) <= 255:
|
||||
fp.write(b"!" +
|
||||
o8(254) + # extension intro
|
||||
o8(len(im.encoderinfo["comment"])) +
|
||||
|
@ -543,7 +547,6 @@ def _write_local_header(fp, im, offset, flags):
|
|||
o8(0))
|
||||
include_color_table = im.encoderinfo.get('include_color_table')
|
||||
if include_color_table:
|
||||
palette = im.encoderinfo.get("palette", None)
|
||||
palette_bytes = _get_palette_bytes(im)
|
||||
color_table_size = _get_color_table_size(palette_bytes)
|
||||
if color_table_size:
|
||||
|
@ -692,7 +695,8 @@ def _get_global_header(im, info):
|
|||
for extensionKey in ["transparency", "duration", "loop", "comment"]:
|
||||
if info and extensionKey in info:
|
||||
if ((extensionKey == "duration" and info[extensionKey] == 0) or
|
||||
(extensionKey == "comment" and not (1 <= len(info[extensionKey]) <= 255))):
|
||||
(extensionKey == "comment" and
|
||||
not (1 <= len(info[extensionKey]) <= 255))):
|
||||
continue
|
||||
version = b"89a"
|
||||
break
|
||||
|
|
|
@ -305,10 +305,10 @@ def profileToProfile(
|
|||
:param renderingIntent: Integer (0-3) specifying the rendering intent you
|
||||
wish to use for the transform
|
||||
|
||||
INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL)
|
||||
INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC)
|
||||
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
|
||||
INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
|
||||
ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT)
|
||||
ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1
|
||||
ImageCms.INTENT_SATURATION = 2
|
||||
ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3
|
||||
|
||||
see the pyCMS documentation for details on rendering intents and what
|
||||
they do.
|
||||
|
@ -424,10 +424,10 @@ def buildTransform(
|
|||
:param renderingIntent: Integer (0-3) specifying the rendering intent you
|
||||
wish to use for the transform
|
||||
|
||||
INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL)
|
||||
INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC)
|
||||
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
|
||||
INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
|
||||
ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT)
|
||||
ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1
|
||||
ImageCms.INTENT_SATURATION = 2
|
||||
ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3
|
||||
|
||||
see the pyCMS documentation for details on rendering intents and what
|
||||
they do.
|
||||
|
@ -512,20 +512,20 @@ def buildProofTransform(
|
|||
:param renderingIntent: Integer (0-3) specifying the rendering intent you
|
||||
wish to use for the input->proof (simulated) transform
|
||||
|
||||
INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL)
|
||||
INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC)
|
||||
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
|
||||
INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
|
||||
ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT)
|
||||
ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1
|
||||
ImageCms.INTENT_SATURATION = 2
|
||||
ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3
|
||||
|
||||
see the pyCMS documentation for details on rendering intents and what
|
||||
they do.
|
||||
:param proofRenderingIntent: Integer (0-3) specifying the rendering intent you
|
||||
wish to use for proof->output transform
|
||||
:param proofRenderingIntent: Integer (0-3) specifying the rendering intent
|
||||
you wish to use for proof->output transform
|
||||
|
||||
INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL)
|
||||
INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC)
|
||||
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
|
||||
INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
|
||||
ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT)
|
||||
ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1
|
||||
ImageCms.INTENT_SATURATION = 2
|
||||
ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3
|
||||
|
||||
see the pyCMS documentation for details on rendering intents and what
|
||||
they do.
|
||||
|
@ -875,10 +875,10 @@ def getDefaultIntent(profile):
|
|||
:returns: Integer 0-3 specifying the default rendering intent for this
|
||||
profile.
|
||||
|
||||
INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL)
|
||||
INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC)
|
||||
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
|
||||
INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
|
||||
ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT)
|
||||
ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1
|
||||
ImageCms.INTENT_SATURATION = 2
|
||||
ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3
|
||||
|
||||
see the pyCMS documentation for details on rendering intents and what
|
||||
they do.
|
||||
|
@ -913,15 +913,15 @@ def isIntentSupported(profile, intent, direction):
|
|||
:param intent: Integer (0-3) specifying the rendering intent you wish to
|
||||
use with this profile
|
||||
|
||||
INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL)
|
||||
INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC)
|
||||
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
|
||||
INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
|
||||
ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT)
|
||||
ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1
|
||||
ImageCms.INTENT_SATURATION = 2
|
||||
ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3
|
||||
|
||||
see the pyCMS documentation for details on rendering intents and what
|
||||
they do.
|
||||
:param direction: Integer specifying if the profile is to be used for input,
|
||||
output, or proof
|
||||
:param direction: Integer specifying if the profile is to be used for
|
||||
input, output, or proof
|
||||
|
||||
INPUT = 0 (or use ImageCms.DIRECTION_INPUT)
|
||||
OUTPUT = 1 (or use ImageCms.DIRECTION_OUTPUT)
|
||||
|
|
|
@ -138,7 +138,7 @@ class ImageDraw(object):
|
|||
ink, fill = self._getink(outline, fill)
|
||||
if fill is not None:
|
||||
self.draw.draw_chord(xy, start, end, fill, 1)
|
||||
if ink is not None:
|
||||
if ink is not None and ink != fill:
|
||||
self.draw.draw_chord(xy, start, end, ink, 0, width)
|
||||
|
||||
def ellipse(self, xy, fill=None, outline=None, width=0):
|
||||
|
@ -146,7 +146,7 @@ class ImageDraw(object):
|
|||
ink, fill = self._getink(outline, fill)
|
||||
if fill is not None:
|
||||
self.draw.draw_ellipse(xy, fill, 1)
|
||||
if ink is not None:
|
||||
if ink is not None and ink != fill:
|
||||
self.draw.draw_ellipse(xy, ink, 0, width)
|
||||
|
||||
def line(self, xy, fill=None, width=0):
|
||||
|
@ -161,7 +161,7 @@ class ImageDraw(object):
|
|||
ink, fill = self._getink(outline, fill)
|
||||
if fill is not None:
|
||||
self.draw.draw_outline(shape, fill, 1)
|
||||
if ink is not None:
|
||||
if ink is not None and ink != fill:
|
||||
self.draw.draw_outline(shape, ink, 0)
|
||||
|
||||
def pieslice(self, xy, start, end, fill=None, outline=None, width=0):
|
||||
|
@ -169,7 +169,7 @@ class ImageDraw(object):
|
|||
ink, fill = self._getink(outline, fill)
|
||||
if fill is not None:
|
||||
self.draw.draw_pieslice(xy, start, end, fill, 1)
|
||||
if ink is not None:
|
||||
if ink is not None and ink != fill:
|
||||
self.draw.draw_pieslice(xy, start, end, ink, 0, width)
|
||||
|
||||
def point(self, xy, fill=None):
|
||||
|
@ -183,7 +183,7 @@ class ImageDraw(object):
|
|||
ink, fill = self._getink(outline, fill)
|
||||
if fill is not None:
|
||||
self.draw.draw_polygon(xy, fill, 1)
|
||||
if ink is not None:
|
||||
if ink is not None and ink != fill:
|
||||
self.draw.draw_polygon(xy, ink, 0)
|
||||
|
||||
def rectangle(self, xy, fill=None, outline=None, width=0):
|
||||
|
@ -191,7 +191,7 @@ class ImageDraw(object):
|
|||
ink, fill = self._getink(outline, fill)
|
||||
if fill is not None:
|
||||
self.draw.draw_rectangle(xy, fill, 1)
|
||||
if ink is not None:
|
||||
if ink is not None and ink != fill:
|
||||
self.draw.draw_rectangle(xy, ink, 0, width)
|
||||
|
||||
def _multiline_check(self, text):
|
||||
|
@ -217,7 +217,8 @@ class ImageDraw(object):
|
|||
ink = fill
|
||||
if ink is not None:
|
||||
try:
|
||||
mask, offset = font.getmask2(text, self.fontmode, *args, **kwargs)
|
||||
mask, offset = font.getmask2(text, self.fontmode,
|
||||
*args, **kwargs)
|
||||
xy = xy[0] + offset[0], xy[1] + offset[1]
|
||||
except AttributeError:
|
||||
try:
|
||||
|
@ -384,4 +385,4 @@ def _color_diff(rgb1, rgb2):
|
|||
"""
|
||||
Uses 1-norm distance to calculate difference between two rgb values.
|
||||
"""
|
||||
return abs(rgb1[0]-rgb2[0]) + abs(rgb1[1]-rgb2[1]) + abs(rgb1[2]-rgb2[2])
|
||||
return abs(rgb1[0]-rgb2[0]) + abs(rgb1[1]-rgb2[1]) + abs(rgb1[2]-rgb2[2])
|
||||
|
|
|
@ -51,7 +51,8 @@ class Color(_Enhance):
|
|||
if 'A' in image.getbands():
|
||||
self.intermediate_mode = 'LA'
|
||||
|
||||
self.degenerate = image.convert(self.intermediate_mode).convert(image.mode)
|
||||
self.degenerate = image.convert(
|
||||
self.intermediate_mode).convert(image.mode)
|
||||
|
||||
|
||||
class Contrast(_Enhance):
|
||||
|
|
|
@ -166,8 +166,9 @@ class ImageFile(Image.Image):
|
|||
if use_mmap:
|
||||
# try memory mapping
|
||||
decoder_name, extents, offset, args = self.tile[0]
|
||||
if decoder_name == "raw" and len(args) >= 3 and args[0] == self.mode \
|
||||
and args[0] in Image._MAPMODES:
|
||||
if decoder_name == "raw" and len(args) >= 3 and \
|
||||
args[0] == self.mode and \
|
||||
args[0] in Image._MAPMODES:
|
||||
try:
|
||||
if hasattr(Image.core, "map"):
|
||||
# use built-in mapper WIN32 only
|
||||
|
@ -180,12 +181,14 @@ class ImageFile(Image.Image):
|
|||
# use mmap, if possible
|
||||
import mmap
|
||||
with open(self.filename, "r") as fp:
|
||||
self.map = mmap.mmap(fp.fileno(), 0, access=mmap.ACCESS_READ)
|
||||
self.map = mmap.mmap(fp.fileno(), 0,
|
||||
access=mmap.ACCESS_READ)
|
||||
self.im = Image.core.map_buffer(
|
||||
self.map, self.size, decoder_name, extents, offset, args
|
||||
)
|
||||
self.map, self.size, decoder_name, extents,
|
||||
offset, args)
|
||||
readonly = 1
|
||||
# After trashing self.im, we might need to reload the palette data.
|
||||
# After trashing self.im,
|
||||
# we might need to reload the palette data.
|
||||
if self.palette:
|
||||
self.palette.dirty = 1
|
||||
except (AttributeError, EnvironmentError, ImportError):
|
||||
|
@ -217,7 +220,8 @@ class ImageFile(Image.Image):
|
|||
while True:
|
||||
try:
|
||||
s = read(self.decodermaxblock)
|
||||
except (IndexError, struct.error): # truncated png/gif
|
||||
except (IndexError, struct.error):
|
||||
# truncated png/gif
|
||||
if LOAD_TRUNCATED_IMAGES:
|
||||
break
|
||||
else:
|
||||
|
@ -229,7 +233,8 @@ class ImageFile(Image.Image):
|
|||
else:
|
||||
self.tile = []
|
||||
raise IOError("image file is truncated "
|
||||
"(%d bytes not processed)" % len(b))
|
||||
"(%d bytes not processed)" %
|
||||
len(b))
|
||||
|
||||
b = b + s
|
||||
n, err_code = decoder.decode(b)
|
||||
|
@ -588,10 +593,12 @@ class PyDecoder(object):
|
|||
"""
|
||||
Override to perform the decoding process.
|
||||
|
||||
: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). If finished with decoding
|
||||
return <0 for the bytes consumed. Err codes are from `ERRORS`
|
||||
: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).
|
||||
If finished with decoding return <0 for the bytes consumed.
|
||||
Err codes are from `ERRORS`
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
@ -650,8 +657,8 @@ class PyDecoder(object):
|
|||
Convenience method to set the internal image from a stream of raw data
|
||||
|
||||
:param data: Bytes to be set
|
||||
:param rawmode: The rawmode to be used for the decoder. If not specified,
|
||||
it will default to the mode of the image
|
||||
:param rawmode: The rawmode to be used for the decoder.
|
||||
If not specified, it will default to the mode of the image
|
||||
:returns: None
|
||||
"""
|
||||
|
||||
|
|
|
@ -141,7 +141,8 @@ class FreeTypeFont(object):
|
|||
self.layout_engine = layout_engine
|
||||
|
||||
if isPath(font):
|
||||
self.font = core.getfont(font, size, index, encoding, layout_engine=layout_engine)
|
||||
self.font = core.getfont(font, size, index, encoding,
|
||||
layout_engine=layout_engine)
|
||||
else:
|
||||
self.font_bytes = font.read()
|
||||
self.font = core.getfont(
|
||||
|
@ -175,9 +176,11 @@ class FreeTypeFont(object):
|
|||
return self.font.getsize(text)[1]
|
||||
|
||||
def getmask(self, text, mode="", direction=None, features=None):
|
||||
return self.getmask2(text, mode, direction=direction, features=features)[0]
|
||||
return self.getmask2(text, mode, direction=direction,
|
||||
features=features)[0]
|
||||
|
||||
def getmask2(self, text, mode="", fill=Image.core.fill, direction=None, features=None, *args, **kwargs):
|
||||
def getmask2(self, text, mode="", fill=Image.core.fill, direction=None,
|
||||
features=None, *args, **kwargs):
|
||||
size, offset = self.font.getsize(text, direction, features)
|
||||
im = fill("L", size, 0)
|
||||
self.font.render(text, im.id, mode == "1", direction, features)
|
||||
|
@ -194,12 +197,13 @@ class FreeTypeFont(object):
|
|||
|
||||
:return: A FreeTypeFont object.
|
||||
"""
|
||||
return FreeTypeFont(font=self.path if font is None else font,
|
||||
size=self.size if size is None else size,
|
||||
index=self.index if index is None else index,
|
||||
encoding=self.encoding if encoding is None else encoding,
|
||||
layout_engine=self.layout_engine if layout_engine is None else layout_engine
|
||||
)
|
||||
return FreeTypeFont(
|
||||
font=self.path if font is None else font,
|
||||
size=self.size if size is None else size,
|
||||
index=self.index if index is None else index,
|
||||
encoding=self.encoding if encoding is None else encoding,
|
||||
layout_engine=self.layout_engine if layout_engine is None else layout_engine
|
||||
)
|
||||
|
||||
|
||||
class TransposedFont(object):
|
||||
|
@ -303,12 +307,16 @@ def truetype(font=None, size=10, index=0, encoding="",
|
|||
for walkfilename in walkfilenames:
|
||||
if ext and walkfilename == ttf_filename:
|
||||
fontpath = os.path.join(walkroot, walkfilename)
|
||||
return FreeTypeFont(fontpath, size, index, encoding, layout_engine)
|
||||
elif not ext and os.path.splitext(walkfilename)[0] == ttf_filename:
|
||||
return FreeTypeFont(fontpath, size, index,
|
||||
encoding, layout_engine)
|
||||
elif (not ext and
|
||||
os.path.splitext(walkfilename)[0] == ttf_filename):
|
||||
fontpath = os.path.join(walkroot, walkfilename)
|
||||
if os.path.splitext(fontpath)[1] == '.ttf':
|
||||
return FreeTypeFont(fontpath, size, index, encoding, layout_engine)
|
||||
if not ext and first_font_with_a_different_extension is None:
|
||||
return FreeTypeFont(fontpath, size, index,
|
||||
encoding, layout_engine)
|
||||
if not ext \
|
||||
and first_font_with_a_different_extension is None:
|
||||
first_font_with_a_different_extension = fontpath
|
||||
if first_font_with_a_different_extension:
|
||||
return FreeTypeFont(first_font_with_a_different_extension, size,
|
||||
|
|
|
@ -42,7 +42,8 @@ def getmode(mode):
|
|||
for m, (basemode, basetype, bands) in Image._MODEINFO.items():
|
||||
modes[m] = ModeDescriptor(m, bands, basemode, basetype)
|
||||
# extra experimental modes
|
||||
modes["RGBa"] = ModeDescriptor("RGBa", ("R", "G", "B", "a"), "RGB", "L")
|
||||
modes["RGBa"] = ModeDescriptor("RGBa",
|
||||
("R", "G", "B", "a"), "RGB", "L")
|
||||
modes["LA"] = ModeDescriptor("LA", ("L", "A"), "L", "L")
|
||||
modes["La"] = ModeDescriptor("La", ("L", "a"), "L", "L")
|
||||
modes["PA"] = ModeDescriptor("PA", ("P", "A"), "RGB", "L")
|
||||
|
|
|
@ -136,28 +136,87 @@ def autocontrast(image, cutoff=0, ignore=None):
|
|||
return _lut(image, lut)
|
||||
|
||||
|
||||
def colorize(image, black, white):
|
||||
def colorize(image, black, white, mid=None, blackpoint=0,
|
||||
whitepoint=255, midpoint=127):
|
||||
"""
|
||||
Colorize grayscale image. The **black** and **white**
|
||||
arguments should be RGB tuples; this function calculates a color
|
||||
wedge mapping all black pixels in the source image to the first
|
||||
color, and all white pixels to the second color.
|
||||
Colorize grayscale image.
|
||||
This function calculates a color wedge which maps all black pixels in
|
||||
the source image to the first color and all white pixels to the
|
||||
second color. If **mid** is specified, it uses three-color mapping.
|
||||
The **black** and **white** arguments should be RGB tuples or color names;
|
||||
optionally you can use three-color mapping by also specifying **mid**.
|
||||
Mapping positions for any of the colors can be specified
|
||||
(e.g. **blackpoint**), where these parameters are the integer
|
||||
value corresponding to where the corresponding color should be mapped.
|
||||
These parameters must have logical order, such that
|
||||
**blackpoint** <= **midpoint** <= **whitepoint** (if **mid** is specified).
|
||||
|
||||
:param image: The image to colorize.
|
||||
:param black: The color to use for black input pixels.
|
||||
:param white: The color to use for white input pixels.
|
||||
:param mid: The color to use for midtone input pixels.
|
||||
:param blackpoint: an int value [0, 255] for the black mapping.
|
||||
:param whitepoint: an int value [0, 255] for the white mapping.
|
||||
:param midpoint: an int value [0, 255] for the midtone mapping.
|
||||
:return: An image.
|
||||
"""
|
||||
|
||||
# Initial asserts
|
||||
assert image.mode == "L"
|
||||
if mid is None:
|
||||
assert 0 <= blackpoint <= whitepoint <= 255
|
||||
else:
|
||||
assert 0 <= blackpoint <= midpoint <= whitepoint <= 255
|
||||
|
||||
# Define colors from arguments
|
||||
black = _color(black, "RGB")
|
||||
white = _color(white, "RGB")
|
||||
if mid is not None:
|
||||
mid = _color(mid, "RGB")
|
||||
|
||||
# Empty lists for the mapping
|
||||
red = []
|
||||
green = []
|
||||
blue = []
|
||||
for i in range(256):
|
||||
red.append(black[0]+i*(white[0]-black[0])//255)
|
||||
green.append(black[1]+i*(white[1]-black[1])//255)
|
||||
blue.append(black[2]+i*(white[2]-black[2])//255)
|
||||
|
||||
# Create the low-end values
|
||||
for i in range(0, blackpoint):
|
||||
red.append(black[0])
|
||||
green.append(black[1])
|
||||
blue.append(black[2])
|
||||
|
||||
# Create the mapping (2-color)
|
||||
if mid is None:
|
||||
|
||||
range_map = range(0, whitepoint - blackpoint)
|
||||
|
||||
for i in range_map:
|
||||
red.append(black[0] + i * (white[0] - black[0]) // len(range_map))
|
||||
green.append(black[1] + i * (white[1] - black[1]) // len(range_map))
|
||||
blue.append(black[2] + i * (white[2] - black[2]) // len(range_map))
|
||||
|
||||
# Create the mapping (3-color)
|
||||
else:
|
||||
|
||||
range_map1 = range(0, midpoint - blackpoint)
|
||||
range_map2 = range(0, whitepoint - midpoint)
|
||||
|
||||
for i in range_map1:
|
||||
red.append(black[0] + i * (mid[0] - black[0]) // len(range_map1))
|
||||
green.append(black[1] + i * (mid[1] - black[1]) // len(range_map1))
|
||||
blue.append(black[2] + i * (mid[2] - black[2]) // len(range_map1))
|
||||
for i in range_map2:
|
||||
red.append(mid[0] + i * (white[0] - mid[0]) // len(range_map2))
|
||||
green.append(mid[1] + i * (white[1] - mid[1]) // len(range_map2))
|
||||
blue.append(mid[2] + i * (white[2] - mid[2]) // len(range_map2))
|
||||
|
||||
# Create the high-end values
|
||||
for i in range(0, 256 - whitepoint):
|
||||
red.append(white[0])
|
||||
green.append(white[1])
|
||||
blue.append(white[2])
|
||||
|
||||
# Return converted image
|
||||
image = image.convert("RGB")
|
||||
return _lut(image, red + green + blue)
|
||||
|
||||
|
|
|
@ -119,7 +119,8 @@ def align8to32(bytes, width, mode):
|
|||
|
||||
new_data = []
|
||||
for i in range(len(bytes) // bytes_per_line):
|
||||
new_data.append(bytes[i*bytes_per_line:(i+1)*bytes_per_line] + b'\x00' * extra_padding)
|
||||
new_data.append(bytes[i*bytes_per_line:(i+1)*bytes_per_line]
|
||||
+ b'\x00' * extra_padding)
|
||||
|
||||
return b''.join(new_data)
|
||||
|
||||
|
|
|
@ -110,11 +110,11 @@ class Stat(object):
|
|||
v = []
|
||||
for i in self.bands:
|
||||
s = 0
|
||||
l = self.count[i]//2
|
||||
half = self.count[i]//2
|
||||
b = i * 256
|
||||
for j in range(256):
|
||||
s = s + self.h[b+j]
|
||||
if s > l:
|
||||
if s > half:
|
||||
break
|
||||
v.append(j)
|
||||
return v
|
||||
|
|
|
@ -195,7 +195,8 @@ class PhotoImage(object):
|
|||
# Pypy is using a ffi cdata element
|
||||
# (Pdb) self.tk.interp
|
||||
# <cdata 'Tcl_Interp *' 0x3061b50>
|
||||
_imagingtk.tkinit(int(ffi.cast("uintptr_t", tk.interp)), 1)
|
||||
_imagingtk.tkinit(
|
||||
int(ffi.cast("uintptr_t", tk.interp)), 1)
|
||||
else:
|
||||
_imagingtk.tkinit(tk.interpaddr(), 1)
|
||||
except AttributeError:
|
||||
|
|
|
@ -270,7 +270,8 @@ def _save(im, fp, filename):
|
|||
Image.register_open(Jpeg2KImageFile.format, Jpeg2KImageFile, _accept)
|
||||
Image.register_save(Jpeg2KImageFile.format, _save)
|
||||
|
||||
Image.register_extensions(Jpeg2KImageFile.format, [".jp2", ".j2k", ".jpc", ".jpf", ".jpx", ".j2c"])
|
||||
Image.register_extensions(Jpeg2KImageFile.format,
|
||||
[".jp2", ".j2k", ".jpc", ".jpf", ".jpx", ".j2c"])
|
||||
|
||||
Image.register_mime(Jpeg2KImageFile.format, 'image/jp2')
|
||||
Image.register_mime(Jpeg2KImageFile.format, 'image/jpx')
|
||||
|
|
|
@ -799,6 +799,7 @@ def jpeg_factory(fp=None, filename=None):
|
|||
Image.register_open(JpegImageFile.format, jpeg_factory, _accept)
|
||||
Image.register_save(JpegImageFile.format, _save)
|
||||
|
||||
Image.register_extensions(JpegImageFile.format, [".jfif", ".jpe", ".jpg", ".jpeg"])
|
||||
Image.register_extensions(JpegImageFile.format,
|
||||
[".jfif", ".jpe", ".jpg", ".jpeg"])
|
||||
|
||||
Image.register_mime(JpegImageFile.format, "image/jpeg")
|
||||
|
|
|
@ -126,8 +126,9 @@ class MspDecoder(ImageFile.PyDecoder):
|
|||
continue
|
||||
row = self.fd.read(rowlen)
|
||||
if len(row) != rowlen:
|
||||
raise IOError("Truncated MSP file, expected %d bytes on row %s",
|
||||
(rowlen, x))
|
||||
raise IOError(
|
||||
"Truncated MSP file, expected %d bytes on row %s",
|
||||
(rowlen, x))
|
||||
idx = 0
|
||||
while idx < rowlen:
|
||||
runtype = i8(row[idx])
|
||||
|
|
|
@ -98,7 +98,8 @@ def _save(im, fp, filename, save_all=False):
|
|||
try:
|
||||
im_numberOfPages = im.n_frames
|
||||
except AttributeError:
|
||||
# Image format does not have n_frames. It is a single frame image
|
||||
# Image format does not have n_frames.
|
||||
# It is a single frame image
|
||||
pass
|
||||
numberOfPages += im_numberOfPages
|
||||
for i in range(im_numberOfPages):
|
||||
|
@ -115,9 +116,9 @@ def _save(im, fp, filename, save_all=False):
|
|||
for imSequence in ims:
|
||||
im_pages = ImageSequence.Iterator(imSequence) if save_all else [imSequence]
|
||||
for im in im_pages:
|
||||
# FIXME: Should replace ASCIIHexDecode with RunLengthDecode (packbits)
|
||||
# or LZWDecode (tiff/lzw compression). Note that PDF 1.2 also supports
|
||||
# Flatedecode (zip compression).
|
||||
# FIXME: Should replace ASCIIHexDecode with RunLengthDecode
|
||||
# (packbits) or LZWDecode (tiff/lzw compression). Note that
|
||||
# PDF 1.2 also supports Flatedecode (zip compression).
|
||||
|
||||
bits = 8
|
||||
params = None
|
||||
|
@ -135,7 +136,12 @@ def _save(im, fp, filename, save_all=False):
|
|||
elif im.mode == "P":
|
||||
filter = "ASCIIHexDecode"
|
||||
palette = im.im.getpalette("RGB")
|
||||
colorspace = [PdfParser.PdfName("Indexed"), PdfParser.PdfName("DeviceRGB"), 255, PdfParser.PdfBinary(palette)]
|
||||
colorspace = [
|
||||
PdfParser.PdfName("Indexed"),
|
||||
PdfParser.PdfName("DeviceRGB"),
|
||||
255,
|
||||
PdfParser.PdfBinary(palette)
|
||||
]
|
||||
procset = "ImageI" # indexed color
|
||||
elif im.mode == "RGB":
|
||||
filter = "DCTDecode"
|
||||
|
@ -166,7 +172,8 @@ def _save(im, fp, filename, save_all=False):
|
|||
elif filter == "FlateDecode":
|
||||
ImageFile._save(im, op, [("zip", (0, 0)+im.size, 0, im.mode)])
|
||||
elif filter == "RunLengthDecode":
|
||||
ImageFile._save(im, op, [("packbits", (0, 0)+im.size, 0, im.mode)])
|
||||
ImageFile._save(im, op,
|
||||
[("packbits", (0, 0)+im.size, 0, im.mode)])
|
||||
else:
|
||||
raise ValueError("unsupported PDF filter (%s)" % filter)
|
||||
|
||||
|
@ -175,26 +182,37 @@ def _save(im, fp, filename, save_all=False):
|
|||
|
||||
width, height = im.size
|
||||
|
||||
existing_pdf.write_obj(image_refs[pageNumber], stream=op.getvalue(),
|
||||
Type=PdfParser.PdfName("XObject"),
|
||||
Subtype=PdfParser.PdfName("Image"),
|
||||
Width=width, # * 72.0 / resolution,
|
||||
Height=height, # * 72.0 / resolution,
|
||||
Filter=PdfParser.PdfName(filter),
|
||||
BitsPerComponent=bits,
|
||||
DecodeParams=params,
|
||||
ColorSpace=colorspace)
|
||||
existing_pdf.write_obj(image_refs[pageNumber],
|
||||
stream=op.getvalue(),
|
||||
Type=PdfParser.PdfName("XObject"),
|
||||
Subtype=PdfParser.PdfName("Image"),
|
||||
Width=width, # * 72.0 / resolution,
|
||||
Height=height, # * 72.0 / resolution,
|
||||
Filter=PdfParser.PdfName(filter),
|
||||
BitsPerComponent=bits,
|
||||
DecodeParams=params,
|
||||
ColorSpace=colorspace)
|
||||
|
||||
#
|
||||
# page
|
||||
|
||||
existing_pdf.write_page(page_refs[pageNumber],
|
||||
Resources=PdfParser.PdfDict(
|
||||
ProcSet=[PdfParser.PdfName("PDF"), PdfParser.PdfName(procset)],
|
||||
XObject=PdfParser.PdfDict(image=image_refs[pageNumber])),
|
||||
MediaBox=[0, 0, int(width * 72.0 / resolution), int(height * 72.0 / resolution)],
|
||||
Contents=contents_refs[pageNumber]
|
||||
)
|
||||
Resources=PdfParser.PdfDict(
|
||||
ProcSet=[
|
||||
PdfParser.PdfName("PDF"),
|
||||
PdfParser.PdfName(procset)
|
||||
],
|
||||
XObject=PdfParser.PdfDict(
|
||||
image=image_refs[pageNumber]
|
||||
)
|
||||
),
|
||||
MediaBox=[
|
||||
0,
|
||||
0,
|
||||
int(width * 72.0 / resolution),
|
||||
int(height * 72.0 / resolution)
|
||||
],
|
||||
Contents=contents_refs[pageNumber])
|
||||
|
||||
#
|
||||
# page contents
|
||||
|
@ -204,7 +222,8 @@ def _save(im, fp, filename, save_all=False):
|
|||
int(width * 72.0 / resolution),
|
||||
int(height * 72.0 / resolution)))
|
||||
|
||||
existing_pdf.write_obj(contents_refs[pageNumber], stream=page_contents)
|
||||
existing_pdf.write_obj(contents_refs[pageNumber],
|
||||
stream=page_contents)
|
||||
|
||||
pageNumber += 1
|
||||
|
||||
|
|
|
@ -20,7 +20,8 @@ else: # Python 2.x
|
|||
return s # pragma: no cover
|
||||
|
||||
|
||||
# see 7.9.2.2 Text String Type on page 86 and D.3 PDFDocEncoding Character Set on page 656
|
||||
# see 7.9.2.2 Text String Type on page 86 and D.3 PDFDocEncoding Character Set
|
||||
# on page 656
|
||||
def encode_text(s):
|
||||
return codecs.BOM_UTF16_BE + s.encode("utf_16_be")
|
||||
|
||||
|
@ -80,7 +81,8 @@ def decode_text(b):
|
|||
|
||||
|
||||
class PdfFormatError(RuntimeError):
|
||||
"""An error that probably indicates a syntactic or semantic error in the PDF file structure"""
|
||||
"""An error that probably indicates a syntactic or semantic error in the
|
||||
PDF file structure"""
|
||||
pass
|
||||
|
||||
|
||||
|
@ -89,7 +91,8 @@ def check_format_condition(condition, error_message):
|
|||
raise PdfFormatError(error_message)
|
||||
|
||||
|
||||
class IndirectReference(collections.namedtuple("IndirectReferenceTuple", ["object_id", "generation"])):
|
||||
class IndirectReference(collections.namedtuple("IndirectReferenceTuple",
|
||||
["object_id", "generation"])):
|
||||
def __str__(self):
|
||||
return "%s %s R" % self
|
||||
|
||||
|
@ -97,7 +100,9 @@ class IndirectReference(collections.namedtuple("IndirectReferenceTuple", ["objec
|
|||
return self.__str__().encode("us-ascii")
|
||||
|
||||
def __eq__(self, other):
|
||||
return other.__class__ is self.__class__ and other.object_id == self.object_id and other.generation == self.generation
|
||||
return other.__class__ is self.__class__ and \
|
||||
other.object_id == self.object_id and \
|
||||
other.generation == self.generation
|
||||
|
||||
def __ne__(self, other):
|
||||
return not (self == other)
|
||||
|
@ -143,19 +148,26 @@ class XrefTable:
|
|||
elif key in self.deleted_entries:
|
||||
generation = self.deleted_entries[key]
|
||||
else:
|
||||
raise IndexError("object ID " + str(key) + " cannot be deleted because it doesn't exist")
|
||||
raise IndexError("object ID " + str(key) +
|
||||
" cannot be deleted because it doesn't exist")
|
||||
|
||||
def __contains__(self, key):
|
||||
return key in self.existing_entries or key in self.new_entries
|
||||
|
||||
def __len__(self):
|
||||
return len(set(self.existing_entries.keys()) | set(self.new_entries.keys()) | set(self.deleted_entries.keys()))
|
||||
return len(set(self.existing_entries.keys()) |
|
||||
set(self.new_entries.keys()) |
|
||||
set(self.deleted_entries.keys()))
|
||||
|
||||
def keys(self):
|
||||
return (set(self.existing_entries.keys()) - set(self.deleted_entries.keys())) | set(self.new_entries.keys())
|
||||
return (
|
||||
set(self.existing_entries.keys()) -
|
||||
set(self.deleted_entries.keys())
|
||||
) | set(self.new_entries.keys())
|
||||
|
||||
def write(self, f):
|
||||
keys = sorted(set(self.new_entries.keys()) | set(self.deleted_entries.keys()))
|
||||
keys = sorted(set(self.new_entries.keys()) |
|
||||
set(self.deleted_entries.keys()))
|
||||
deleted_keys = sorted(set(self.deleted_entries.keys()))
|
||||
startxref = f.tell()
|
||||
f.write(b"xref\n")
|
||||
|
@ -172,10 +184,12 @@ class XrefTable:
|
|||
else:
|
||||
contiguous_keys = keys
|
||||
keys = None
|
||||
f.write(make_bytes("%d %d\n" % (contiguous_keys[0], len(contiguous_keys))))
|
||||
f.write(make_bytes("%d %d\n" %
|
||||
(contiguous_keys[0], len(contiguous_keys))))
|
||||
for object_id in contiguous_keys:
|
||||
if object_id in self.new_entries:
|
||||
f.write(make_bytes("%010d %05d n \n" % self.new_entries[object_id]))
|
||||
f.write(make_bytes("%010d %05d n \n" %
|
||||
self.new_entries[object_id]))
|
||||
else:
|
||||
this_deleted_object_id = deleted_keys.pop(0)
|
||||
check_format_condition(object_id == this_deleted_object_id,
|
||||
|
@ -186,7 +200,9 @@ class XrefTable:
|
|||
next_in_linked_list = deleted_keys[0]
|
||||
except IndexError:
|
||||
next_in_linked_list = 0
|
||||
f.write(make_bytes("%010d %05d f \n" % (next_in_linked_list, self.deleted_entries[object_id])))
|
||||
f.write(make_bytes("%010d %05d f \n" %
|
||||
(next_in_linked_list,
|
||||
self.deleted_entries[object_id])))
|
||||
return startxref
|
||||
|
||||
|
||||
|
@ -203,7 +219,8 @@ class PdfName:
|
|||
return self.name.decode("us-ascii")
|
||||
|
||||
def __eq__(self, other):
|
||||
return (isinstance(other, PdfName) and other.name == self.name) or other == self.name
|
||||
return (isinstance(other, PdfName) and other.name == self.name) or \
|
||||
other == self.name
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.name)
|
||||
|
@ -313,7 +330,9 @@ class PdfStream:
|
|||
expected_length = self.dictionary.Length
|
||||
return zlib.decompress(self.buf, bufsize=int(expected_length))
|
||||
else:
|
||||
raise NotImplementedError("stream filter %s unknown/unsupported" % repr(self.dictionary.Filter))
|
||||
raise NotImplementedError(
|
||||
"stream filter %s unknown/unsupported" %
|
||||
repr(self.dictionary.Filter))
|
||||
|
||||
|
||||
def pdf_repr(x):
|
||||
|
@ -323,7 +342,8 @@ def pdf_repr(x):
|
|||
return b"false"
|
||||
elif x is None:
|
||||
return b"null"
|
||||
elif isinstance(x, PdfName) or isinstance(x, PdfDict) or isinstance(x, PdfArray) or isinstance(x, PdfBinary):
|
||||
elif (isinstance(x, PdfName) or isinstance(x, PdfDict) or
|
||||
isinstance(x, PdfArray) or isinstance(x, PdfBinary)):
|
||||
return bytes(x)
|
||||
elif isinstance(x, int):
|
||||
return str(x).encode("us-ascii")
|
||||
|
@ -331,10 +351,15 @@ def pdf_repr(x):
|
|||
return bytes(PdfDict(x))
|
||||
elif isinstance(x, list):
|
||||
return bytes(PdfArray(x))
|
||||
elif (py3 and isinstance(x, str)) or (not py3 and isinstance(x, unicode)):
|
||||
elif ((py3 and isinstance(x, str)) or
|
||||
(not py3 and isinstance(x, unicode))):
|
||||
return pdf_repr(encode_text(x))
|
||||
elif isinstance(x, bytes):
|
||||
return b"(" + x.replace(b"\\", b"\\\\").replace(b"(", b"\\(").replace(b")", b"\\)") + b")" # XXX escape more chars? handle binary garbage
|
||||
# XXX escape more chars? handle binary garbage
|
||||
x = x.replace(b"\\", b"\\\\")
|
||||
x = x.replace(b"(", b"\\(")
|
||||
x = x.replace(b")", b"\\)")
|
||||
return b"(" + x + b")"
|
||||
else:
|
||||
return bytes(x)
|
||||
|
||||
|
@ -344,10 +369,13 @@ class PdfParser:
|
|||
Supports PDF up to 1.4
|
||||
"""
|
||||
|
||||
def __init__(self, filename=None, f=None, buf=None, start_offset=0, mode="rb"):
|
||||
# type: (PdfParser, str, file, Union[bytes, bytearray], int, str) -> None
|
||||
def __init__(self, filename=None, f=None,
|
||||
buf=None, start_offset=0, mode="rb"):
|
||||
# type: (PdfParser, str, file, Union[bytes, bytearray], int, str)
|
||||
# -> None
|
||||
if buf and f:
|
||||
raise RuntimeError("specify buf or f or filename, but not both buf and f")
|
||||
raise RuntimeError(
|
||||
"specify buf or f or filename, but not both buf and f")
|
||||
self.filename = filename
|
||||
self.buf = buf
|
||||
self.f = f
|
||||
|
@ -473,7 +501,8 @@ class PdfParser:
|
|||
if self.info:
|
||||
trailer_dict[b"Info"] = self.info_ref
|
||||
self.last_xref_section_offset = start_xref
|
||||
self.f.write(b"trailer\n" + bytes(PdfDict(trailer_dict)) + make_bytes("\nstartxref\n%d\n%%%%EOF" % start_xref))
|
||||
self.f.write(b"trailer\n" + bytes(PdfDict(trailer_dict)) +
|
||||
make_bytes("\nstartxref\n%d\n%%%%EOF" % start_xref))
|
||||
|
||||
def write_page(self, ref, *objs, **dict_obj):
|
||||
if isinstance(ref, int):
|
||||
|
@ -535,13 +564,18 @@ class PdfParser:
|
|||
else:
|
||||
self.info = PdfDict(self.read_indirect(self.info_ref))
|
||||
check_format_condition(b"Type" in self.root, "/Type missing in Root")
|
||||
check_format_condition(self.root[b"Type"] == b"Catalog", "/Type in Root is not /Catalog")
|
||||
check_format_condition(self.root[b"Type"] == b"Catalog",
|
||||
"/Type in Root is not /Catalog")
|
||||
check_format_condition(b"Pages" in self.root, "/Pages missing in Root")
|
||||
check_format_condition(isinstance(self.root[b"Pages"], IndirectReference), "/Pages in Root is not an indirect reference")
|
||||
check_format_condition(isinstance(self.root[b"Pages"],
|
||||
IndirectReference),
|
||||
"/Pages in Root is not an indirect reference")
|
||||
self.pages_ref = self.root[b"Pages"]
|
||||
self.page_tree_root = self.read_indirect(self.pages_ref)
|
||||
self.pages = self.linearize_page_tree(self.page_tree_root)
|
||||
# save the original list of page references in case the user modifies, adds or deletes some pages and we need to rewrite the pages and their list
|
||||
# save the original list of page references
|
||||
# in case the user modifies, adds or deletes some pages
|
||||
# and we need to rewrite the pages and their list
|
||||
self.orig_pages = self.pages[:]
|
||||
|
||||
def next_object_id(self, offset=None):
|
||||
|
@ -562,10 +596,14 @@ class PdfParser:
|
|||
whitespace_mandatory = whitespace + b"+"
|
||||
newline_only = br"[\r\n]+"
|
||||
newline = whitespace_optional + newline_only + whitespace_optional
|
||||
re_trailer_end = re.compile(whitespace_mandatory + br"trailer" + whitespace_optional + br"\<\<(.*\>\>)" + newline
|
||||
+ br"startxref" + newline + br"([0-9]+)" + newline + br"%%EOF" + whitespace_optional + br"$", re.DOTALL)
|
||||
re_trailer_prev = re.compile(whitespace_optional + br"trailer" + whitespace_optional + br"\<\<(.*?\>\>)" + newline
|
||||
+ br"startxref" + newline + br"([0-9]+)" + newline + br"%%EOF" + whitespace_optional, re.DOTALL)
|
||||
re_trailer_end = re.compile(
|
||||
whitespace_mandatory + br"trailer" + whitespace_optional +
|
||||
br"\<\<(.*\>\>)" + newline + br"startxref" + newline + br"([0-9]+)" +
|
||||
newline + br"%%EOF" + whitespace_optional + br"$", re.DOTALL)
|
||||
re_trailer_prev = re.compile(
|
||||
whitespace_optional + br"trailer" + whitespace_optional +
|
||||
br"\<\<(.*?\>\>)" + newline + br"startxref" + newline + br"([0-9]+)" +
|
||||
newline + br"%%EOF" + whitespace_optional, re.DOTALL)
|
||||
|
||||
def read_trailer(self):
|
||||
search_start_offset = len(self.buf) - 16384
|
||||
|
@ -589,19 +627,26 @@ class PdfParser:
|
|||
self.read_prev_trailer(self.trailer_dict[b"Prev"])
|
||||
|
||||
def read_prev_trailer(self, xref_section_offset):
|
||||
trailer_offset = self.read_xref_table(xref_section_offset=xref_section_offset)
|
||||
m = self.re_trailer_prev.search(self.buf[trailer_offset:trailer_offset+16384])
|
||||
trailer_offset = self.read_xref_table(
|
||||
xref_section_offset=xref_section_offset)
|
||||
m = self.re_trailer_prev.search(
|
||||
self.buf[trailer_offset:trailer_offset+16384])
|
||||
check_format_condition(m, "previous trailer not found")
|
||||
trailer_data = m.group(1)
|
||||
check_format_condition(int(m.group(2)) == xref_section_offset, "xref section offset in previous trailer doesn't match what was expected")
|
||||
check_format_condition(int(m.group(2)) == xref_section_offset,
|
||||
"xref section offset in previous trailer "
|
||||
"doesn't match what was expected")
|
||||
trailer_dict = self.interpret_trailer(trailer_data)
|
||||
if b"Prev" in trailer_dict:
|
||||
self.read_prev_trailer(trailer_dict[b"Prev"])
|
||||
|
||||
re_whitespace_optional = re.compile(whitespace_optional)
|
||||
re_name = re.compile(whitespace_optional + br"/([!-$&'*-.0-;=?-Z\\^-z|~]+)(?=" + delimiter_or_ws + br")")
|
||||
re_name = re.compile(
|
||||
whitespace_optional + br"/([!-$&'*-.0-;=?-Z\\^-z|~]+)(?=" +
|
||||
delimiter_or_ws + br")")
|
||||
re_dict_start = re.compile(whitespace_optional + br"\<\<")
|
||||
re_dict_end = re.compile(whitespace_optional + br"\>\>" + whitespace_optional)
|
||||
re_dict_end = re.compile(
|
||||
whitespace_optional + br"\>\>" + whitespace_optional)
|
||||
|
||||
@classmethod
|
||||
def interpret_trailer(cls, trailer_data):
|
||||
|
@ -611,13 +656,21 @@ class PdfParser:
|
|||
m = cls.re_name.match(trailer_data, offset)
|
||||
if not m:
|
||||
m = cls.re_dict_end.match(trailer_data, offset)
|
||||
check_format_condition(m and m.end() == len(trailer_data), "name not found in trailer, remaining data: " + repr(trailer_data[offset:]))
|
||||
check_format_condition(
|
||||
m and m.end() == len(trailer_data),
|
||||
"name not found in trailer, remaining data: " +
|
||||
repr(trailer_data[offset:]))
|
||||
break
|
||||
key = cls.interpret_name(m.group(1))
|
||||
value, offset = cls.get_value(trailer_data, m.end())
|
||||
trailer[key] = value
|
||||
check_format_condition(b"Size" in trailer and isinstance(trailer[b"Size"], int), "/Size not in trailer or not an integer")
|
||||
check_format_condition(b"Root" in trailer and isinstance(trailer[b"Root"], IndirectReference), "/Root not in trailer or not an indirect reference")
|
||||
check_format_condition(
|
||||
b"Size" in trailer and isinstance(trailer[b"Size"], int),
|
||||
"/Size not in trailer or not an integer")
|
||||
check_format_condition(
|
||||
b"Root" in trailer and
|
||||
isinstance(trailer[b"Root"], IndirectReference),
|
||||
"/Root not in trailer or not an indirect reference")
|
||||
return trailer
|
||||
|
||||
re_hashes_in_name = re.compile(br"([^#]*)(#([0-9a-fA-F]{2}))?")
|
||||
|
@ -627,7 +680,8 @@ class PdfParser:
|
|||
name = b""
|
||||
for m in cls.re_hashes_in_name.finditer(raw):
|
||||
if m.group(3):
|
||||
name += m.group(1) + bytearray.fromhex(m.group(3).decode("us-ascii"))
|
||||
name += m.group(1) + \
|
||||
bytearray.fromhex(m.group(3).decode("us-ascii"))
|
||||
else:
|
||||
name += m.group(1)
|
||||
if as_text:
|
||||
|
@ -635,21 +689,37 @@ class PdfParser:
|
|||
else:
|
||||
return bytes(name)
|
||||
|
||||
re_null = re.compile(whitespace_optional + br"null(?=" + delimiter_or_ws + br")")
|
||||
re_true = re.compile(whitespace_optional + br"true(?=" + delimiter_or_ws + br")")
|
||||
re_false = re.compile(whitespace_optional + br"false(?=" + delimiter_or_ws + br")")
|
||||
re_int = re.compile(whitespace_optional + br"([-+]?[0-9]+)(?=" + delimiter_or_ws + br")")
|
||||
re_real = re.compile(whitespace_optional + br"([-+]?([0-9]+\.[0-9]*|[0-9]*\.[0-9]+))(?=" + delimiter_or_ws + br")")
|
||||
re_null = re.compile(
|
||||
whitespace_optional + br"null(?=" + delimiter_or_ws + br")")
|
||||
re_true = re.compile(
|
||||
whitespace_optional + br"true(?=" + delimiter_or_ws + br")")
|
||||
re_false = re.compile(
|
||||
whitespace_optional + br"false(?=" + delimiter_or_ws + br")")
|
||||
re_int = re.compile(
|
||||
whitespace_optional + br"([-+]?[0-9]+)(?=" + delimiter_or_ws + br")")
|
||||
re_real = re.compile(
|
||||
whitespace_optional + br"([-+]?([0-9]+\.[0-9]*|[0-9]*\.[0-9]+))(?=" +
|
||||
delimiter_or_ws + br")")
|
||||
re_array_start = re.compile(whitespace_optional + br"\[")
|
||||
re_array_end = re.compile(whitespace_optional + br"]")
|
||||
re_string_hex = re.compile(whitespace_optional + br"\<(" + whitespace_or_hex + br"*)\>")
|
||||
re_string_hex = re.compile(
|
||||
whitespace_optional + br"\<(" + whitespace_or_hex + br"*)\>")
|
||||
re_string_lit = re.compile(whitespace_optional + br"\(")
|
||||
re_indirect_reference = re.compile(whitespace_optional + br"([-+]?[0-9]+)" + whitespace_mandatory + br"([-+]?[0-9]+)" + whitespace_mandatory + br"R(?=" + delimiter_or_ws + br")")
|
||||
re_indirect_def_start = re.compile(whitespace_optional + br"([-+]?[0-9]+)" + whitespace_mandatory + br"([-+]?[0-9]+)" + whitespace_mandatory + br"obj(?=" + delimiter_or_ws + br")")
|
||||
re_indirect_def_end = re.compile(whitespace_optional + br"endobj(?=" + delimiter_or_ws + br")")
|
||||
re_comment = re.compile(br"(" + whitespace_optional + br"%[^\r\n]*" + newline + br")*")
|
||||
re_indirect_reference = re.compile(
|
||||
whitespace_optional + br"([-+]?[0-9]+)" + whitespace_mandatory +
|
||||
br"([-+]?[0-9]+)" + whitespace_mandatory + br"R(?=" + delimiter_or_ws +
|
||||
br")")
|
||||
re_indirect_def_start = re.compile(
|
||||
whitespace_optional + br"([-+]?[0-9]+)" + whitespace_mandatory +
|
||||
br"([-+]?[0-9]+)" + whitespace_mandatory + br"obj(?=" +
|
||||
delimiter_or_ws + br")")
|
||||
re_indirect_def_end = re.compile(
|
||||
whitespace_optional + br"endobj(?=" + delimiter_or_ws + br")")
|
||||
re_comment = re.compile(
|
||||
br"(" + whitespace_optional + br"%[^\r\n]*" + newline + br")*")
|
||||
re_stream_start = re.compile(whitespace_optional + br"stream\r?\n")
|
||||
re_stream_end = re.compile(whitespace_optional + br"endstream(?=" + delimiter_or_ws + br")")
|
||||
re_stream_end = re.compile(
|
||||
whitespace_optional + br"endstream(?=" + delimiter_or_ws + br")")
|
||||
|
||||
@classmethod
|
||||
def get_value(cls, data, offset, expect_indirect=None, max_nesting=-1):
|
||||
|
@ -660,21 +730,34 @@ class PdfParser:
|
|||
offset = m.end()
|
||||
m = cls.re_indirect_def_start.match(data, offset)
|
||||
if m:
|
||||
check_format_condition(int(m.group(1)) > 0, "indirect object definition: object ID must be greater than 0")
|
||||
check_format_condition(int(m.group(2)) >= 0, "indirect object definition: generation must be non-negative")
|
||||
check_format_condition(expect_indirect is None or expect_indirect == IndirectReference(int(m.group(1)), int(m.group(2))),
|
||||
check_format_condition(
|
||||
int(m.group(1)) > 0,
|
||||
"indirect object definition: object ID must be greater than 0")
|
||||
check_format_condition(
|
||||
int(m.group(2)) >= 0,
|
||||
"indirect object definition: generation must be non-negative")
|
||||
check_format_condition(
|
||||
expect_indirect is None or expect_indirect ==
|
||||
IndirectReference(int(m.group(1)), int(m.group(2))),
|
||||
"indirect object definition different than expected")
|
||||
object, offset = cls.get_value(data, m.end(), max_nesting=max_nesting-1)
|
||||
object, offset = cls.get_value(
|
||||
data, m.end(), max_nesting=max_nesting-1)
|
||||
if offset is None:
|
||||
return object, None
|
||||
m = cls.re_indirect_def_end.match(data, offset)
|
||||
check_format_condition(m, "indirect object definition end not found")
|
||||
check_format_condition(
|
||||
m, "indirect object definition end not found")
|
||||
return object, m.end()
|
||||
check_format_condition(not expect_indirect, "indirect object definition not found")
|
||||
check_format_condition(
|
||||
not expect_indirect, "indirect object definition not found")
|
||||
m = cls.re_indirect_reference.match(data, offset)
|
||||
if m:
|
||||
check_format_condition(int(m.group(1)) > 0, "indirect object reference: object ID must be greater than 0")
|
||||
check_format_condition(int(m.group(2)) >= 0, "indirect object reference: generation must be non-negative")
|
||||
check_format_condition(
|
||||
int(m.group(1)) > 0,
|
||||
"indirect object reference: object ID must be greater than 0")
|
||||
check_format_condition(
|
||||
int(m.group(2)) >= 0,
|
||||
"indirect object reference: generation must be non-negative")
|
||||
return IndirectReference(int(m.group(1)), int(m.group(2))), m.end()
|
||||
m = cls.re_dict_start.match(data, offset)
|
||||
if m:
|
||||
|
@ -682,10 +765,12 @@ class PdfParser:
|
|||
result = {}
|
||||
m = cls.re_dict_end.match(data, offset)
|
||||
while not m:
|
||||
key, offset = cls.get_value(data, offset, max_nesting=max_nesting-1)
|
||||
key, offset = cls.get_value(
|
||||
data, offset, max_nesting=max_nesting-1)
|
||||
if offset is None:
|
||||
return result, None
|
||||
value, offset = cls.get_value(data, offset, max_nesting=max_nesting-1)
|
||||
value, offset = cls.get_value(
|
||||
data, offset, max_nesting=max_nesting-1)
|
||||
result[key] = value
|
||||
if offset is None:
|
||||
return result, None
|
||||
|
@ -696,7 +781,9 @@ class PdfParser:
|
|||
try:
|
||||
stream_len = int(result[b"Length"])
|
||||
except (TypeError, KeyError, ValueError):
|
||||
raise PdfFormatError("bad or missing Length in stream dict (%r)" % result.get(b"Length", None))
|
||||
raise PdfFormatError(
|
||||
"bad or missing Length in stream dict (%r)" %
|
||||
result.get(b"Length", None))
|
||||
stream_data = data[m.end():m.end() + stream_len]
|
||||
m = cls.re_stream_end.match(data, m.end() + stream_len)
|
||||
check_format_condition(m, "stream end not found")
|
||||
|
@ -711,7 +798,8 @@ class PdfParser:
|
|||
result = []
|
||||
m = cls.re_array_end.match(data, offset)
|
||||
while not m:
|
||||
value, offset = cls.get_value(data, offset, max_nesting=max_nesting-1)
|
||||
value, offset = cls.get_value(
|
||||
data, offset, max_nesting=max_nesting-1)
|
||||
result.append(value)
|
||||
if offset is None:
|
||||
return result, None
|
||||
|
@ -734,18 +822,25 @@ class PdfParser:
|
|||
return int(m.group(1)), m.end()
|
||||
m = cls.re_real.match(data, offset)
|
||||
if m:
|
||||
return float(m.group(1)), m.end() # XXX Decimal instead of float???
|
||||
# XXX Decimal instead of float???
|
||||
return float(m.group(1)), m.end()
|
||||
m = cls.re_string_hex.match(data, offset)
|
||||
if m:
|
||||
hex_string = bytearray([b for b in m.group(1) if b in b"0123456789abcdefABCDEF"]) # filter out whitespace
|
||||
# filter out whitespace
|
||||
hex_string = bytearray([
|
||||
b for b in m.group(1)
|
||||
if b in b"0123456789abcdefABCDEF"
|
||||
])
|
||||
if len(hex_string) % 2 == 1:
|
||||
hex_string.append(ord(b"0")) # append a 0 if the length is not even - yes, at the end
|
||||
# append a 0 if the length is not even - yes, at the end
|
||||
hex_string.append(ord(b"0"))
|
||||
return bytearray.fromhex(hex_string.decode("us-ascii")), m.end()
|
||||
m = cls.re_string_lit.match(data, offset)
|
||||
if m:
|
||||
return cls.get_literal_string(data, m.end())
|
||||
#return None, offset # fallback (only for debugging)
|
||||
raise PdfFormatError("unrecognized object: " + repr(data[offset:offset+32]))
|
||||
# return None, offset # fallback (only for debugging)
|
||||
raise PdfFormatError(
|
||||
"unrecognized object: " + repr(data[offset:offset+32]))
|
||||
|
||||
re_lit_str_token = re.compile(br"(\\[nrtbf()\\])|(\\[0-9]{1,3})|(\\(\r\n|\r|\n))|(\r\n|\r|\n)|(\()|(\))")
|
||||
escaped_chars = {
|
||||
|
@ -792,19 +887,24 @@ class PdfParser:
|
|||
offset = m.end()
|
||||
raise PdfFormatError("unfinished literal string")
|
||||
|
||||
re_xref_section_start = re.compile(whitespace_optional + br"xref" + newline)
|
||||
re_xref_subsection_start = re.compile(whitespace_optional + br"([0-9]+)" + whitespace_mandatory + br"([0-9]+)" + whitespace_optional + newline_only)
|
||||
re_xref_section_start = re.compile(
|
||||
whitespace_optional + br"xref" + newline)
|
||||
re_xref_subsection_start = re.compile(
|
||||
whitespace_optional + br"([0-9]+)" + whitespace_mandatory +
|
||||
br"([0-9]+)" + whitespace_optional + newline_only)
|
||||
re_xref_entry = re.compile(br"([0-9]{10}) ([0-9]{5}) ([fn])( \r| \n|\r\n)")
|
||||
|
||||
def read_xref_table(self, xref_section_offset):
|
||||
subsection_found = False
|
||||
m = self.re_xref_section_start.match(self.buf, xref_section_offset + self.start_offset)
|
||||
m = self.re_xref_section_start.match(
|
||||
self.buf, xref_section_offset + self.start_offset)
|
||||
check_format_condition(m, "xref section start not found")
|
||||
offset = m.end()
|
||||
while True:
|
||||
m = self.re_xref_subsection_start.match(self.buf, offset)
|
||||
if not m:
|
||||
check_format_condition(subsection_found, "xref subsection start not found")
|
||||
check_format_condition(
|
||||
subsection_found, "xref subsection start not found")
|
||||
break
|
||||
subsection_found = True
|
||||
offset = m.end()
|
||||
|
@ -818,22 +918,31 @@ class PdfParser:
|
|||
generation = int(m.group(2))
|
||||
if not is_free:
|
||||
new_entry = (int(m.group(1)), generation)
|
||||
check_format_condition(i not in self.xref_table or self.xref_table[i] == new_entry, "xref entry duplicated (and not identical)")
|
||||
check_format_condition(
|
||||
i not in self.xref_table or
|
||||
self.xref_table[i] == new_entry,
|
||||
"xref entry duplicated (and not identical)")
|
||||
self.xref_table[i] = new_entry
|
||||
return offset
|
||||
|
||||
def read_indirect(self, ref, max_nesting=-1):
|
||||
offset, generation = self.xref_table[ref[0]]
|
||||
check_format_condition(generation == ref[1], "expected to find generation %s for object ID %s in xref table, instead found generation %s at offset %s" \
|
||||
check_format_condition(
|
||||
generation == ref[1],
|
||||
"expected to find generation %s for object ID %s in xref table, "
|
||||
"instead found generation %s at offset %s"
|
||||
% (ref[1], ref[0], generation, offset))
|
||||
value = self.get_value(self.buf, offset + self.start_offset, expect_indirect=IndirectReference(*ref), max_nesting=max_nesting)[0]
|
||||
value = self.get_value(self.buf, offset + self.start_offset,
|
||||
expect_indirect=IndirectReference(*ref),
|
||||
max_nesting=max_nesting)[0]
|
||||
self.cached_objects[ref] = value
|
||||
return value
|
||||
|
||||
def linearize_page_tree(self, node=None):
|
||||
if node is None:
|
||||
node = self.page_tree_root
|
||||
check_format_condition(node[b"Type"] == b"Pages", "/Type of page tree node is not /Pages")
|
||||
check_format_condition(
|
||||
node[b"Type"] == b"Pages", "/Type of page tree node is not /Pages")
|
||||
pages = []
|
||||
for kid in node[b"Kids"]:
|
||||
kid_object = self.read_indirect(kid)
|
||||
|
|
|
@ -142,7 +142,8 @@ class ChunkStream(object):
|
|||
def crc(self, cid, data):
|
||||
"Read and verify checksum"
|
||||
|
||||
# Skip CRC checks for ancillary chunks if allowed to load truncated images
|
||||
# Skip CRC checks for ancillary chunks if allowed to load truncated
|
||||
# images
|
||||
# 5th byte of first char is 1 [specs, section 5.4]
|
||||
if ImageFile.LOAD_TRUNCATED_IMAGES and (i8(cid[0]) >> 5 & 1):
|
||||
self.crc_skip(cid, data)
|
||||
|
@ -301,8 +302,8 @@ class PngStream(ChunkStream):
|
|||
def check_text_memory(self, chunklen):
|
||||
self.text_memory += chunklen
|
||||
if self.text_memory > MAX_TEXT_MEMORY:
|
||||
raise ValueError("Too much memory used in text chunks: %s>MAX_TEXT_MEMORY" %
|
||||
self.text_memory)
|
||||
raise ValueError("Too much memory used in text chunks: "
|
||||
"%s>MAX_TEXT_MEMORY" % self.text_memory)
|
||||
|
||||
def chunk_iCCP(self, pos, length):
|
||||
|
||||
|
|
|
@ -83,7 +83,8 @@ class PpmImageFile(ImageFile.ImageFile):
|
|||
if s not in b_whitespace:
|
||||
break
|
||||
if s == b"":
|
||||
raise ValueError("File does not extend beyond magic number")
|
||||
raise ValueError(
|
||||
"File does not extend beyond magic number")
|
||||
if s != b"#":
|
||||
break
|
||||
s = self.fp.readline()
|
||||
|
|
|
@ -222,6 +222,7 @@ Image.register_save(SgiImageFile.format, _save)
|
|||
Image.register_mime(SgiImageFile.format, "image/sgi")
|
||||
Image.register_mime(SgiImageFile.format, "image/rgb")
|
||||
|
||||
Image.register_extensions(SgiImageFile.format, [".bw", ".rgb", ".rgba", ".sgi"])
|
||||
Image.register_extensions(SgiImageFile.format,
|
||||
[".bw", ".rgb", ".rgba", ".sgi"])
|
||||
|
||||
# End of file
|
||||
|
|
|
@ -62,7 +62,7 @@ class SunImageFile(ImageFile.ImageFile):
|
|||
self.size = i32(s[4:8]), i32(s[8:12])
|
||||
|
||||
depth = i32(s[12:16])
|
||||
data_length = i32(s[16:20]) # unreliable, ignore.
|
||||
# data_length = i32(s[16:20]) # unreliable, ignore.
|
||||
file_type = i32(s[20:24])
|
||||
palette_type = i32(s[24:28]) # 0: None, 1: RGB, 2: Raw/arbitrary
|
||||
palette_length = i32(s[28:32])
|
||||
|
@ -94,7 +94,8 @@ class SunImageFile(ImageFile.ImageFile):
|
|||
raise SyntaxError("Unsupported Palette Type")
|
||||
|
||||
offset = offset + palette_length
|
||||
self.palette = ImagePalette.raw("RGB;L", self.fp.read(palette_length))
|
||||
self.palette = ImagePalette.raw("RGB;L",
|
||||
self.fp.read(palette_length))
|
||||
if self.mode == "L":
|
||||
self.mode = "P"
|
||||
rawmode = rawmode.replace('L', 'P')
|
||||
|
|
|
@ -45,7 +45,6 @@ from . import Image, ImageFile, ImagePalette, TiffTags
|
|||
from ._binary import i8, o8
|
||||
from ._util import py3
|
||||
|
||||
import collections
|
||||
from fractions import Fraction
|
||||
from numbers import Number, Rational
|
||||
|
||||
|
@ -431,7 +430,8 @@ class ImageFileDirectory_v2(MutableMapping):
|
|||
* self.tagtype = {}
|
||||
|
||||
* Key: numerical tiff tag number
|
||||
* Value: integer corresponding to the data type from `~PIL.TiffTags.TYPES`
|
||||
* Value: integer corresponding to the data type from
|
||||
~PIL.TiffTags.TYPES`
|
||||
|
||||
.. versionadded:: 3.0.0
|
||||
"""
|
||||
|
@ -577,8 +577,8 @@ class ImageFileDirectory_v2(MutableMapping):
|
|||
# Spec'd length == 1, Actual > 1, Warn and truncate. Formerly barfed.
|
||||
# No Spec, Actual length 1, Formerly (<4.2) returned a 1 element tuple.
|
||||
# Don't mess with the legacy api, since it's frozen.
|
||||
if ((info.length == 1) or
|
||||
(info.length is None and len(values) == 1 and not legacy_api)):
|
||||
if (info.length == 1) or \
|
||||
(info.length is None and len(values) == 1 and not legacy_api):
|
||||
# Don't mess with the legacy api, since it's frozen.
|
||||
if legacy_api and self.tagtype[tag] in [5, 10]: # rationals
|
||||
values = values,
|
||||
|
@ -1245,7 +1245,7 @@ class TiffImageFile(ImageFile.ImageFile):
|
|||
self.info["resolution"] = xres, yres
|
||||
|
||||
# build tile descriptors
|
||||
x = y = l = 0
|
||||
x = y = layer = 0
|
||||
self.tile = []
|
||||
self.use_load_libtiff = False
|
||||
if STRIPOFFSETS in self.tag_v2:
|
||||
|
@ -1305,7 +1305,7 @@ class TiffImageFile(ImageFile.ImageFile):
|
|||
|
||||
else:
|
||||
for i, offset in enumerate(offsets):
|
||||
a = self._decoder(rawmode, l, i)
|
||||
a = self._decoder(rawmode, layer, i)
|
||||
self.tile.append(
|
||||
(self._compression,
|
||||
(0, min(y, ysize), w, min(y+h, ysize)),
|
||||
|
@ -1315,7 +1315,7 @@ class TiffImageFile(ImageFile.ImageFile):
|
|||
y = y + h
|
||||
if y >= self.size[1]:
|
||||
x = y = 0
|
||||
l += 1
|
||||
layer += 1
|
||||
a = None
|
||||
elif TILEOFFSETS in self.tag_v2:
|
||||
# tiled image
|
||||
|
@ -1324,7 +1324,7 @@ class TiffImageFile(ImageFile.ImageFile):
|
|||
a = None
|
||||
for o in self.tag_v2[TILEOFFSETS]:
|
||||
if not a:
|
||||
a = self._decoder(rawmode, l)
|
||||
a = self._decoder(rawmode, layer)
|
||||
# FIXME: this doesn't work if the image size
|
||||
# is not a multiple of the tile size...
|
||||
self.tile.append(
|
||||
|
@ -1336,7 +1336,7 @@ class TiffImageFile(ImageFile.ImageFile):
|
|||
x, y = 0, y + h
|
||||
if y >= self.size[1]:
|
||||
x = y = 0
|
||||
l += 1
|
||||
layer += 1
|
||||
a = None
|
||||
else:
|
||||
if DEBUG:
|
||||
|
|
|
@ -23,7 +23,8 @@ from collections import namedtuple
|
|||
class TagInfo(namedtuple("_TagInfo", "value name type length enum")):
|
||||
__slots__ = []
|
||||
|
||||
def __new__(cls, value=None, name="unknown", type=None, length=None, enum=None):
|
||||
def __new__(cls, value=None, name="unknown",
|
||||
type=None, length=None, enum=None):
|
||||
return super(TagInfo, cls).__new__(
|
||||
cls, value, name, type, length, enum or {})
|
||||
|
||||
|
@ -72,8 +73,8 @@ TAGS_V2 = {
|
|||
257: ("ImageLength", LONG, 1),
|
||||
258: ("BitsPerSample", SHORT, 0),
|
||||
259: ("Compression", SHORT, 1,
|
||||
{"Uncompressed": 1, "CCITT 1d": 2, "Group 3 Fax": 3, "Group 4 Fax": 4,
|
||||
"LZW": 5, "JPEG": 6, "PackBits": 32773}),
|
||||
{"Uncompressed": 1, "CCITT 1d": 2, "Group 3 Fax": 3,
|
||||
"Group 4 Fax": 4, "LZW": 5, "JPEG": 6, "PackBits": 32773}),
|
||||
|
||||
262: ("PhotometricInterpretation", SHORT, 1,
|
||||
{"WhiteIsZero": 0, "BlackIsZero": 1, "RGB": 2, "RGB Palette": 3,
|
||||
|
|
|
@ -22,7 +22,8 @@
|
|||
from __future__ import print_function
|
||||
|
||||
from . import Image, ImageFile
|
||||
from ._binary import i16le as word, si16le as short, i32le as dword, si32le as _long
|
||||
from ._binary import i16le as word, si16le as short, \
|
||||
i32le as dword, si32le as _long
|
||||
from ._util import py3
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import os, sys
|
||||
import os
|
||||
import sys
|
||||
|
||||
py3 = sys.version_info.major >= 3
|
||||
|
||||
|
|
|
@ -434,7 +434,7 @@ polygon_generic(Imaging im, int n, Edge *e, int ink, int eofill,
|
|||
}
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
/* This causes that the pixels of horizontal edges are drawn twice :(
|
||||
/* This causes the pixels of horizontal edges to be drawn twice :(
|
||||
* but without it there are inconsistencies in ellipses */
|
||||
if (e[i].ymin == e[i].ymax) {
|
||||
(*hline)(im, e[i].xmin, e[i].ymin, e[i].xmax, ink);
|
||||
|
|