diff --git a/CHANGES.rst b/CHANGES.rst index 0c31eb3c7..b4bbfbb60 100644 --- a/CHANGES.rst +++ b/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) ------------------ diff --git a/RELEASING.md b/RELEASING.md index 936d68d8d..3c0e50457 100644 --- a/RELEASING.md +++ b/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 Travis CI. @@ -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,7 +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-*`` +* [ ] Upload all binaries and source distributions e.g. ``twine upload dist/Pillow-5.2.0-*`` * [ ] In compliance with [PEP 440](https://www.python.org/dev/peps/pep-0440/), append `.dev0` to version identifier in `src/PIL/_version.py` ## Point Release @@ -32,17 +32,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``. * [ ] Check out release branch e.g.: ``` - git checkout -t remotes/origin/2.9.x + git checkout -t remotes/origin/5.2.x ``` * [ ] In compliance with [PEP 440](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 +99,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/ diff --git a/Tests/helper.py b/Tests/helper.py index 5dbdb66be..b6ef6dc13 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -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 diff --git a/Tests/images/bw_gradient.png b/Tests/images/bw_gradient.png new file mode 100644 index 000000000..79c921486 Binary files /dev/null and b/Tests/images/bw_gradient.png differ diff --git a/Tests/images/imagedraw_outline_chord_L.png b/Tests/images/imagedraw_outline_chord_L.png new file mode 100644 index 000000000..9c20ad217 Binary files /dev/null and b/Tests/images/imagedraw_outline_chord_L.png differ diff --git a/Tests/images/imagedraw_outline_chord_RGB.png b/Tests/images/imagedraw_outline_chord_RGB.png new file mode 100644 index 000000000..9e9cb5af0 Binary files /dev/null and b/Tests/images/imagedraw_outline_chord_RGB.png differ diff --git a/Tests/images/imagedraw_outline_ellipse_L.png b/Tests/images/imagedraw_outline_ellipse_L.png new file mode 100644 index 000000000..53b76b62b Binary files /dev/null and b/Tests/images/imagedraw_outline_ellipse_L.png differ diff --git a/Tests/images/imagedraw_outline_ellipse_RGB.png b/Tests/images/imagedraw_outline_ellipse_RGB.png new file mode 100644 index 000000000..37a519327 Binary files /dev/null and b/Tests/images/imagedraw_outline_ellipse_RGB.png differ diff --git a/Tests/images/imagedraw_outline_pieslice_L.png b/Tests/images/imagedraw_outline_pieslice_L.png new file mode 100644 index 000000000..92972d54c Binary files /dev/null and b/Tests/images/imagedraw_outline_pieslice_L.png differ diff --git a/Tests/images/imagedraw_outline_pieslice_RGB.png b/Tests/images/imagedraw_outline_pieslice_RGB.png new file mode 100644 index 000000000..4be4fc4af Binary files /dev/null and b/Tests/images/imagedraw_outline_pieslice_RGB.png differ diff --git a/Tests/images/imagedraw_outline_polygon_L.png b/Tests/images/imagedraw_outline_polygon_L.png new file mode 100644 index 000000000..57ed9d43b Binary files /dev/null and b/Tests/images/imagedraw_outline_polygon_L.png differ diff --git a/Tests/images/imagedraw_outline_polygon_RGB.png b/Tests/images/imagedraw_outline_polygon_RGB.png new file mode 100644 index 000000000..286b71c23 Binary files /dev/null and b/Tests/images/imagedraw_outline_polygon_RGB.png differ diff --git a/Tests/images/imagedraw_outline_rectangle_L.png b/Tests/images/imagedraw_outline_rectangle_L.png new file mode 100644 index 000000000..b9c47018f Binary files /dev/null and b/Tests/images/imagedraw_outline_rectangle_L.png differ diff --git a/Tests/images/imagedraw_outline_rectangle_RGB.png b/Tests/images/imagedraw_outline_rectangle_RGB.png new file mode 100644 index 000000000..41b92fb75 Binary files /dev/null and b/Tests/images/imagedraw_outline_rectangle_RGB.png differ diff --git a/Tests/images/imagedraw_outline_shape_L.png b/Tests/images/imagedraw_outline_shape_L.png new file mode 100644 index 000000000..20ebef157 Binary files /dev/null and b/Tests/images/imagedraw_outline_shape_L.png differ diff --git a/Tests/images/imagedraw_outline_shape_RGB.png b/Tests/images/imagedraw_outline_shape_RGB.png new file mode 100644 index 000000000..6fb6f623e Binary files /dev/null and b/Tests/images/imagedraw_outline_shape_RGB.png differ diff --git a/Tests/test_color_lut.py b/Tests/test_color_lut.py index c9793c950..6d0b76fed 100644 --- a/Tests/test_color_lut.py +++ b/Tests/test_color_lut.py @@ -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), "") 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)) diff --git a/Tests/test_file_eps.py b/Tests/test_file_eps.py index 9d6bed060..66fedee90 100644 --- a/Tests/test_file_eps.py +++ b/Tests/test_file_eps.py @@ -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): diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index b1006a630..086a0f5d0 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -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') diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 2d9a9a091..c42d67885 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -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') diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 05433d9b6..77caa0b9d 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -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") diff --git a/Tests/test_file_pdf.py b/Tests/test_file_pdf.py index 3d359f445..f012fb9d8 100644 --- a/Tests/test_file_pdf.py +++ b/Tests/test_file_pdf.py @@ -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: diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index bc2b557d2..e9dcd5203 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -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 diff --git a/Tests/test_file_tga.py b/Tests/test_file_tga.py index c7e8ab71b..226b899dc 100644 --- a/Tests/test_file_tga.py +++ b/Tests/test_file_tga.py @@ -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 = { diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index aac845e0f..79630c773 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -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) diff --git a/Tests/test_file_webp_animated.py b/Tests/test_file_webp_animated.py index f98cde764..6b3dc1622 100644 --- a/Tests/test_file_webp_animated.py +++ b/Tests/test_file_webp_animated.py @@ -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): diff --git a/Tests/test_font_leaks.py b/Tests/test_font_leaks.py index 4de28a95a..f1ce44e6d 100644 --- a/Tests/test_font_leaks.py +++ b/Tests/test_font_leaks.py @@ -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) diff --git a/Tests/test_image_access.py b/Tests/test_image_access.py index 9cc623856..7a9378bbd 100644 --- a/Tests/test_image_access.py +++ b/Tests/test_image_access.py @@ -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']) diff --git a/Tests/test_image_array.py b/Tests/test_image_array.py index 11c2648bb..7a86a3e54 100644 --- a/Tests/test_image_array.py +++ b/Tests/test_image_array.py @@ -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)) diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index c7c381382..1e208d80c 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -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') diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py index 1ecb6cda6..3687a1a2c 100644 --- a/Tests/test_image_resample.py +++ b/Tests/test_image_resample.py @@ -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"): diff --git a/Tests/test_image_resize.py b/Tests/test_image_resize.py index 8d14b9008..535f1d77e 100644 --- a/Tests/test_image_resize.py +++ b/Tests/test_image_resize.py @@ -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)) diff --git a/Tests/test_image_rotate.py b/Tests/test_image_rotate.py index 8ddb5ddf8..17d394a02 100644 --- a/Tests/test_image_rotate.py +++ b/Tests/test_image_rotate.py @@ -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') diff --git a/Tests/test_image_toqimage.py b/Tests/test_image_toqimage.py index 6d7715c80..c9971cf73 100644 --- a/Tests/test_image_toqimage.py +++ b/Tests/test_image_toqimage.py @@ -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 diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index 4138f455f..1fdfe916d 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -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__': diff --git a/Tests/test_imagecolor.py b/Tests/test_imagecolor.py index 0aac21278..1ea37544b 100644 --- a/Tests/test_imagecolor.py +++ b/Tests/test_imagecolor.py @@ -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)"), diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 1c8fe3153..33e7f8477 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -576,6 +576,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() diff --git a/Tests/test_imageenhance.py b/Tests/test_imageenhance.py index e9727613b..54288f4db 100644 --- a/Tests/test_imageenhance.py +++ b/Tests/test_imageenhance.py @@ -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__': diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index 4b750af0d..837e81d30 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -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): diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 0a36734d2..f2116bdc4 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -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) diff --git a/Tests/test_imagefont_bitmap.py b/Tests/test_imagefont_bitmap.py index bee415fd7..8376728a2 100644 --- a/Tests/test_imagefont_bitmap.py +++ b/Tests/test_imagefont_bitmap.py @@ -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) diff --git a/Tests/test_imagefontctl.py b/Tests/test_imagefontctl.py index 89f9563f3..04432b14f 100644 --- a/Tests/test_imagefontctl.py +++ b/Tests/test_imagefontctl.py @@ -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) diff --git a/Tests/test_imagemorph.py b/Tests/test_imagemorph.py index 83fa0b5a9..dceadebf4 100644 --- a/Tests/test_imagemorph.py +++ b/Tests/test_imagemorph.py @@ -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 diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index 11cf3619d..9c4da2463 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -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() diff --git a/Tests/test_imageops_usm.py b/Tests/test_imageops_usm.py index 2666d3482..20758e9f8 100644 --- a/Tests/test_imageops_usm.py +++ b/Tests/test_imageops_usm.py @@ -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)) diff --git a/Tests/test_imagepalette.py b/Tests/test_imagepalette.py index 889f022ae..3b7087d7a 100644 --- a/Tests/test_imagepalette.py +++ b/Tests/test_imagepalette.py @@ -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__': diff --git a/Tests/test_lib_pack.py b/Tests/test_lib_pack.py index 002db2e19..e0fcf264a 100644 --- a/Tests/test_lib_pack.py +++ b/Tests/test_lib_pack.py @@ -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) diff --git a/Tests/test_locale.py b/Tests/test_locale.py index 142753791..5aef8427b 100644 --- a/Tests/test_locale.py +++ b/Tests/test_locale.py @@ -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) diff --git a/Tests/test_map.py b/Tests/test_map.py index 14bd835a2..8e3916d27 100644 --- a/Tests/test_map.py +++ b/Tests/test_map.py @@ -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 diff --git a/Tests/test_numpy.py b/Tests/test_numpy.py index 1501e54bb..4efcd2c51 100644 --- a/Tests/test_numpy.py +++ b/Tests/test_numpy.py @@ -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) diff --git a/Tests/test_pdfparser.py b/Tests/test_pdfparser.py index db97c97dd..42c813520 100644 --- a/Tests/test_pdfparser.py +++ b/Tests/test_pdfparser.py @@ -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>") diff --git a/docs/releasenotes/5.3.0.rst b/docs/releasenotes/5.3.0.rst new file mode 100644 index 000000000..9869b6a7a --- /dev/null +++ b/docs/releasenotes/5.3.0.rst @@ -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 +============= + diff --git a/docs/releasenotes/index.rst b/docs/releasenotes/index.rst index 16e5c1d85..fc8d686eb 100644 --- a/docs/releasenotes/index.rst +++ b/docs/releasenotes/index.rst @@ -6,6 +6,7 @@ Release Notes .. toctree:: :maxdepth: 2 + 5.3.0 5.2.0 5.1.0 5.0.0 diff --git a/setup.py b/setup.py index 761d552cc..9529787f9 100755 --- a/setup.py +++ b/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 diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index f9e5ee5c8..8b86f9a00 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -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 """ diff --git a/src/PIL/ContainerIO.py b/src/PIL/ContainerIO.py index 496ed6826..682ad9031 100644 --- a/src/PIL/ContainerIO.py +++ b/src/PIL/ContainerIO.py @@ -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 diff --git a/src/PIL/DdsImagePlugin.py b/src/PIL/DdsImagePlugin.py index e755f94b9..3bd65c93d 100644 --- a/src/PIL/DdsImagePlugin.py +++ b/src/PIL/DdsImagePlugin.py @@ -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("= 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 diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 1bfbb5ffd..fec2f7663 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -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 diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index d82e30efc..4b6281f13 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -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) diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index 5bc890252..ca8c1d17b 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -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) def ellipse(self, xy, fill=None, outline=None): @@ -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) 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): @@ -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) 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): @@ -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) 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]) diff --git a/src/PIL/ImageEnhance.py b/src/PIL/ImageEnhance.py index 11c9c3a06..1b78bfd9b 100644 --- a/src/PIL/ImageEnhance.py +++ b/src/PIL/ImageEnhance.py @@ -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): diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index 681dee524..bdcc43d1f 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -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 """ diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 3ac29e8f6..099ccc4ff 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -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, diff --git a/src/PIL/ImageMode.py b/src/PIL/ImageMode.py index b227f2127..2b3377a14 100644 --- a/src/PIL/ImageMode.py +++ b/src/PIL/ImageMode.py @@ -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") diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index 25d491aff..9b470062a 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -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) diff --git a/src/PIL/ImageQt.py b/src/PIL/ImageQt.py index c9dc36312..2930c1d9c 100644 --- a/src/PIL/ImageQt.py +++ b/src/PIL/ImageQt.py @@ -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) diff --git a/src/PIL/ImageStat.py b/src/PIL/ImageStat.py index cd58fc8ff..d4b38d856 100644 --- a/src/PIL/ImageStat.py +++ b/src/PIL/ImageStat.py @@ -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 diff --git a/src/PIL/ImageTk.py b/src/PIL/ImageTk.py index b5ad53df7..17bf32f62 100644 --- a/src/PIL/ImageTk.py +++ b/src/PIL/ImageTk.py @@ -195,7 +195,8 @@ class PhotoImage(object): # Pypy is using a ffi cdata element # (Pdb) self.tk.interp # - _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: diff --git a/src/PIL/Jpeg2KImagePlugin.py b/src/PIL/Jpeg2KImagePlugin.py index 25fbefbca..7b170fe16 100644 --- a/src/PIL/Jpeg2KImagePlugin.py +++ b/src/PIL/Jpeg2KImagePlugin.py @@ -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') diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index 97ef834d3..98c27010c 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -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") diff --git a/src/PIL/MspImagePlugin.py b/src/PIL/MspImagePlugin.py index 9692d1162..b2c7a3d79 100644 --- a/src/PIL/MspImagePlugin.py +++ b/src/PIL/MspImagePlugin.py @@ -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]) diff --git a/src/PIL/PdfImagePlugin.py b/src/PIL/PdfImagePlugin.py index 8538bcd49..d411bfc41 100644 --- a/src/PIL/PdfImagePlugin.py +++ b/src/PIL/PdfImagePlugin.py @@ -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 diff --git a/src/PIL/PdfParser.py b/src/PIL/PdfParser.py index c0635ef31..971f44514 100644 --- a/src/PIL/PdfParser.py +++ b/src/PIL/PdfParser.py @@ -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) diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 826061990..4f1f0f9cf 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -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): diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index c599ba8d5..9866c9040 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -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() diff --git a/src/PIL/SgiImagePlugin.py b/src/PIL/SgiImagePlugin.py index ef0f40ebd..113d44fac 100644 --- a/src/PIL/SgiImagePlugin.py +++ b/src/PIL/SgiImagePlugin.py @@ -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 diff --git a/src/PIL/SunImagePlugin.py b/src/PIL/SunImagePlugin.py index fd5e82724..3126bd9d6 100644 --- a/src/PIL/SunImagePlugin.py +++ b/src/PIL/SunImagePlugin.py @@ -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') diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 6f032f49d..66b211cbf 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -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: diff --git a/src/PIL/TiffTags.py b/src/PIL/TiffTags.py index 427f3a489..ef19ee66e 100644 --- a/src/PIL/TiffTags.py +++ b/src/PIL/TiffTags.py @@ -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, diff --git a/src/PIL/WmfImagePlugin.py b/src/PIL/WmfImagePlugin.py index 213584497..aeb19374d 100644 --- a/src/PIL/WmfImagePlugin.py +++ b/src/PIL/WmfImagePlugin.py @@ -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 diff --git a/src/PIL/_util.py b/src/PIL/_util.py index 6618c625f..e6989d69d 100644 --- a/src/PIL/_util.py +++ b/src/PIL/_util.py @@ -1,4 +1,5 @@ -import os, sys +import os +import sys py3 = sys.version_info.major >= 3 diff --git a/src/PIL/_version.py b/src/PIL/_version.py index b42628dde..f4bf34e00 100644 --- a/src/PIL/_version.py +++ b/src/PIL/_version.py @@ -1,2 +1,2 @@ # Master version for Pillow -__version__ = '5.2.0' +__version__ = '5.3.0.dev0' diff --git a/src/libImaging/Draw.c b/src/libImaging/Draw.c index 7b7c5fac0..a3bb92309 100644 --- a/src/libImaging/Draw.c +++ b/src/libImaging/Draw.c @@ -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);