Created
December 10, 2013 08:22
-
-
Save shnjp/7887290 to your computer and use it in GitHub Desktop.
iOS用の画像がどかっと渡された時に、いっぱつでImage.xcassetsを作ってくれるスクリプト
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/local/bin/python3 | |
# -*- coding:utf-8 -*- | |
""" | |
iOS用の画像がどかっと渡された時に、いっぱつでImage.xcassetsを作ってくれるスクリプ | |
python3 yorosikuimage [画像のディレクトリ] [Image.xcassetsのパス] | |
""" | |
from PIL import Image | |
import os | |
import json | |
import shutil | |
from collections import namedtuple | |
IconSpec = namedtuple('IconSpec', 'size scale idiom') | |
APPICON_SPECS = [ | |
IconSpec((60, 60), '2x', 'iphone'), | |
IconSpec((57, 57), '1x', 'iphone'), | |
IconSpec((57, 57), '2x', 'iphone'), | |
IconSpec((72, 72), '1x', 'ipad'), | |
IconSpec((72, 72), '2x', 'ipad'), | |
IconSpec((76, 76), '1x', 'ipad'), | |
IconSpec((76, 76), '2x', 'ipad'), | |
IconSpec((29, 29), '1x', 'iphone'), | |
IconSpec((29, 29), '2x', 'iphone'), | |
IconSpec((40, 40), '2x', 'iphone'), | |
IconSpec((50, 50), '1x', 'ipad'), | |
IconSpec((50, 50), '2x', 'ipad'), | |
IconSpec((40, 40), '1x', 'ipad'), | |
IconSpec((40, 40), '2x', 'ipad'), | |
IconSpec((29, 29), '1x', 'ipad'), | |
IconSpec((29, 29), '2x', 'ipad'), | |
] | |
LaunchImageSpec = namedtuple('LaunchImageSpec', 'size scale orientation subtype minimum_system_version idiom extent') | |
LAUNCHIMAGE_SPECS = [ | |
LaunchImageSpec((320, 480), '1x', 'portrait', None, None, 'iphone', None), | |
LaunchImageSpec((640, 960), '2x', 'portrait', None, None, 'iphone', None), | |
LaunchImageSpec((640, 1136), '2x', 'portrait', 'retina4', None, 'iphone', None), | |
LaunchImageSpec((640, 960), '2x', 'portrait', None, '7.0', 'iphone', None), | |
LaunchImageSpec((640, 1136), '2x', 'portrait', 'retina4', '7.0', 'iphone', None), | |
LaunchImageSpec((768, 1004), '1x', 'portrait', None, None, 'ipad', 'to-status-bar'), | |
LaunchImageSpec((1536, 2008), '2x', 'portrait', None, None, 'ipad', 'to-status-bar'), | |
LaunchImageSpec((1024, 748), '1x', 'landscape', None, None, 'ipad', 'to-status-bar'), | |
LaunchImageSpec((2048, 1496), '2x', 'landscape', None, None, 'ipad', 'to-status-bar'), | |
LaunchImageSpec((768, 1024), '1x', 'portrait', None, '7.0', 'ipad', 'full-screen'), | |
LaunchImageSpec((1536, 2048), '2x', 'portrait', None, '7.0', 'ipad', 'full-screen'), | |
LaunchImageSpec((1024, 768), '1x', 'landscape', None, '7.0', 'ipad', 'full-screen'), | |
LaunchImageSpec((2048, 1536), '2x', 'landscape', None, '7.0', 'ipad', 'full-screen'), | |
] | |
def main(): | |
import argparse | |
parser = argparse.ArgumentParser() | |
parser.add_argument('from_dir', metavar='FROM_DIR') | |
parser.add_argument('output_path', metavar='OUTPUT_PATH') | |
args = parser.parse_args() | |
images = enum_images(args.from_dir) | |
# ベタ書き | |
output_path = args.output_path | |
if not output_path.endswith('.xcassets'): | |
output_path = output_path + '.xcassets' | |
if not os.path.isdir(output_path): | |
os.makedirs(output_path) | |
make_app_icon_asset(output_path, 'AppIcon', images) | |
make_launch_image_asset(output_path, 'LaunchImage', images) | |
def enum_images(from_dir): | |
images = {} | |
for path, dirs, files in os.walk(from_dir): | |
for filename in files: | |
ext = os.path.splitext(filename)[1] | |
if ext not in ('.jpg', '.jpeg', '.png'): | |
continue | |
filepath = os.path.join(path, filename) | |
im = Image.open(filepath) | |
images[im.size] = filepath | |
return images | |
def make_app_icon_asset(assets_path, setname, images): | |
set_path = os.path.join(assets_path, '{}.appiconset'.format(setname)) | |
if os.path.exists(set_path): | |
shutil.rmtree(set_path) | |
os.makedirs(set_path) | |
# find source image | |
try: | |
source_path = images[(2048, 2048)] | |
except: | |
print('iTunes size AppIcon not found') | |
source_im = Image.open(source_path) | |
images = [] | |
for spec in APPICON_SPECS: | |
assert(spec.size[0] == spec.size[1]) | |
assert(spec.scale in ('1x', '2x')) | |
size = spec.size[0] * (2 if spec.scale == '2x' else 1) | |
# save resized image | |
resized = source_im.resize((size, size)) | |
icon_file_name = '{}_{}'.format(setname, spec.size[0]) | |
if spec.scale != '1x': | |
icon_file_name += '@' + spec.scale | |
icon_file_name += '.png' | |
resized.save(os.path.join(set_path, icon_file_name), 'PNG') | |
# add icon to images | |
images.append({ | |
'idiom': spec.idiom, | |
'scale': spec.scale, | |
'size': '{}x{}'.format(*spec.size), | |
'filename': icon_file_name | |
}) | |
contents = { | |
'images': images, | |
'info' : { | |
'version' : 1, | |
'author' : 'xcode' | |
} | |
} | |
with open(os.path.join(set_path, 'Contents.json'), 'w', encoding='utf8') as fp: | |
json.dump(contents, fp, sort_keys=True, indent=2) | |
def make_launch_image_asset(assets_path, setname, images): | |
set_path = os.path.join(assets_path, '{}.launchimage'.format(setname)) | |
if os.path.exists(set_path): | |
shutil.rmtree(set_path) | |
os.makedirs(set_path) | |
contents_images = [] | |
for spec in LAUNCHIMAGE_SPECS: | |
assert(spec.scale in ('1x', '2x')) | |
d = { | |
'idiom': spec.idiom, | |
'scale': spec.scale, | |
'orientation': spec.orientation, | |
} | |
if spec.subtype: | |
d['subtype'] = spec.subtype | |
if spec.extent: | |
d['extent'] = spec.extent | |
if spec.minimum_system_version: | |
d['minimum-system-version'] = spec.minimum_system_version | |
if spec.size in images: | |
subtype = '' | |
suffix = '' | |
if spec.idiom == 'iphone' and spec.subtype == 'retina4': | |
subtype = '-568h' | |
elif spec.idiom == 'ipad': | |
if spec.orientation == 'portrait': | |
subtype = '-Portrait' | |
else: | |
subtype = '-Landscape' | |
if spec.extent == 'to-status-bar': | |
suffix = '_tsb' | |
image_file_name = 'Default{suffix}{subtype}{scale}~{device}.png'.format( | |
subtype=subtype, | |
scale=('' if spec.scale == '1x' else '@' + spec.scale), | |
device=spec.idiom, | |
suffix=suffix | |
) | |
d['filename'] = image_file_name | |
dst_path = os.path.join(set_path, image_file_name) | |
shutil.copyfile(images[spec.size], dst_path) | |
contents_images.append(d) | |
contents = { | |
'images': contents_images, | |
'info' : { | |
'version' : 1, | |
'author' : 'xcode' | |
} | |
} | |
with open(os.path.join(set_path, 'Contents.json'), 'w', encoding='utf8') as fp: | |
json.dump(contents, fp, sort_keys=True, indent=2) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment