""" Icon and LaunchImage generator for iOS ====================================== .. author:: Mathieu Virbel """ __all__ = ["launchimage"] import sh import json from os.path import join, exists from os import makedirs appicon_json = { "images" : [ { "size" : "29x29", "idiom" : "iphone", "filename" : "Icon29.png", "scale" : "1x" }, { "size" : "29x29", "idiom" : "iphone", "filename" : "Icon58.png", "scale" : "2x" }, { "size" : "29x29", "idiom" : "iphone", "filename" : "Icon87.png", "scale" : "3x" }, { "size" : "40x40", "idiom" : "iphone", "filename" : "Icon80.png", "scale" : "2x" }, { "size" : "40x40", "idiom" : "iphone", "filename" : "Icon120.png", "scale" : "3x" }, { "size" : "57x57", "idiom" : "iphone", "filename" : "Icon57.png", "scale" : "1x" }, { "size" : "57x57", "idiom" : "iphone", "filename" : "Icon114.png", "scale" : "2x" }, { "size" : "60x60", "idiom" : "iphone", "filename" : "Icon120.png", "scale" : "2x" }, { "size" : "60x60", "idiom" : "iphone", "filename" : "Icon180.png", "scale" : "3x" }, { "size" : "29x29", "idiom" : "ipad", "filename" : "Icon29.png", "scale" : "1x" }, { "size" : "29x29", "idiom" : "ipad", "filename" : "Icon58.png", "scale" : "2x" }, { "size" : "40x40", "idiom" : "ipad", "filename" : "Icon40.png", "scale" : "1x" }, { "size" : "40x40", "idiom" : "ipad", "filename" : "Icon80.png", "scale" : "2x" }, { "size" : "50x50", "idiom" : "ipad", "filename" : "Icon50.png", "scale" : "1x" }, { "size" : "50x50", "idiom" : "ipad", "filename" : "Icon100.png", "scale" : "2x" }, { "size" : "72x72", "idiom" : "ipad", "filename" : "Icon72.png", "scale" : "1x" }, { "size" : "72x72", "idiom" : "ipad", "filename" : "Icon144.png", "scale" : "2x" }, { "size" : "76x76", "idiom" : "ipad", "filename" : "Icon76.png", "scale" : "1x" }, { "size" : "76x76", "idiom" : "ipad", "filename" : "Icon152.png", "scale" : "2x" }, # If activated, we got a submission error: # "Error ITMS-9000: Invalid Image Path - No image found at the path # referenced under key 'CFBundleIcons': 'AppIcon120x120'" # { # "size" : "120x120", # "idiom" : "car", # "filename" : "Icon120.png", # "scale" : "1x" # }, { "size" : "24x24", "idiom" : "watch", "scale" : "2x", "filename" : "Icon48.png", "role" : "notificationCenter", "subtype" : "38mm" }, { "size" : "27.5x27.5", "idiom" : "watch", "scale" : "2x", "filename" : "Icon55.png", "role" : "notificationCenter", "subtype" : "42mm" }, { "size" : "29x29", "idiom" : "watch", "filename" : "Icon58.png", "role" : "companionSettings", "scale" : "2x" }, { "size" : "29x29", "idiom" : "watch", "filename" : "Icon87.png", "role" : "companionSettings", "scale" : "3x" }, { "size" : "40x40", "idiom" : "watch", "scale" : "2x", "filename" : "Icon80.png", "role" : "appLauncher", "subtype" : "38mm" }, { "size" : "44x44", "idiom" : "watch", "scale" : "2x", "filename" : "Icon88.png", "role" : "longLook", "subtype" : "42mm" }, { "size" : "86x86", "idiom" : "watch", "scale" : "2x", "filename" : "Icon172.png", "role" : "quickLook", "subtype" : "38mm" }, { "size" : "98x98", "idiom" : "watch", "scale" : "2x", "filename" : "Icon196.png", "role" : "quickLook", "subtype" : "42mm" }, { "size" : "16x16", "idiom" : "mac", "filename" : "Icon16.png", "scale" : "1x" }, { "size" : "16x16", "idiom" : "mac", "filename" : "Icon32.png", "scale" : "2x" }, { "size" : "32x32", "idiom" : "mac", "filename" : "Icon32.png", "scale" : "1x" }, { "size" : "32x32", "idiom" : "mac", "filename" : "Icon64.png", "scale" : "2x" }, { "size" : "128x128", "idiom" : "mac", "filename" : "Icon128.png", "scale" : "1x" }, { "size" : "128x128", "idiom" : "mac", "filename" : "Icon256.png", "scale" : "2x" }, { "size" : "256x256", "idiom" : "mac", "filename" : "Icon256.png", "scale" : "1x" }, { "size" : "256x256", "idiom" : "mac", "filename" : "Icon512.png", "scale" : "2x" }, { "size" : "512x512", "idiom" : "mac", "filename" : "Icon512.png", "scale" : "1x" }, { "size" : "512x512", "idiom" : "mac", "filename" : "Icon1024.png", "scale" : "2x" } ], "info" : { "version" : 1, "author" : "xcode" }, # "properties" : { # "pre-rendered" : True # } } launchimage_json = { "images" : [ { "extent" : "full-screen", "idiom" : "iphone", "subtype" : "736h", "filename" : "Default1242x2208.png", "minimum-system-version" : "8.0", "orientation" : "portrait", "scale" : "3x" }, { "extent" : "full-screen", "idiom" : "iphone", "subtype" : "736h", "filename" : "Default2208x1242.png", "minimum-system-version" : "8.0", "orientation" : "landscape", "scale" : "3x" }, { "extent" : "full-screen", "idiom" : "iphone", "subtype" : "667h", "filename" : "Default750x1334.png", "minimum-system-version" : "8.0", "orientation" : "portrait", "scale" : "2x" }, { "orientation" : "portrait", "idiom" : "iphone", "extent" : "full-screen", "minimum-system-version" : "7.0", "filename" : "Default640x960.png", "scale" : "2x" }, { "extent" : "full-screen", "idiom" : "iphone", "subtype" : "retina4", "filename" : "Default640x1136.png", "minimum-system-version" : "7.0", "orientation" : "portrait", "scale" : "2x" }, { "orientation" : "portrait", "idiom" : "ipad", "extent" : "full-screen", "minimum-system-version" : "7.0", "filename" : "Default768x1024.png", "scale" : "1x" }, { "orientation" : "landscape", "idiom" : "ipad", "extent" : "full-screen", "minimum-system-version" : "7.0", "filename" : "Default1024x768.png", "scale" : "1x" }, { "orientation" : "portrait", "idiom" : "ipad", "extent" : "full-screen", "minimum-system-version" : "7.0", "filename" : "Default1536x2048.png", "scale" : "2x" }, { "orientation" : "landscape", "idiom" : "ipad", "extent" : "full-screen", "minimum-system-version" : "7.0", "filename" : "Default2048x1536.png", "scale" : "2x" }, { "orientation" : "portrait", "idiom" : "iphone", "extent" : "full-screen", "filename" : "Default320x480.png", "scale" : "1x" }, { "orientation" : "portrait", "idiom" : "iphone", "extent" : "full-screen", "filename" : "Default640x960.png", "scale" : "2x" }, { "orientation" : "portrait", "idiom" : "iphone", "extent" : "full-screen", "filename" : "Default640x1136.png", "subtype" : "retina4", "scale" : "2x" }, { "orientation" : "portrait", "idiom" : "ipad", "extent" : "full-screen", "filename" : "Default768x1024.png", "scale" : "1x" }, { "orientation" : "landscape", "idiom" : "ipad", "extent" : "full-screen", "filename" : "Default1024x768.png", "scale" : "1x" }, { "orientation" : "portrait", "idiom" : "ipad", "extent" : "full-screen", "filename" : "Default1536x2048.png", "scale" : "2x" }, { "orientation" : "landscape", "idiom" : "ipad", "extent" : "full-screen", "filename" : "Default2048x1536.png", "scale" : "2x" } ], "info" : { "version" : 1, "author" : "xcode" } } def icon(image_xcassets, image_fn): """Generate all the possible Icon from a single image_fn """ appicon_dir = join(image_xcassets, "AppIcon.appiconset") if not exists(appicon_dir): makedirs(appicon_dir) with open(join(appicon_dir, "Contents.json"), "w") as fd: json.dump(appicon_json, fd) options = ( # iPhone # Spotlight - iOS 5,6 # Settings - iOS 5-8 # 29pt - 1x,2x,3x ("87", None, "Icon87.png"), ("58", None, "Icon58.png"), ("29", "Icon58.png", "Icon29.png"), # iPhone # Spotlight - iOS 7-8 # 40pt 2x,3x ("120", None, "Icon120.png"), ("80", None, "Icon80.png"), # iPhone # App - iOS 5,6 # 57pt 1x,2x ("114", None, "Icon114.png"), ("57", "Icon114.png", "Icon57.png"), # iPhone # App - iOS 7,8 # 60pt 2x,3x ("180", None, "Icon180.png"), #("120", None, "Icon120.png # duplicate"), # iPad # Settings iOS 5-8 #("58", None, "Icon58.png # duplicate"), #("29", "Icon58.png", "Icon29.png # duplicate"), # iPad # Spotlight iOS 7,8 # 40pt 1x,2x #("80", None, "Icon80.png # duplicate"), ("40", "Icon80.png", "Icon40.png"), # iPad # Spotlight iOS 5,6 # 50pt 1x,2x ("100", None, "Icon100.png"), ("50", "Icon100.png", "Icon50.png"), # iPad # App iOS 5,6 # 72pt 1x,2x ("144", None, "Icon144.png"), ("72", "Icon144.png", "Icon72.png"), # iPad # App iOS 7,8 # 76pt 1x,2x ("152", None, "Icon152.png"), ("76", "Icon152.png", "Icon76.png"), # CarPlay # App iOS 8 # 120pt 1x #("120", None, "Icon120.png # duplicate"), # Apple Watch # Notification Center # 38mm, 42mm ("48", None, "Icon48.png"), ("55", None, "Icon55.png"), # Apple Watch # Companion Settings # 29pt 2x,3x #("58", None, "Icon58.png # duplicate"), #("87", None, "Icon87.png # duplicate"), # Apple Watch # Home Screen (All) # Long Look (38mm) #("80", None, "Icon80.png # duplicate"), # Apple Watch # Long Look (42mm) ("88", None, "Icon88.png"), # Apple Watch # Short Look # 38mm, 42mm ("172", None, "Icon172.png"), ("196", None, "Icon196.png"), # OS X # 512pt 1x,2x ("1024", None, "Icon1024.png"), ("512", "Icon1024.png", "Icon512.png"), # OS X # 256pt 1x,2x #("512", "Icon1024.png", "Icon512.png # duplicate"), ("256", "Icon512.png", "Icon256.png"), # OS X # 128pt 1x,2x #("256", "Icon512.png", "Icon256.png # duplicate"), ("128", "Icon256.png", "Icon128.png"), # OS X # 32pt 1x,2x ("64", "Icon128.png", "Icon64.png"), ("32", "Icon64.png", "Icon32.png"), # OS X # 16pt 1x,2x #("32", "Icon64.png", "Icon32.png # duplicate"), ("16", "Icon32.png", "Icon16.png")) _generate("AppIcon.appiconset", image_xcassets, image_fn, options, icon=True) def launchimage(image_xcassets, image_fn): """Generate all the possible Launch Images from a single image_fn """ launchimage_dir = join(image_xcassets, "LaunchImage.launchimage") if not exists(launchimage_dir): makedirs(launchimage_dir) with open(join(launchimage_dir, "Contents.json"), "w") as fd: json.dump(launchimage_json, fd) options = ( # size, input, output # iPhone 3.5" @2x ("640 960", None, "Default640x960.png"), # iPhone 3.5" @1x ("320 480", "Default640x960.png", "Default320x480.png"), # iPhone 4.0" @2x ("640 1136", None, "Default640x1136.png"), # iPhone 5.5" @3x - landscape ("2208 1242", None, "Default2208x1242.png"), # iPhone 5.5" @3x - portrait ("1242 2208", "Default2208x1242.png", "Default1242x2208.png"), # iPhone 4.7" @2x ("750 1334", None, "Default750x1334.png"), # iPad @2x - landscape ("2048 1536", None, "Default2048x1536.png"), # iPad @2x - portrait ("1536 2048", "Default2048x1536.png", "Default1536x2048.png"), # iPad @1x - landscape ("1024 768", "Default2048x1536.png", "Default1024x768.png"), # iPad @1x - portrait ("768 1024", "Default1024x768.png", "Default768x1024.png"), ) _generate("LaunchImage.launchimage", image_xcassets, image_fn, options) def _generate(d, image_xcassets, image_fn, options, icon=False): for c, in_fn, out_fn in options: args = [] if icon: args += ["-Z", c] else: # ensure one side will not be bigger than the other (ie, the image will # fit to the screen) args += ["-Z", str(min(map(int, c.split())))] # if there is any left pixel, cover in black. args += ["-p"] + c.split() # and crop the image in necessary. args += ["-c"] + c.split()[::-1] if in_fn is not None: args += [join(image_xcassets, d, in_fn)] else: args += [image_fn] args += [ "--out", join(image_xcassets, d, out_fn) ] print "sips", " ".join(args) sh.sips(*args)