From 4d2dd3ee7bffcbb264912d7e01add8c099024cae Mon Sep 17 00:00:00 2001 From: Charles Law Date: Fri, 26 Dec 2014 15:12:21 -0800 Subject: [PATCH 01/16] Partial Fix #370: look for osx fonts in common places --- PIL/ImageFont.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/PIL/ImageFont.py b/PIL/ImageFont.py index bd52ea938..e59f6a46d 100644 --- a/PIL/ImageFont.py +++ b/PIL/ImageFont.py @@ -239,6 +239,10 @@ def truetype(font=None, size=10, index=0, encoding="", filename=None): try: return FreeTypeFont(font, size, index, encoding) except IOError: + if font.endswith(".ttf"): + ttf_filename = font + else: + ttf_filename = "%s.ttf" % font if sys.platform == "win32": # check the windows font repository # NOTE: must use uppercase WINDIR, to work around bugs in @@ -247,6 +251,12 @@ def truetype(font=None, size=10, index=0, encoding="", filename=None): if windir: filename = os.path.join(windir, "fonts", font) return FreeTypeFont(filename, size, index, encoding) + elif sys.platform == 'darwin': + macdirs = ['/Library/Fonts/', '/System/Library/Fonts/', os.path.expanduser('~/Library/Fonts/')] + for macdir in macdirs: + filepath = os.path.join(macdir, ttf_filename) + if os.path.exists(filepath): + return FreeTypeFont(filepath, size, index, encoding) raise From 17d720aebc82f9d0b99ad88d7bb1d12023a44a50 Mon Sep 17 00:00:00 2001 From: Charles Law Date: Fri, 26 Dec 2014 15:10:52 -0800 Subject: [PATCH 02/16] Partial Fix #370: look for linux fonts in common places --- PIL/ImageFont.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/PIL/ImageFont.py b/PIL/ImageFont.py index e59f6a46d..5b4770f54 100644 --- a/PIL/ImageFont.py +++ b/PIL/ImageFont.py @@ -251,6 +251,14 @@ def truetype(font=None, size=10, index=0, encoding="", filename=None): if windir: filename = os.path.join(windir, "fonts", font) return FreeTypeFont(filename, size, index, encoding) + elif sys.platform in ('linux', 'linux2'): + lindirs = os.environ.get("XDG_DATA_DIRS", "").split(":") + for lindir in lindirs: + parentpath = os.path.join(lindir, "fonts") + for walkroot, walkdir, walkfilenames in os.walk(parentpath): + if ttf_filename in walkfilenames: + filepath = os.path.join(walkroot, ttf_filename) + return FreeTypeFont(filepath, size, index, encoding) elif sys.platform == 'darwin': macdirs = ['/Library/Fonts/', '/System/Library/Fonts/', os.path.expanduser('~/Library/Fonts/')] for macdir in macdirs: From 44286ba3c9bfa6ed565d11bd61460d8ec215e1ea Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 30 Nov 2014 23:31:29 -0800 Subject: [PATCH 03/16] Fix for zlib.decompression bomb in iTXt,zTXt, and iCCP chunks --- PIL/PngImagePlugin.py | 13 ++++++++++--- Tests/check_png_dos.py | 24 ++++++++++++++++++++++++ Tests/images/png_decompression_dos.png | Bin 0 -> 6289 bytes 3 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 Tests/check_png_dos.py create mode 100644 Tests/images/png_decompression_dos.png diff --git a/PIL/PngImagePlugin.py b/PIL/PngImagePlugin.py index 8403461be..514b76f14 100644 --- a/PIL/PngImagePlugin.py +++ b/PIL/PngImagePlugin.py @@ -72,6 +72,13 @@ _MODES = { _simple_palette = re.compile(b'^\xff+\x00\xff*$') +def _safe_zlib_decompress(s): + dobj = zlib.decompressobj() + plaintext = dobj.decompress(s, ImageFile.SAFEBLOCK) + if dobj.unconsumed_tail: + raise ValueError("Decompressed Data Too Large") + return plaintext + # -------------------------------------------------------------------- # Support classes. Suitable for PNG and related formats like MNG etc. @@ -278,7 +285,7 @@ class PngStream(ChunkStream): raise SyntaxError("Unknown compression method %s in iCCP chunk" % comp_method) try: - icc_profile = zlib.decompress(s[i+2:]) + icc_profile = _safe_zlib_decompress(s[i+2:]) except zlib.error: icc_profile = None # FIXME self.im_info["icc_profile"] = icc_profile @@ -391,7 +398,7 @@ class PngStream(ChunkStream): raise SyntaxError("Unknown compression method %s in zTXt chunk" % comp_method) try: - v = zlib.decompress(v[1:]) + v = _safe_zlib_decompress(v[1:]) except zlib.error: v = b"" @@ -421,7 +428,7 @@ class PngStream(ChunkStream): if cf != 0: if cm == 0: try: - v = zlib.decompress(v) + v = _safe_zlib_decompress(v) except zlib.error: return s else: diff --git a/Tests/check_png_dos.py b/Tests/check_png_dos.py new file mode 100644 index 000000000..4e9b76537 --- /dev/null +++ b/Tests/check_png_dos.py @@ -0,0 +1,24 @@ +from helper import unittest, PillowTestCase +import sys +from PIL import Image +from io import BytesIO + +test_file = "Tests/images/png_decompression_dos.png" + +@unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or MacOS") +class TestPngDos(PillowTestCase): + + def test_dos_text(self): + + try: + im = Image.open(test_file) + im.load() + except ValueError as msg: + self.assert_(msg, "Decompressed Data Too Large") + return + + for s in im.text.values(): + self.assert_(len(s) < 1024*1024, "Text chunk larger than 1M") + +if __name__ == '__main__': + unittest.main() diff --git a/Tests/images/png_decompression_dos.png b/Tests/images/png_decompression_dos.png new file mode 100644 index 0000000000000000000000000000000000000000..986561b2e78e76e92e0a8b07886dc26f6444257b GIT binary patch literal 6289 zcmeAS@N?(olHy`uVBq!ia0vp^j3CSbBp9sfW`{B`aPU=yM3f|DrW-OaRLpsM&=92H z&;qa5^Uq1xIe<8$U^E0qLtr!nMnhmU1V%$(c!xj@@8mO4jE~Ov%|>b&7>%|JhIg!w z`ffA?MnhmU1V%$(Gz4&k0HkHWQYFiY)G{z0Z5iMSuTk;Q5Eu=C(GVC7fzc2c-XQ>K p8N9c+xC7`9Q7kcv6U2|zXz1Ea_KC51p1gQu&X%Q~loCIE&GIWYhL literal 0 HcmV?d00001 From b73c4b9e8b4f7bee3ca64ea5524fbf233ac42617 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 28 Dec 2014 22:34:13 -0800 Subject: [PATCH 04/16] Test change -- different representation for invalid compressed object --- Tests/test_file_png.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index 7a43414eb..b556199f5 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -153,7 +153,7 @@ class TestFilePng(PillowTestCase): im = load(HEAD + chunk(b'iTXt', b'spam\0\1\0en\0Spam\0' + zlib.compress(b"egg")[:1]) + TAIL) - self.assertEqual(im.info, {}) + self.assertEqual(im.info, {'spam':''}) im = load(HEAD + chunk(b'iTXt', b'spam\0\1\1en\0Spam\0' + zlib.compress(b"egg")) + TAIL) From ee7b15c9240f0226e3dfc144c7e8c8aa7bb17009 Mon Sep 17 00:00:00 2001 From: Charles Law Date: Mon, 29 Dec 2014 10:09:37 -0800 Subject: [PATCH 05/16] Add tests for finding fonts in linux and osx --- Tests/test_imagefont.py | 61 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index ed2439e7c..291736e4b 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -4,6 +4,8 @@ from PIL import Image from PIL import ImageDraw from io import BytesIO import os +import sys +import copy FONT_PATH = "Tests/fonts/FreeMono.ttf" FONT_SIZE = 20 @@ -13,6 +15,29 @@ try: from PIL import ImageFont ImageFont.core.getfont # check if freetype is available + class SimplePatcher(): + def __init__(self, parent_obj, attr_name, value): + self._parent_obj = parent_obj + self._attr_name = attr_name + self._saved = None + self._is_saved = False + self._value = value + def __enter__(self): + # Patch the attr on the object + if hasattr(self._parent_obj, self._attr_name): + self._saved = getattr(self._parent_obj, self._attr_name) + setattr(self._parent_obj, self._attr_name, self._value) + self._is_saved = True + else: + setattr(self._parent_obj, self._attr_name, self._value) + self._is_saved = False + def __exit__(self, type, value, traceback): + # Restore the original value + if self._is_saved: + setattr(self._parent_obj, self._attr_name, self._saved) + else: + delattr(self._parent_obj, self._attr_name) + class TestImageFont(PillowTestCase): def test_sanity(self): @@ -192,6 +217,42 @@ try: # Assert self.assert_image_equal(im, target_img) + def _test_fake_loading_font(self, path_to_fake): + #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): + if filepath == path_to_fake: + return ImageFont._FreeTypeFont(FONT_PATH, size, index, encoding) + return ImageFont._FreeTypeFont(filepath, size, index, encoding) + with SimplePatcher(ImageFont, 'FreeTypeFont', loadable_font): + font = ImageFont.truetype('Arial') + #Make sure it's loaded + name = font.getname() + self.assertEqual(('FreeMono', 'Regular'), name) + + def test_find_linux_font(self): + #A lot of mocking here - this is more for hitting code and catching + #syntax like errors + with SimplePatcher(sys, 'platform', 'linux'): + patched_env = copy.deepcopy(os.environ) + patched_env['XDG_DATA_DIRS'] = '/usr/share/:/usr/local/share/' + with SimplePatcher(os, 'environ', patched_env): + def fake_walker(path): + if path == '/usr/local/share/fonts': + return [(path, [], ['Arial.ttf'], )] + return [(path, [], ['some_random_font.ttf'], )] + with SimplePatcher(os, 'walk', fake_walker): + self._test_fake_loading_font('/usr/local/share/fonts/Arial.ttf') + + def test_find_osx_font(self): + #Like the linux test, more cover hitting code rather than testing + #correctness. + with SimplePatcher(sys, 'platform', 'darwin'): + fake_font_path = '/System/Library/Fonts/Arial.ttf' + with SimplePatcher(os.path, 'exists', lambda x: x == fake_font_path): + self._test_fake_loading_font(fake_font_path) + except ImportError: class TestImageFont(PillowTestCase): From 1c6a89e4c2c3a27d7dcd4d5a3662b1d3008d1ad3 Mon Sep 17 00:00:00 2001 From: Charles Law Date: Mon, 29 Dec 2014 17:03:39 -0800 Subject: [PATCH 06/16] default to using /usr/share when XDG_DATA_DIRS is empty or unset --- PIL/ImageFont.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/PIL/ImageFont.py b/PIL/ImageFont.py index 5b4770f54..afbae372f 100644 --- a/PIL/ImageFont.py +++ b/PIL/ImageFont.py @@ -252,7 +252,12 @@ def truetype(font=None, size=10, index=0, encoding="", filename=None): filename = os.path.join(windir, "fonts", font) return FreeTypeFont(filename, size, index, encoding) elif sys.platform in ('linux', 'linux2'): - lindirs = os.environ.get("XDG_DATA_DIRS", "").split(":") + lindirs = os.environ.get("XDG_DATA_DIRS", "") + if not lindirs: + #According to the freedesktop spec, XDG_DATA_DIRS should + #default to /usr/share + lindirs = '/usr/share' + lindirs = lindirs.split(":") for lindir in lindirs: parentpath = os.path.join(lindir, "fonts") for walkroot, walkdir, walkfilenames in os.walk(parentpath): From 0b75526ffe41a4697231beb8b5740617c98f290b Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 29 Dec 2014 17:10:27 -0800 Subject: [PATCH 07/16] Limit total text chunk size to 64k --- PIL/PngImagePlugin.py | 23 +++++++++++++++++++++-- Tests/check_png_dos.py | 30 +++++++++++++++++++++++++++--- 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/PIL/PngImagePlugin.py b/PIL/PngImagePlugin.py index 514b76f14..7a9becd3b 100644 --- a/PIL/PngImagePlugin.py +++ b/PIL/PngImagePlugin.py @@ -72,9 +72,15 @@ _MODES = { _simple_palette = re.compile(b'^\xff+\x00\xff*$') +# Maximum decompressed size for a iTXt or zTXt chunk. +# Eliminates decompression bombs where compressed chunks can expand 1000x +MAX_TEXT_CHUNK = ImageFile.SAFEBLOCK +# Set the maximum total text chunk size. +MAX_TEXT_MEMORY = 64 * MAX_TEXT_CHUNK + def _safe_zlib_decompress(s): dobj = zlib.decompressobj() - plaintext = dobj.decompress(s, ImageFile.SAFEBLOCK) + plaintext = dobj.decompress(s, MAX_TEXT_CHUNK) if dobj.unconsumed_tail: raise ValueError("Decompressed Data Too Large") return plaintext @@ -267,6 +273,14 @@ class PngStream(ChunkStream): self.im_tile = None self.im_palette = None + self.text_memory = 0 + + 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) + def chunk_iCCP(self, pos, length): # ICC profile @@ -379,6 +393,8 @@ class PngStream(ChunkStream): v = v.decode('latin-1', 'replace') self.im_info[k] = self.im_text[k] = v + self.check_text_memory(len(v)) + return s def chunk_zTXt(self, pos, length): @@ -408,6 +424,8 @@ class PngStream(ChunkStream): v = v.decode('latin-1', 'replace') self.im_info[k] = self.im_text[k] = v + self.check_text_memory(len(v)) + return s def chunk_iTXt(self, pos, length): @@ -443,7 +461,8 @@ class PngStream(ChunkStream): return s self.im_info[k] = self.im_text[k] = iTXt(v, lang, tk) - + self.check_text_memory(len(v)) + return s diff --git a/Tests/check_png_dos.py b/Tests/check_png_dos.py index 4e9b76537..8f974d293 100644 --- a/Tests/check_png_dos.py +++ b/Tests/check_png_dos.py @@ -1,13 +1,12 @@ from helper import unittest, PillowTestCase import sys -from PIL import Image +from PIL import Image, PngImagePlugin from io import BytesIO +import zlib test_file = "Tests/images/png_decompression_dos.png" -@unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or MacOS") class TestPngDos(PillowTestCase): - def test_dos_text(self): try: @@ -20,5 +19,30 @@ class TestPngDos(PillowTestCase): for s in im.text.values(): self.assert_(len(s) < 1024*1024, "Text chunk larger than 1M") + def test_dos_total_memory(self): + im = Image.new('L',(1,1)) + compressed_data = zlib.compress('a'*1024*1023) + + info = PngImagePlugin.PngInfo() + + for x in range(64): + info.add_text('t%s'%x, compressed_data, 1) + info.add_itxt('i%s'%x, compressed_data, zip=True) + + b = BytesIO() + im.save(b, 'PNG', pnginfo=info) + b.seek(0) + + try: + im2 = Image.open(b) + except ValueError as msg: + self.assert_("Too much memory" in msg) + return + + total_len = 0 + for txt in im2.text.values(): + total_len += len(txt) + self.assert_(total_len < 64*1024*1024) + if __name__ == '__main__': unittest.main() From 6696b780e3b38672106c12be12e82f195c93b71a Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 30 Dec 2014 16:57:24 -0800 Subject: [PATCH 08/16] Test style cleanup --- Tests/check_png_dos.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Tests/check_png_dos.py b/Tests/check_png_dos.py index 8f974d293..c74990a8c 100644 --- a/Tests/check_png_dos.py +++ b/Tests/check_png_dos.py @@ -1,23 +1,22 @@ from helper import unittest, PillowTestCase -import sys from PIL import Image, PngImagePlugin from io import BytesIO import zlib -test_file = "Tests/images/png_decompression_dos.png" +TEST_FILE = "Tests/images/png_decompression_dos.png" class TestPngDos(PillowTestCase): def test_dos_text(self): try: - im = Image.open(test_file) + im = Image.open(TEST_FILE) im.load() except ValueError as msg: - self.assert_(msg, "Decompressed Data Too Large") + self.assertTrue(msg, "Decompressed Data Too Large") return for s in im.text.values(): - self.assert_(len(s) < 1024*1024, "Text chunk larger than 1M") + self.assertLess(len(s), 1024*1024, "Text chunk larger than 1M") def test_dos_total_memory(self): im = Image.new('L',(1,1)) @@ -36,13 +35,13 @@ class TestPngDos(PillowTestCase): try: im2 = Image.open(b) except ValueError as msg: - self.assert_("Too much memory" in msg) + self.assertIn("Too much memory", msg) return total_len = 0 for txt in im2.text.values(): total_len += len(txt) - self.assert_(total_len < 64*1024*1024) + self.assertLess(total_len, 64*1024*1024, "Total text chunks greater than 64M") if __name__ == '__main__': unittest.main() From a59eb3975f9efb1ca88a7e823011ac0968d7d6ea Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 30 Dec 2014 17:06:38 -0800 Subject: [PATCH 09/16] Documentation Update for PNG zlib DOS --- docs/handbook/image-file-formats.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index f9216818d..f8db2660f 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -333,7 +333,12 @@ The :py:meth:`~PIL.Image.Image.open` method sets the following transparent palette image. ``Open`` also sets ``Image.text`` to a list of the values of the -``tEXt``, ``zTXt``, and ``iTXt`` chunks of the PNG image. +``tEXt``, ``zTXt``, and ``iTXt`` chunks of the PNG image. Individual +compressed chunks are limited to a decompressed size of +``PngImagePlugin.MAX_TEXT_CHUNK``, by default 1MB, to prevent +decompression bombs. Additionally, the total size of all of the text +chunks is limited to ``PngImagePlugin.MAX_TEXT_MEMORY``, defaulting to +64MB. The :py:meth:`~PIL.Image.Image.save` method supports the following options: From 3085a268969f6a739ce30656b8dd16dd1035df1a Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 31 Dec 2014 20:34:39 +0200 Subject: [PATCH 10/16] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 01dcca6cc..1eea075eb 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.7.0 (unreleased) ------------------ +- Fix potential PNG decompression DOS #1060 + [wiredfool] + - Use underscores, not spaces, in TIFF tag kwargs. #1044, #1058 [anntzer, hugovk] From 4889ae776ccbf27a7f955204067812f2aceefba3 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 31 Dec 2014 14:44:32 -0800 Subject: [PATCH 11/16] Updated Changes.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 1eea075eb..7440ed8b5 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.7.0 (unreleased) ------------------ +- Look for OSX and Linux fonts in common places. #1054 + [charleslaw] + - Fix potential PNG decompression DOS #1060 [wiredfool] From 6a7a6fbdb8ff03257ebd30576e6268bcc25cac0f Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 31 Dec 2014 16:35:56 -0800 Subject: [PATCH 12/16] Fixed warning on docs building (url can't wrap), added parameters --- PIL/ImageFilter.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/PIL/ImageFilter.py b/PIL/ImageFilter.py index 1e0154d12..b46845807 100644 --- a/PIL/ImageFilter.py +++ b/PIL/ImageFilter.py @@ -162,8 +162,13 @@ class UnsharpMask(Filter): See Wikipedia's entry on `digital unsharp masking`_ for an explanation of the parameters. - .. _digital unsharp masking: - https://en.wikipedia.org/wiki/Unsharp_masking#Digital_unsharp_masking + :param radius: Blur Radius + :param percent: Unsharp strength, in percent + :param threshold: Threshold controls the minimum brightness change that + will be sharpened + + .. _digital unsharp masking: https://en.wikipedia.org/wiki/Unsharp_masking#Digital_unsharp_masking + """ name = "UnsharpMask" From 80c7dcbce47437e5fb3fdb05c1fbbdbae0b31e88 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 31 Dec 2014 16:36:24 -0800 Subject: [PATCH 13/16] Release notes for PNG dos, Tiff parameter change --- docs/releasenotes/2.7.0.rst | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/releasenotes/2.7.0.rst b/docs/releasenotes/2.7.0.rst index bf3f163f2..abb717587 100644 --- a/docs/releasenotes/2.7.0.rst +++ b/docs/releasenotes/2.7.0.rst @@ -1,6 +1,21 @@ Pillow 2.7.0 ============ +Png text chunk size limits +-------------------------- + +To prevent potential denial of service attacks using compressed text +chunks, there are now limits to the decompressed size of text chunks +decoded from PNG images. If the limits are exceeded when opening a PNG +image a ``ValueError`` will be raised. + +Individual text chunks are limited to +:py:attr:`PIL.PngImagePlugin.MAX_TEXT_CHUNK`, set to 1MB by +default. The total decompressed size of all text chunks is limited to +:py:attr:`PIL.PngImagePlugin.MAX_TEXT_MEMORY`, which defaults to +64MB. These values can be changed prior to opening PNG images if you +know that there are large text blocks that are desired. + Image resizing filters ---------------------- @@ -141,3 +156,13 @@ The previous implementation takes into account only source pixels within so the quality was worse compared to other Gaussian blur software. The new implementation does not have this drawback. + +TFF Parameter Changes +---------------------- + +Several kwarg parameters for saving TIFF images were previously +specified as strings with included spaces (e.g. 'x resolution'). This +was difficult to use as kwargs without constructing and passing a +dictionary. These parameters now use the underscore character instead +of space. (e.g. 'x_resolution') + From ca4ec0cea28878791fa95ed92656f2e8445383a4 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 31 Dec 2014 17:17:42 -0800 Subject: [PATCH 14/16] Fixed array parameters to reflect new image resizing behavior --- Tests/test_scipy.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Tests/test_scipy.py b/Tests/test_scipy.py index e3d1d97cd..926bbb186 100644 --- a/Tests/test_scipy.py +++ b/Tests/test_scipy.py @@ -11,7 +11,10 @@ except: class Test_scipy_resize(PillowTestCase): - """ Tests for scipy regression in 2.6.0 """ + """ Tests for scipy regression in 2.6.0 + + Tests from https://github.com/scipy/scipy/blob/master/scipy/misc/pilutil.py + """ def setUp(self): if not HAS_SCIPY: @@ -27,10 +30,10 @@ class Test_scipy_resize(PillowTestCase): def test_imresize4(self): im = np.array([[1,2], [3,4]]) - res = np.array([[ 1. , 1. , 1.5, 2. ], - [ 1. , 1. , 1.5, 2. ], - [ 2. , 2. , 2.5, 3. ], - [ 3. , 3. , 3.5, 4. ]], dtype=np.float32) + res = np.array([[ 1. , 1.25, 1.75, 2. ], + [ 1.5 , 1.75, 2.25, 2.5 ], + [ 2.5 , 2.75, 3.25, 3.5 ], + [ 3. , 3.25, 3.75, 4. ]], dtype=np.float32) # Check that resizing by target size, float and int are the same im2 = misc.imresize(im, (4,4), mode='F') # output size im3 = misc.imresize(im, 2., mode='F') # fraction From 96c06de6955a24874d4b0a8a6850ba648f54e9a8 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 31 Dec 2014 19:59:50 -0800 Subject: [PATCH 15/16] Restrict tests to linux/osx --- Tests/test_imagefont.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 291736e4b..1f935088c 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -231,6 +231,8 @@ try: name = font.getname() self.assertEqual(('FreeMono', 'Regular'), name) + + @unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or MacOS") def test_find_linux_font(self): #A lot of mocking here - this is more for hitting code and catching #syntax like errors @@ -245,6 +247,7 @@ try: with SimplePatcher(os, 'walk', fake_walker): self._test_fake_loading_font('/usr/local/share/fonts/Arial.ttf') + @unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or MacOS") def test_find_osx_font(self): #Like the linux test, more cover hitting code rather than testing #correctness. From 0f05eb287a223ce106848cd048cfcb45e9faa565 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 31 Dec 2014 20:34:36 -0800 Subject: [PATCH 16/16] Version Bump --2.7.0 --- CHANGES.rst | 11 ++++++++++- PIL/__init__.py | 2 +- _imaging.c | 2 +- setup.py | 2 +- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 7440ed8b5..9db9a6645 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,7 +1,7 @@ Changelog (Pillow) ================== -2.7.0 (unreleased) +2.7.0 (2015-01-01) ------------------ - Look for OSX and Linux fonts in common places. #1054 @@ -79,6 +79,15 @@ Changelog (Pillow) - Fixes for things rpmlint complains about #942 [manisandro] +2.6.2 (2015-01-01) +------------------ + +- Fix potential PNG decompression DOS #1060 + [wiredfool] + +- Fix Regression in PyPy 2.4 in streamio #958 + [wiredfool] + 2.6.1 (2014-10-11) ------------------ diff --git a/PIL/__init__.py b/PIL/__init__.py index 1bb1250c8..4d5d7b35b 100644 --- a/PIL/__init__.py +++ b/PIL/__init__.py @@ -12,7 +12,7 @@ # ;-) VERSION = '1.1.7' # PIL version -PILLOW_VERSION = '2.6.0' # Pillow +PILLOW_VERSION = '2.7.0' # Pillow _plugins = ['BmpImagePlugin', 'BufrStubImagePlugin', diff --git a/_imaging.c b/_imaging.c index e11605be4..6ae120688 100644 --- a/_imaging.c +++ b/_imaging.c @@ -71,7 +71,7 @@ * See the README file for information on usage and redistribution. */ -#define PILLOW_VERSION "2.6.0" +#define PILLOW_VERSION "2.7.0" #include "Python.h" diff --git a/setup.py b/setup.py index b1f917f26..90687d51f 100644 --- a/setup.py +++ b/setup.py @@ -90,7 +90,7 @@ except (ImportError, OSError): NAME = 'Pillow' -PILLOW_VERSION = '2.6.0' +PILLOW_VERSION = '2.7.0' TCL_ROOT = None JPEG_ROOT = None JPEG2K_ROOT = None