ICNS save

This commit is contained in:
Herb 2015-04-23 11:22:55 +08:00
parent d46441b789
commit 2d53be5a47
2 changed files with 78 additions and 1 deletions

View File

@ -17,7 +17,17 @@
from PIL import Image, ImageFile, PngImagePlugin, _binary
import io
import sys
import os
import struct
import tempfile
import subprocess
import shutil
try:
import warnings
except ImportError:
warnings = None
enable_jpeg2k = hasattr(Image.core, 'jp2klib_version')
if enable_jpeg2k:
@ -28,6 +38,49 @@ i8 = _binary.i8
HEADERSIZE = 8
def _save(im, fp, filename):
if sys.platform == 'darwin':
tmp_icons_dir = tempfile.mkdtemp(suffix='.iconset')
tmp_targe_handle, tmp_targe_path = tempfile.mkstemp(suffix='.icns')
tmp_targe_file = None
os.close(tmp_targe_handle)
try:
sizes = im.encoderinfo.get("sizes",
[(16, 16, 1), (16, 16, 2),
(32, 32, 1), (32, 32, 2),
(128, 128, 1), (128, 128, 2),
(256, 256, 1), (256, 256, 2),
(512, 512, 1), (512, 512, 2)])
for size in sizes:
width, height, scale = size
size_name = '%sx%s%s' % (width, height,
'' if scale == 1 else
('@%sx' % scale))
if im.size[0] < width*scale or im.size[1] < height*scale:
if warnings:
warnings.warn(('Skip %s because original image ' +
'size(%sx%s) is too small') %
(size_name, im.size[0], im.size[1]))
continue
png_path = os.path.join(tmp_icons_dir,
'icon_%s.png' % size_name)
with open(png_path, 'wb') as png_file:
tmp = im.copy()
tmp.thumbnail((width*scale, height*scale), Image.LANCZOS)
tmp.save(png_file, "png")
subprocess.call(['iconutil', '--convert', 'icns', '--output',
tmp_targe_path, tmp_icons_dir])
with open(tmp_targe_path, 'rb') as tmp_targe_file:
fp.write(tmp_targe_file.read())
finally:
if tmp_targe_file:
tmp_targe_file.close()
os.remove(tmp_targe_path)
shutil.rmtree(tmp_icons_dir)
else:
raise RuntimeError("Unsupported in non-darwin platform")
def nextheader(fobj):
return struct.unpack('>4sI', fobj.read(HEADERSIZE))
@ -294,8 +347,10 @@ class IcnsImageFile(ImageFile.ImageFile):
self.load_end()
Image.register_open("ICNS", IcnsImageFile, lambda x: x[:4] == b'icns')
Image.register_save("ICNS", _save)
Image.register_extension("ICNS", '.icns')
if __name__ == '__main__':
import os
import sys

View File

@ -1,5 +1,6 @@
from helper import unittest, PillowTestCase
from helper import unittest, PillowTestCase, hopper
import io
from PIL import Image
# sample icon file
@ -67,6 +68,27 @@ class TestFileIcns(PillowTestCase):
self.assertEqual(im2.mode, 'RGBA')
self.assertEqual(im2.size, (wr, hr))
def test_save_to_bytes(self):
output = io.BytesIO()
im = hopper()
sizes = [(16, 16, 1), (16, 16, 2),
(32, 32, 1), (32, 32, 2),
(128, 128, 1)]
im.save(output, "icns", sizes=sizes)
output.seek(0)
reloaded = Image.open(output)
self.assertEqual(reloaded.mode, "RGBA")
self.assertEqual(reloaded.format, "ICNS")
self.assertEqual(set(reloaded.info['sizes']), set(sizes))
for w, h, r in reloaded.info['sizes']:
wr = w * r
hr = h * r
im2 = Image.open(file)
im2.size = (w, h, r)
im2.load()
self.assertEqual(im2.mode, 'RGBA')
self.assertEqual(im2.size, (wr, hr))
if __name__ == '__main__':
unittest.main()