Converting Graphics for App and Play Stores Automatically

by | Nov 4, 2015 | Programming

image sizesIf you’ve done any cross-development projects, and had to publish to both Google Play and Apple App stores, you’ll have run into the issue of needing to convert every icon and splash screen into something like 371 different stupid little formats and orientations (don’t even start me on Amazon’s app store, or the half dozen others out there!).  OK, so maybe not quite 371, but still, more than a few.  Too many.  And if you have to do it more than once in the same week, you’ll be driven to insanity … or, perhaps, like me, Python scripts!

To set the stage, I am running Linux Mate (a great flavor of Linux IMHO), with a configuration that includes the standard – and wonderful – ImageMagick GUI / CLI application. ImageMagick has the command line options that I reference here, but you could use your image manipulation software of choice, so long as you are familiar with the CLI.  Now that’s settled, let’s get on with it, shall we?

Step 1: App and Play Store Image Sizes

As I mentioned, there’s a metric tonne of image sizes to resolve – both icon and splash screens require multiple variations and alignments, and it can be a real pain to set them all up manually. At a later date, I’ll talk about including splash files, but that is a non-trivial addition to this code so for simplicity’s sake I’m doing icon files only here.  The image file sizes required by these outlets are:

Android Phone iOS / iPhone Amazon Store
36×36 60×60 114×114
48×48 76×76 512×512
72×72 120×120
96×96 152×152
 180×180

Gotta love how not even one single icon size matches up, right?  At least they’re all square … and now that we have that we can start jumping into code script.

Step 2: Open Your Editor, Start Declaring

First things first, get your header and declarations going inside your Python editor of choice.  I generally use JetBrain’s PyCharm community edition (there’s a wonderful tutorial on it you can find here). I typically don’t need all the project settings options and all, but I’ve become comfortable with the layout and plugin availability. As with anything, YMMV so I’ll just say pick one you like and stick with it.

__author__ = '43Folders Technology Solutions'

#  Coded by Michael Kastler
# ---- 43FoldersTech.net ----
#  [email protected]
#      (c) 2015-09-30
#
# Purpose is to take an icon image - preferably at least 1024X1024 - and resize to all
# required icon sizes for both android and ios app development
#

import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox
from subprocess import call
import string

imageList = [".png", ".jpg", ".gif", ".bmp", ".jpeg"]
iOSIcons = ["180x180", "120x120", "60x60", "152x152", "76x76"]
andIcons = ["36x36", "48x48", "72x72", "96x96"]

sourceFile = ""  # this is the basic file input by user
sourceExt = "" # this will be the resolved extension of the primary file

Here we’ve got our header, and then our import statements.  You can see I’m using tkinter for the interface, so that I can get the file locations for processing user input, give pretty feedback and so on.  I’m also adding some basic variable declarations such as sourceFile and sourceExt.

You’ll notice that imageList, iOSicons, and andIcons are all declared as arrays so that we can iterate the values later.  That makes it easy to add things like amazonIcons or whatever.

Step 2: Interface for Files

Now we’re ready to get a file and start working it – but also need to make sure that you get a valid image file.  I approached it as simply as possible, asking for a file and then checking the extension against a predetermined list of file types that I know ImageMagick will work with.  Not robust, but generally functional.  I also need the file name and extension separated for use later so I can save the new icon files without overwriting the original.

sourceFile = filedialog.askopenfilename(title='Select square icon file to convert:', initialdir='~/Pictures')
sourceExt = sourceFile[-4:]
sourceFile = sourceFile[:-4]

if sourceExt.lower() not in imageList:
    messagebox.showerror("NOT AN IMAGE FILE", "That file cannot be converted, please choose an appropriate image")
    quit
else:

If the file selected is an image file (at least as far as we test for), then we move on to:

Step 3: Figure Out the Command Line

Before we start creating our loops, we need to get a command line that will work for us, and do the things we need it to.  ImageMagick has a great page with all the command line options defined, linked here.

