From b685c18e95487076897d75004d21a01e1c748157 Mon Sep 17 00:00:00 2001 From: panjing <599194993@qq.com> Date: Sat, 4 Apr 2020 11:22:11 +0800 Subject: [PATCH 01/23] Save in icns format to support all operating systems. --- src/PIL/IcnsImagePlugin.py | 95 ++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 44 deletions(-) diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index c003926154e..ce317f12c20 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -6,11 +6,13 @@ # # history: # 2004-10-09 fl Turned into a PIL plugin; removed 2.3 dependencies. +# 2020-04-04 Save in icns format to support all operating systems. # # Copyright (c) 2004 by Bob Ippolito. # Copyright (c) 2004 by Secret Labs. # Copyright (c) 2004 by Fredrik Lundh. # Copyright (c) 2014 by Alastair Houghton. +# Copyright (c) 2020 by Pan Jing. # # See the README file for information on usage and redistribution. # @@ -130,7 +132,6 @@ def read_png_or_jpeg2000(fobj, start_length, size): class IcnsFile: - SIZES = { (512, 512, 2): [(b"ic10", read_png_or_jpeg2000)], (512, 512, 1): [(b"ic09", read_png_or_jpeg2000)], @@ -302,65 +303,71 @@ def load(self): self.load_end() +def to_int(s): + b = s.encode('ascii') + return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3] + + +MAGIC = to_int("icns") +HEADER_SIZE = 8 +TOC = 'TOC ' + + def _save(im, fp, filename): """ Saves the image as a series of PNG files, that are then converted to a .icns file - using the macOS command line utility 'iconutil'. - macOS only. + Support for arbitrary systems """ if hasattr(fp, "flush"): fp.flush() - # create the temporary set of pngs - with tempfile.TemporaryDirectory(".iconset") as iconset: - provided_images = { - im.width: im for im in im.encoderinfo.get("append_images", []) - } - last_w = None - second_path = None - for w in [16, 32, 128, 256, 512]: - prefix = "icon_{}x{}".format(w, w) - - first_path = os.path.join(iconset, prefix + ".png") - if last_w == w: - shutil.copyfile(second_path, first_path) - else: - im_w = provided_images.get(w, im.resize((w, w), Image.LANCZOS)) - im_w.save(first_path) - - second_path = os.path.join(iconset, prefix + "@2x.png") - im_w2 = provided_images.get(w * 2, im.resize((w * 2, w * 2), Image.LANCZOS)) - im_w2.save(second_path) - last_w = w * 2 - - # iconutil -c icns -o {} {} - - convert_cmd = ["iconutil", "-c", "icns", "-o", filename, iconset] - convert_proc = subprocess.Popen( - convert_cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL - ) - - convert_proc.stdout.close() - - retcode = convert_proc.wait() - - if retcode: - raise subprocess.CalledProcessError(retcode, convert_cmd) + # size + sizes = [128, 256, 512, 32, 64, 256, 512, 1024] + size_str = ['ic07', 'ic08', 'ic09', 'ic11', 'ic12', 'ic13', 'ic14', 'ic10'] + file_size = 0 + entries = [] + for index, s in enumerate(sizes): + temp = io.BytesIO() + nb = im.resize((s, s)) + nb.save(temp, 'png') + file_size += len(temp.getvalue()) + entries.append({ + 'type': size_str[index], + 'size': len(temp.getvalue()), + 'stream': temp + }) + + # Header + fp.write(struct.pack('i', MAGIC)[::-1]) + fp.write(struct.pack('i', file_size)[::-1]) + + # TOC + toc_size = HEADER_SIZE + (len(entries) * HEADER_SIZE) + fp.write(struct.pack('i', to_int(TOC))[::-1]) + fp.write(struct.pack('i', toc_size)[::-1]) + for e in entries: + fp.write(struct.pack('i', to_int(e.get('type')))[::-1]) + fp.write(struct.pack('i', HEADER_SIZE + e.get('size'))[::-1]) + + # Data + for index, e in enumerate(entries): + fp.write(struct.pack('i', to_int(e.get('type')))[::-1]) + fp.write(struct.pack('i', HEADER_SIZE + e.get('size'))[::-1]) + fp.write(e.get('stream').getvalue()) + + fp.flush() Image.register_open(IcnsImageFile.format, IcnsImageFile, lambda x: x[:4] == b"icns") Image.register_extension(IcnsImageFile.format, ".icns") -if sys.platform == "darwin": - Image.register_save(IcnsImageFile.format, _save) - - Image.register_mime(IcnsImageFile.format, "image/icns") - +# if sys.platform == "darwin": +Image.register_save(IcnsImageFile.format, _save) +Image.register_mime(IcnsImageFile.format, "image/icns") if __name__ == "__main__": - if len(sys.argv) < 2: print("Syntax: python IcnsImagePlugin.py [file]") sys.exit() From 4500acb27471f37b5cf9c8de910c28334db6dce7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 4 Apr 2020 15:02:02 +1100 Subject: [PATCH 02/23] Lint fixes --- src/PIL/IcnsImagePlugin.py | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index ce317f12c20..8d02e44e482 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -19,11 +19,8 @@ import io import os -import shutil import struct -import subprocess import sys -import tempfile from PIL import Image, ImageFile, PngImagePlugin from PIL._binary import i8 @@ -304,13 +301,13 @@ def load(self): def to_int(s): - b = s.encode('ascii') + b = s.encode("ascii") return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3] MAGIC = to_int("icns") HEADER_SIZE = 8 -TOC = 'TOC ' +TOC = "TOC " def _save(im, fp, filename): @@ -325,37 +322,35 @@ def _save(im, fp, filename): # size sizes = [128, 256, 512, 32, 64, 256, 512, 1024] - size_str = ['ic07', 'ic08', 'ic09', 'ic11', 'ic12', 'ic13', 'ic14', 'ic10'] + size_str = ["ic07", "ic08", "ic09", "ic11", "ic12", "ic13", "ic14", "ic10"] file_size = 0 entries = [] for index, s in enumerate(sizes): temp = io.BytesIO() nb = im.resize((s, s)) - nb.save(temp, 'png') + nb.save(temp, "png") file_size += len(temp.getvalue()) - entries.append({ - 'type': size_str[index], - 'size': len(temp.getvalue()), - 'stream': temp - }) + entries.append( + {"type": size_str[index], "size": len(temp.getvalue()), "stream": temp} + ) # Header - fp.write(struct.pack('i', MAGIC)[::-1]) - fp.write(struct.pack('i', file_size)[::-1]) + fp.write(struct.pack("i", MAGIC)[::-1]) + fp.write(struct.pack("i", file_size)[::-1]) # TOC toc_size = HEADER_SIZE + (len(entries) * HEADER_SIZE) - fp.write(struct.pack('i', to_int(TOC))[::-1]) - fp.write(struct.pack('i', toc_size)[::-1]) + fp.write(struct.pack("i", to_int(TOC))[::-1]) + fp.write(struct.pack("i", toc_size)[::-1]) for e in entries: - fp.write(struct.pack('i', to_int(e.get('type')))[::-1]) - fp.write(struct.pack('i', HEADER_SIZE + e.get('size'))[::-1]) + fp.write(struct.pack("i", to_int(e.get("type")))[::-1]) + fp.write(struct.pack("i", HEADER_SIZE + e.get("size"))[::-1]) # Data for index, e in enumerate(entries): - fp.write(struct.pack('i', to_int(e.get('type')))[::-1]) - fp.write(struct.pack('i', HEADER_SIZE + e.get('size'))[::-1]) - fp.write(e.get('stream').getvalue()) + fp.write(struct.pack("i", to_int(e.get("type")))[::-1]) + fp.write(struct.pack("i", HEADER_SIZE + e.get("size"))[::-1]) + fp.write(e.get("stream").getvalue()) fp.flush() From 5ef382f61279185dec365cd2122ef2768c8cd237 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 4 Apr 2020 15:02:15 +1100 Subject: [PATCH 03/23] Test all operating systems --- Tests/test_file_icns.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/Tests/test_file_icns.py b/Tests/test_file_icns.py index aeb146f7ec2..9278888fcac 100644 --- a/Tests/test_file_icns.py +++ b/Tests/test_file_icns.py @@ -1,5 +1,4 @@ import io -import sys import pytest from PIL import IcnsImagePlugin, Image @@ -25,7 +24,6 @@ def test_sanity(): assert im.format == "ICNS" -@pytest.mark.skipif(sys.platform != "darwin", reason="Requires macOS") def test_save(tmp_path): temp_file = str(tmp_path / "temp.icns") @@ -38,7 +36,6 @@ def test_save(tmp_path): assert reread.format == "ICNS" -@pytest.mark.skipif(sys.platform != "darwin", reason="Requires macOS") def test_save_append_images(tmp_path): temp_file = str(tmp_path / "temp.icns") provided_im = Image.new("RGBA", (32, 32), (255, 0, 0, 128)) From ea98bb61638f3c65ecd8236798ff85a83b211476 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 4 Apr 2020 14:54:49 +1100 Subject: [PATCH 04/23] Restored append_images --- src/PIL/IcnsImagePlugin.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index 8d02e44e482..8a576315cb2 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -325,9 +325,10 @@ def _save(im, fp, filename): size_str = ["ic07", "ic08", "ic09", "ic11", "ic12", "ic13", "ic14", "ic10"] file_size = 0 entries = [] + provided_images = {im.width: im for im in im.encoderinfo.get("append_images", [])} for index, s in enumerate(sizes): temp = io.BytesIO() - nb = im.resize((s, s)) + nb = provided_images[s] if s in provided_images else im.resize((s, s)) nb.save(temp, "png") file_size += len(temp.getvalue()) entries.append( From cc1e78ccdfea5783c449b006c4e9cadc713cd459 Mon Sep 17 00:00:00 2001 From: panjing <599194993@qq.com> Date: Tue, 7 Apr 2020 13:59:27 +0800 Subject: [PATCH 05/23] Remove unused packages --- src/PIL/IcnsImagePlugin.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index ce317f12c20..4edf8759e82 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -19,11 +19,8 @@ import io import os -import shutil import struct -import subprocess import sys -import tempfile from PIL import Image, ImageFile, PngImagePlugin from PIL._binary import i8 @@ -363,7 +360,6 @@ def _save(im, fp, filename): Image.register_open(IcnsImageFile.format, IcnsImageFile, lambda x: x[:4] == b"icns") Image.register_extension(IcnsImageFile.format, ".icns") -# if sys.platform == "darwin": Image.register_save(IcnsImageFile.format, _save) Image.register_mime(IcnsImageFile.format, "image/icns") From a618e2ca1cd1b761ffaa6ea9c95b25b92f597d9d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 7 Apr 2020 21:13:40 +1000 Subject: [PATCH 06/23] Fixed big-endian saving --- src/PIL/IcnsImagePlugin.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index 660de2b9250..963ad199e48 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -336,21 +336,21 @@ def _save(im, fp, filename): ) # Header - fp.write(struct.pack("i", MAGIC)[::-1]) - fp.write(struct.pack("i", file_size)[::-1]) + fp.write(struct.pack(" Date: Tue, 7 Apr 2020 21:11:16 +1000 Subject: [PATCH 07/23] Marked to_int() as private --- src/PIL/IcnsImagePlugin.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index 963ad199e48..e3b274295fb 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -300,12 +300,12 @@ def load(self): self.load_end() -def to_int(s): +def _to_int(s): b = s.encode("ascii") return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3] -MAGIC = to_int("icns") +MAGIC = _to_int("icns") HEADER_SIZE = 8 TOC = "TOC " @@ -341,15 +341,15 @@ def _save(im, fp, filename): # TOC toc_size = HEADER_SIZE + (len(entries) * HEADER_SIZE) - fp.write(struct.pack(" Date: Tue, 7 Apr 2020 21:17:09 +1000 Subject: [PATCH 08/23] ICNS can now be saved on non-macOS platforms [ci skip] --- docs/handbook/image-file-formats.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index a12de82e2c0..7bb846b2a3e 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -200,12 +200,16 @@ attributes before loading the file:: ICNS ^^^^ -Pillow reads and (macOS only) writes macOS ``.icns`` files. By default, the +Pillow reads and writes macOS ``.icns`` files. By default, the largest available icon is read, though you can override this by setting the :py:attr:`~PIL.Image.Image.size` property before calling :py:meth:`~PIL.Image.Image.load`. The :py:meth:`~PIL.Image.Image.open` method sets the following :py:attr:`~PIL.Image.Image.info` property: +.. note:: + + Prior to version 7.2.0, Pillow could only write ICNS files on macOS. + **sizes** A list of supported sizes found in this icon file; these are a 3-tuple, ``(width, height, scale)``, where ``scale`` is 2 for a retina From 84b7e268f9646d4aac968f4fb921e84bd01ef432 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 7 Apr 2020 21:18:37 +1000 Subject: [PATCH 09/23] Merged HEADER_SIZE into HEADERSIZE --- src/PIL/IcnsImagePlugin.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index e3b274295fb..98603e2f967 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -306,7 +306,6 @@ def _to_int(s): MAGIC = _to_int("icns") -HEADER_SIZE = 8 TOC = "TOC " @@ -340,17 +339,17 @@ def _save(im, fp, filename): fp.write(struct.pack(" Date: Tue, 7 Apr 2020 21:42:23 +1000 Subject: [PATCH 10/23] Do not save two temporary files for the same size --- src/PIL/IcnsImagePlugin.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index 98603e2f967..cf9b5dcf5e8 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -325,10 +325,12 @@ def _save(im, fp, filename): file_size = 0 entries = [] provided_images = {im.width: im for im in im.encoderinfo.get("append_images", [])} - for index, s in enumerate(sizes): - temp = io.BytesIO() + temp_sizes = {s: io.BytesIO() for s in set(sizes)} + for s, temp in temp_sizes.items(): nb = provided_images[s] if s in provided_images else im.resize((s, s)) nb.save(temp, "png") + for index, s in enumerate(sizes): + temp = temp_sizes[s] file_size += len(temp.getvalue()) entries.append( {"type": size_str[index], "size": len(temp.getvalue()), "stream": temp} @@ -343,14 +345,14 @@ def _save(im, fp, filename): fp.write(struct.pack(" Date: Sun, 1 Nov 2020 14:02:15 +1100 Subject: [PATCH 11/23] Changed wording --- src/PIL/IcnsImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index cf9b5dcf5e8..d7aeb53582d 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -6,7 +6,7 @@ # # history: # 2004-10-09 fl Turned into a PIL plugin; removed 2.3 dependencies. -# 2020-04-04 Save in icns format to support all operating systems. +# 2020-04-04 Allow saving on all operating systems. # # Copyright (c) 2004 by Bob Ippolito. # Copyright (c) 2004 by Secret Labs. From 63e8420ef668022c1e259d736d376e68adcfeca8 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Sun, 1 Nov 2020 14:05:52 +1100 Subject: [PATCH 12/23] Removed docstring sentence Co-authored-by: Hugo van Kemenade --- src/PIL/IcnsImagePlugin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index d7aeb53582d..b5de09945ee 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -314,7 +314,6 @@ def _save(im, fp, filename): Saves the image as a series of PNG files, that are then converted to a .icns file - Support for arbitrary systems """ if hasattr(fp, "flush"): fp.flush() From d7245eae23f99f8883f1a7b7f642a080753dfc5e Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Sun, 1 Nov 2020 14:07:31 +1100 Subject: [PATCH 13/23] Capitalisation Co-authored-by: Hugo van Kemenade --- src/PIL/IcnsImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index b5de09945ee..481ef3ec313 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -318,7 +318,7 @@ def _save(im, fp, filename): if hasattr(fp, "flush"): fp.flush() - # size + # Size sizes = [128, 256, 512, 32, 64, 256, 512, 1024] size_str = ["ic07", "ic08", "ic09", "ic11", "ic12", "ic13", "ic14", "ic10"] file_size = 0 From 0ff800a52546a65d73db31af86bbd1a0ce87fc3e Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Wed, 30 Dec 2020 12:49:22 +1100 Subject: [PATCH 14/23] Updated docstring Co-authored-by: Hugo van Kemenade --- src/PIL/IcnsImagePlugin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index 481ef3ec313..abfb4a2f25b 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -313,7 +313,6 @@ def _save(im, fp, filename): """ Saves the image as a series of PNG files, that are then converted to a .icns file - """ if hasattr(fp, "flush"): fp.flush() From 800a265f1c11dd910c3f3e827474cf852e99bb3c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 30 Dec 2020 13:09:30 +1100 Subject: [PATCH 15/23] Test ICNS on all operating systems --- Tests/test_file_icns.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Tests/test_file_icns.py b/Tests/test_file_icns.py index e11eb996dd1..eb9448bfeee 100644 --- a/Tests/test_file_icns.py +++ b/Tests/test_file_icns.py @@ -53,7 +53,6 @@ def test_save_append_images(tmp_path): assert_image_equal(reread, provided_im) -@pytest.mark.skipif(sys.platform != "darwin", reason="Requires macOS") def test_save_fp(): fp = io.BytesIO() From 70fafe3da79ca91457be815d71d17c09d07b31da Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 28 Jun 2021 16:36:30 +0300 Subject: [PATCH 16/23] Update version number --- docs/handbook/image-file-formats.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 696e78ef3a8..9c5af7ac9bc 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -208,7 +208,7 @@ sets the following :py:attr:`~PIL.Image.Image.info` property: .. note:: - Prior to version 7.2.0, Pillow could only write ICNS files on macOS. + Prior to version 8.3.0, Pillow could only write ICNS files on macOS. **sizes** A list of supported sizes found in this icon file; these are a From 38d45d28483a9ae810e373bf41465ee9f73b5d2b Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 28 Jun 2021 17:48:06 +0300 Subject: [PATCH 17/23] flush if hasattr Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- src/PIL/IcnsImagePlugin.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index f227cebb1ab..930131a6885 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -352,7 +352,8 @@ def _save(im, fp, filename): fp.write(struct.pack(" Date: Tue, 29 Jun 2021 20:17:33 +1000 Subject: [PATCH 18/23] Use bytes --- src/PIL/IcnsImagePlugin.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index 930131a6885..0a02658f105 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -300,13 +300,12 @@ def load(self): self.load_end() -def _to_int(s): - b = s.encode("ascii") +def _to_int(b): return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3] -MAGIC = _to_int("icns") -TOC = "TOC " +MAGIC = b"icns" +TOC = b"TOC " def _save(im, fp, filename): @@ -319,7 +318,7 @@ def _save(im, fp, filename): # Size sizes = [128, 256, 512, 32, 64, 256, 512, 1024] - size_str = ["ic07", "ic08", "ic09", "ic11", "ic12", "ic13", "ic14", "ic10"] + size_str = [b"ic07", b"ic08", b"ic09", b"ic11", b"ic12", b"ic13", b"ic14", b"ic10"] file_size = 0 entries = [] provided_images = {im.width: im for im in im.encoderinfo.get("append_images", [])} @@ -331,11 +330,15 @@ def _save(im, fp, filename): temp = temp_sizes[s] file_size += len(temp.getvalue()) entries.append( - {"type": size_str[index], "size": len(temp.getvalue()), "stream": temp} + { + "type": _to_int(size_str[index]), + "size": len(temp.getvalue()), + "stream": temp, + } ) # Header - fp.write(struct.pack(" Date: Tue, 29 Jun 2021 20:18:55 +1000 Subject: [PATCH 19/23] Only getvalue() once per entry --- src/PIL/IcnsImagePlugin.py | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index 0a02658f105..2fec04683de 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -319,7 +319,6 @@ def _save(im, fp, filename): # Size sizes = [128, 256, 512, 32, 64, 256, 512, 1024] size_str = [b"ic07", b"ic08", b"ic09", b"ic11", b"ic12", b"ic13", b"ic14", b"ic10"] - file_size = 0 entries = [] provided_images = {im.width: im for im in im.encoderinfo.get("append_images", [])} temp_sizes = {s: io.BytesIO() for s in set(sizes)} @@ -328,14 +327,11 @@ def _save(im, fp, filename): nb.save(temp, "png") for index, s in enumerate(sizes): temp = temp_sizes[s] - file_size += len(temp.getvalue()) + stream = temp.getvalue() entries.append( - { - "type": _to_int(size_str[index]), - "size": len(temp.getvalue()), - "stream": temp, - } + {"type": _to_int(size_str[index]), "size": len(stream), "stream": stream} ) + file_size = sum(entry["size"] for entry in entries) # Header fp.write(struct.pack(" Date: Tue, 29 Jun 2021 20:20:34 +1000 Subject: [PATCH 20/23] Simplified use of struct --- src/PIL/IcnsImagePlugin.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index 2fec04683de..cc5b8affab2 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -331,24 +331,22 @@ def _save(im, fp, filename): entries.append( {"type": _to_int(size_str[index]), "size": len(stream), "stream": stream} ) - file_size = sum(entry["size"] for entry in entries) # Header - fp.write(struct.pack("i", _to_int(MAGIC))) + fp.write(struct.pack(">i", sum(entry["size"] for entry in entries))) # TOC - toc_size = HEADERSIZE + (len(entries) * HEADERSIZE) - fp.write(struct.pack("i", _to_int(TOC))) + fp.write(struct.pack(">i", HEADERSIZE + len(entries) * HEADERSIZE)) for entry in entries: - fp.write(struct.pack("i", entry["type"])) + fp.write(struct.pack(">i", HEADERSIZE + entry["size"])) # Data for entry in entries: - fp.write(struct.pack("i", entry["type"])) + fp.write(struct.pack(">i", HEADERSIZE + entry["size"])) fp.write(entry["stream"]) if hasattr(fp, "flush"): From f366330a748aef4ac31b4800ee994308cc8f62c3 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 29 Jun 2021 20:49:19 +1000 Subject: [PATCH 21/23] Only open one BytesIO instance at a time --- src/PIL/IcnsImagePlugin.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index cc5b8affab2..397de44fe23 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -319,15 +319,19 @@ def _save(im, fp, filename): # Size sizes = [128, 256, 512, 32, 64, 256, 512, 1024] size_str = [b"ic07", b"ic08", b"ic09", b"ic11", b"ic12", b"ic13", b"ic14", b"ic10"] - entries = [] + provided_images = {im.width: im for im in im.encoderinfo.get("append_images", [])} - temp_sizes = {s: io.BytesIO() for s in set(sizes)} - for s, temp in temp_sizes.items(): - nb = provided_images[s] if s in provided_images else im.resize((s, s)) - nb.save(temp, "png") - for index, s in enumerate(sizes): - temp = temp_sizes[s] - stream = temp.getvalue() + size_streams = {} + for s in set(sizes): + image = provided_images[s] if s in provided_images else im.resize((s, s)) + + temp = io.BytesIO() + image.save(temp, "png") + size_streams[s] = temp.getvalue() + + entries = [] + for index, size in enumerate(sizes): + stream = size_streams[size] entries.append( {"type": _to_int(size_str[index]), "size": len(stream), "stream": stream} ) From 8736a745f41410fb02dc6074ad1d964409bb78f0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 29 Jun 2021 21:08:26 +1000 Subject: [PATCH 22/23] Removed _to_int --- src/PIL/IcnsImagePlugin.py | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index 5442f6fc490..666fa1b1bb9 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -28,6 +28,7 @@ if enable_jpeg2k: from PIL import Jpeg2KImagePlugin +MAGIC = b"icns" HEADERSIZE = 8 @@ -165,7 +166,7 @@ def __init__(self, fobj): self.dct = dct = {} self.fobj = fobj sig, filesize = nextheader(fobj) - if sig != b"icns": + if sig != MAGIC: raise SyntaxError("not an icns file") i = HEADERSIZE while i < filesize: @@ -301,14 +302,6 @@ def load(self): self.load_end() -def _to_int(b): - return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3] - - -MAGIC = b"icns" -TOC = b"TOC " - - def _save(im, fp, filename): """ Saves the image as a series of PNG files, @@ -333,24 +326,22 @@ def _save(im, fp, filename): entries = [] for index, size in enumerate(sizes): stream = size_streams[size] - entries.append( - {"type": _to_int(size_str[index]), "size": len(stream), "stream": stream} - ) + entries.append({"type": size_str[index], "size": len(stream), "stream": stream}) # Header - fp.write(struct.pack(">i", _to_int(MAGIC))) + fp.write(MAGIC) fp.write(struct.pack(">i", sum(entry["size"] for entry in entries))) # TOC - fp.write(struct.pack(">i", _to_int(TOC))) + fp.write(b"TOC ") fp.write(struct.pack(">i", HEADERSIZE + len(entries) * HEADERSIZE)) for entry in entries: - fp.write(struct.pack(">i", entry["type"])) + fp.write(entry["type"]) fp.write(struct.pack(">i", HEADERSIZE + entry["size"])) # Data for entry in entries: - fp.write(struct.pack(">i", entry["type"])) + fp.write(entry["type"]) fp.write(struct.pack(">i", HEADERSIZE + entry["size"])) fp.write(entry["stream"]) From 43f5a5f80b9778a0913ed63403d1e03e76a194e4 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 30 Jun 2021 23:30:59 +1000 Subject: [PATCH 23/23] Combined sizes and types into dictionary --- src/PIL/IcnsImagePlugin.py | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index 666fa1b1bb9..d30eaf90f5c 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -6,7 +6,7 @@ # # history: # 2004-10-09 fl Turned into a PIL plugin; removed 2.3 dependencies. -# 2020-04-04 Allow saving on all operating systems. +# 2020-04-04 Allow saving on all operating systems. # # Copyright (c) 2004 by Bob Ippolito. # Copyright (c) 2004 by Secret Labs. @@ -131,6 +131,7 @@ def read_png_or_jpeg2000(fobj, start_length, size): class IcnsFile: + SIZES = { (512, 512, 2): [(b"ic10", read_png_or_jpeg2000)], (512, 512, 1): [(b"ic09", read_png_or_jpeg2000)], @@ -305,28 +306,38 @@ def load(self): def _save(im, fp, filename): """ Saves the image as a series of PNG files, - that are then converted to a .icns file + that are then combined into a .icns file. """ if hasattr(fp, "flush"): fp.flush() - # Size - sizes = [128, 256, 512, 32, 64, 256, 512, 1024] - size_str = [b"ic07", b"ic08", b"ic09", b"ic11", b"ic12", b"ic13", b"ic14", b"ic10"] - + sizes = { + b"ic07": 128, + b"ic08": 256, + b"ic09": 512, + b"ic10": 1024, + b"ic11": 32, + b"ic12": 64, + b"ic13": 256, + b"ic14": 512, + } provided_images = {im.width: im for im in im.encoderinfo.get("append_images", [])} size_streams = {} - for s in set(sizes): - image = provided_images[s] if s in provided_images else im.resize((s, s)) + for size in set(sizes.values()): + image = ( + provided_images[size] + if size in provided_images + else im.resize((size, size)) + ) temp = io.BytesIO() image.save(temp, "png") - size_streams[s] = temp.getvalue() + size_streams[size] = temp.getvalue() entries = [] - for index, size in enumerate(sizes): + for type, size in sizes.items(): stream = size_streams[size] - entries.append({"type": size_str[index], "size": len(stream), "stream": stream}) + entries.append({"type": type, "size": len(stream), "stream": stream}) # Header fp.write(MAGIC)