All we really need to do here is resize, keeping aspect ratios the same, which is a pretty simple operation. If I’m converting a file called “icon.jpg” from whatever size it is to 36×36, my command line will look like this:

convert icon.jpg -resize 36x36 icon36x36.jpg

So that command line includes calling ImageMagick with convert, identifying our source and output file names, and then the option -resize combined with the size of the resulting file.  Really, what it comes down to is this:

convert SOURCEFILE -resize NEWSIZE OUTPUT

You may notice that the size is required quite conveniently in the same way we stored values in the iOSIcons and andIcon arrays, so we’re ready now for …

Step 4: The Loop-de-Loop

We’re nearly done!  Now that we have a file to work on, know how our command should be structured, and arrays of all the sizes, we’ll just iterate through them in the command and cross our fingers.

    for newsize in iosIcon:
        call(["convert", sourceFile + sourceExt, "-resize", newsize, sourceFile + newsize + sourceExt])

    for newsize in andIcon:
        call(["convert", sourceFile + sourceExt, "-resize", newsize, sourceFile + newsize + sourceExt])

You’ll notice that we use the call function from the subprocess library here. That allows us to send the command to the terminal from within the script. call requires options entered as an object/array, so you’ll see our commend is set up that way, brackets and everything.

An artifact of that “options as array” thing is that you’ll see each part of of the call separated by a comma, but no extra space needed.  If this were set up like a string command, you’d need to remember the spaces, quotes, brackets or whatever else. The call command kind of smart like that, but getting used to the syntax can cause some struggle.

Step 5: We’re Done!

Now that everything has run, and no errors killed our lamentably totally not robust code, we need to let the user know with a big tkinter thumbs up:

messagebox.showinfo("Process Complete", "Congratulations, all your icons belong to me!")

That really is all there is to it here …  Next steps might include error checking, adding the option to process splash screens, or letting the user save their starting directory, but that’s a blog post for another day.

Complete code and a hat

Below you’ll find the complete code for easy copy/paste.  Take it, copy it, use it, abuse it, whatever … but if you do find value here, I’d love it if you bought me my next cup of coffee?  You can throw whatever random amount you want to [email protected] – or use this button    We’re always  up for some custom code, app, or web design engagements too of course … Cheers either way!

__author__ = '43Folders Technology Solutions'

#  Coded by Michael Kastler
# ---- 43FoldersTech.net ----
#  [email protected]
#      (c) 2015-09-30
#
# Purpose is to take an icon image - preferably at least 1024X1024 - and resize to all
# required icon sizes for both android and ios app development
#

import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox
from subprocess import call
import string

imageList = [".png", ".jpg", ".gif", ".bmp", ".jpeg"]
iOSIcons = ["180x180", "120x120", "60x60", "152x152", "76x76"]
andIcons = ["36x36", "48x48", "72x72", "96x96"]

sourceFile = ""  # this is the basic file input by user
sourceExt = "" # this will be the resolved extension of the primary file

sourceFile = filedialog.askopenfilename(title='Select square icon file to convert:', initialdir='~/Pictures')
sourceExt = sourceFile[-4:]
sourceFile = sourceFile[:-4]

if sourceExt.lower() not in imageList:
    messagebox.showerror("NOT AN IMAGE FILE", "That file cannot be converted, please choose an appropriate image")
    quit
else:
    for newsize in iosIcon:
        call(["convert", sourceFile + sourceExt, "-resize", newsize, sourceFile + newsize + sourceExt])

    for newsize in andIcon:
        call(["convert", sourceFile + sourceExt, "-resize", newsize, sourceFile + newsize + sourceExt])

messagebox.showinfo("Process Complete", "Congratulations, all your icons belong to me!")

Initially, I had the call options line defined separately from the actual usage of it, but that would only have been really useful if I were to do error checking or other processing (you know, like real programmers would ::ha ha::).

0 Comments