Skip to content

Instantly share code, notes, and snippets.

@LinusU
Forked from apla/icons_and_splash.js
Last active July 17, 2021 08:06
  • Star 80 You must be signed in to star a gist
  • Fork 11 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save LinusU/7515016 to your computer and use it in GitHub Desktop.
Icons and Splash images for your Cordova project. (with iOS 7 support)

Usage

Install cordova into node_modules

npm install cordova

Add icons_and_splash.js

Copy icons_and_splash.js into the .cordova/hooks/after_prepare directory.

It also needs to be executable: chmod +x .cordova/hooks/after_prepare/icons_and_splash.js

Add this to your config.xml

<icon src="res/icons/" />

Add these files to your www directory

www/res
`-- icons
    |-- android
    |   |-- icon-36-ldpi.png
    |   |-- icon-48-mdpi.png
    |   |-- icon-72-hdpi.png
    |   `-- icon-96-xhdpi.png
    `-- ios
        |-- icon-29-2x.png
        |-- icon-29.png
        |-- icon-40-2x.png
        |-- icon-40.png
        |-- icon-50-2x.png
        |-- icon-50.png
        |-- icon-57-2x.png
        |-- icon-57.png
        |-- icon-60-2x.png
        |-- icon-60.png
        |-- icon-72-2x.png
        |-- icon-72.png
        |-- icon-76-2x.png
        `-- icon-76.png

Report any error

If you get any error, please report error in comments here. I will then try to fix and update this gist accordingly. Please help us take away the pain in adding a custom icon to our applications.

#
# (Windows)
#
# What is this?
#
# It's a shell script that is using ImageMagick to create all the icon files from one source icon.
#
# If you'd like to use it on windows install ImageMagick:
# http://www.imagemagick.org/script/binary-releases.php#windows
#
# Stick the script in your 'www/res/icons' folder with your source icon 'my-hires-icon.png' then trigger it from Command Prompt.
#
mkdir android
convert my-hires-icon.png -resize 36x36 android/icon-36-ldpi.png
convert my-hires-icon.png -resize 48x48 android/icon-48-mdpi.png
convert my-hires-icon.png -resize 72x72 android/icon-72-hdpi.png
convert my-hires-icon.png -resize 96x96 android/icon-96-xhdpi.png
mkdir ios
convert my-hires-icon.png -resize 29 ios/icon-29.png
convert my-hires-icon.png -resize 40 ios/icon-40.png
convert my-hires-icon.png -resize 50 ios/icon-50.png
convert my-hires-icon.png -resize 57 ios/icon-57.png
convert my-hires-icon.png -resize 58 ios/icon-29-2x.png
convert my-hires-icon.png -resize 60 ios/icon-60.png
convert my-hires-icon.png -resize 72 ios/icon-72.png
convert my-hires-icon.png -resize 76 ios/icon-76.png
convert my-hires-icon.png -resize 80 ios/icon-40-2x.png
convert my-hires-icon.png -resize 100 ios/icon-50-2x.png
convert my-hires-icon.png -resize 114 ios/icon-57-2x.png
convert my-hires-icon.png -resize 120 ios/icon-60-2x.png
convert my-hires-icon.png -resize 144 ios/icon-72-2x.png
convert my-hires-icon.png -resize 152 ios/icon-76-2x.png
#
# (OS X, Unix and Linux)
#
# What is this?
#
# It's a shell script that is using ImageMagick to create all the icon files from one source icon.
#
# Stick the script in your 'www/res/icons' folder with your source icon 'my-hires-icon.png' then trigger it from Terminal.
#
ICON=${1:-"my-hires-icon.png"}
mkdir android
convert $ICON -resize 36x36 android/icon-36-ldpi.png
convert $ICON -resize 48x48 android/icon-48-mdpi.png
convert $ICON -resize 72x72 android/icon-72-hdpi.png
convert $ICON -resize 96x96 android/icon-96-xhdpi.png
mkdir ios
convert $ICON -resize 29 ios/icon-29.png
convert $ICON -resize 40 ios/icon-40.png
convert $ICON -resize 50 ios/icon-50.png
convert $ICON -resize 57 ios/icon-57.png
convert $ICON -resize 58 ios/icon-29-2x.png
convert $ICON -resize 60 ios/icon-60.png
convert $ICON -resize 72 ios/icon-72.png
convert $ICON -resize 76 ios/icon-76.png
convert $ICON -resize 80 ios/icon-40-2x.png
convert $ICON -resize 100 ios/icon-50-2x.png
convert $ICON -resize 114 ios/icon-57-2x.png
convert $ICON -resize 120 ios/icon-60-2x.png
convert $ICON -resize 144 ios/icon-72-2x.png
convert $ICON -resize 152 ios/icon-76-2x.png
#!/usr/bin/env node
var cordova_util = require('cordova/src/util');
var projectRoot = cordova_util.isCordova(process.cwd());
var projectXml = cordova_util.projectConfig(projectRoot);
var projectConfig = new cordova_util.config_parser(projectXml);
var projectPlatforms = cordova_util.listPlatforms(projectRoot);
projectConfig.name();
var fs = require ('fs');
var platformDir = {
ios: {
icon: "{$projectName}/Resources/icons",
splash: "{$projectName}/Resources/splash",
nameMap: {
// iOS >= 7 Settings icon
// iOS <= 6.1 Small icon for Spotlight search results and Settings (recommended) iPhone
"icon-29.png": "icon-small.png",
"icon-29-2x.png": "icon-small@2x.png",
// iOS >= 7 Spotlight search results icon (recommended)
"icon-40.png": "icon-40.png",
"icon-40-2x.png": "icon-40@2x.png",
// iOS <= 6.1 Small icon for Spotlight search results and Settings (recommended) iPad
"icon-50.png": "icon-50.png",
"icon-50-2x.png": "icon-50@2x.png",
// iOS <= 6.1 App icon (required) iPhone
"icon-57.png": "icon.png",
"icon-57-2x.png": "icon@2x.png",
// iOS >= 7 App icon (required) iPhone
"icon-60.png": "icon-60.png",
"icon-60-2x.png": "icon-60@2x.png",
// iOS <= 6.1 App icon (required) iPad
"icon-72.png": "icon-72.png",
"icon-72-2x.png": "icon-72@2x.png",
// iOS 7 App icon (required) iPad
"icon-76.png": "icon-76.png",
"icon-76-2x.png": "icon-76@2x.png",
// "screen-iphone-landscape.png": "Default~iphone.png",
"screen-ipad-portrait.png": "Default-Portrait~ipad.png",
"screen-ipad-portrait-2x.png": "Default-Portrait@2x~ipad.png",
"screen-ipad-landscape-2x.png": "Default-Landscape@2x~ipad.png",
"screen-ipad-landscape.png": "Default-Landscape~ipad.png",
"screen-iphone-portrait.png": "Default~iphone.png",
"screen-iphone-portrait-2x.png": "Default@2x~iphone.png",
"screen-iphone-portrait-568h-2x.png": "Default-568h@2x~iphone.png"
}
},
android: {
icon:"res/drawable-{$density}",
splash:"res/drawable-{$density}",
nameMap: {
"icon-36-ldpi.png": "icon.png",
"icon-48-mdpi.png": "icon.png",
"icon-72-hdpi.png": "icon.png",
"icon-96-xhdpi.png": "icon.png",
"screen-ldpi-portrait.png": "ic_launcher.png",
"screen-mdpi-portrait.png": "ic_launcher.png",
"screen-hdpi-portrait.png": "ic_launcher.png",
"screen-xhdpi-portrait.png": "ic_launcher.png"
}
},
blackberry10: {},
wp7: {},
wp8: {}
}
function copyAsset (scope, node) {
var platform = node.attrib['gap:platform'];
var density = node.attrib['gap:density'];
var assetDirTmpl = platformDir[platform] && platformDir[platform][scope];
if (!assetDirTmpl) {
throw new Error('Platform and density not supported: ' + platform + ', ' + density);
}
var dict = {
projectName: projectConfig.name(),
density: density
};
var assetDir = assetDirTmpl.replace(/{\$([^}]+)}/, function (match, p1) {
return dict[p1];
});
var srcPath = 'www/'+node.attrib.src;
var fileName = srcPath.match(/[^\/]+$/)[0];
if (platformDir[platform] && platformDir[platform].nameMap && platformDir[platform].nameMap[fileName]) {
fileName = platformDir[platform].nameMap[fileName];
} else {
throw new Error('Unknown icon name for platform ' + platform);
}
var dstPath = 'platforms/'+platform+'/'+assetDir+'/'+fileName;
console.log ('copying from '+srcPath+' to the '+dstPath);
// so, here we start to copy asset
fs.stat (srcPath, function (err, stats) {
if (err) {
throw err;
}
var r = fs.createReadStream(srcPath);
r.on ('open', function () {
r.pause();
var w = fs.createWriteStream(dstPath);
w.on ('open', function () {
r.pipe(w);
r.resume();
});
w.on ('error', function() {
throw new Error('Cannot write file');
})
});
r.on ('error', function() {
throw new Error('Cannot read file');
});
});
}
projectConfig.doc.findall('icon').map(function (node) {
if (/\/$/.test(node.attrib['src']) && node.attrib['gap:platform'] === undefined && node.attrib['gap:density'] === undefined) {
if (~projectPlatforms.indexOf('android')) {
// Android
copyAsset('icon', { attrib: { 'gap:platform': 'android', src: node.attrib.src + 'android/icon-36-ldpi.png', 'gap:density': 'ldpi' } });
copyAsset('icon', { attrib: { 'gap:platform': 'android', src: node.attrib.src + 'android/icon-48-mdpi.png', 'gap:density': 'mdpi' } });
copyAsset('icon', { attrib: { 'gap:platform': 'android', src: node.attrib.src + 'android/icon-72-hdpi.png', 'gap:density': 'hdpi' } });
copyAsset('icon', { attrib: { 'gap:platform': 'android', src: node.attrib.src + 'android/icon-96-xhdpi.png', 'gap:density': 'xhdpi' } });
}
if (~projectPlatforms.indexOf('ios')) {
// iOS >= 7 Settings icon
// iOS <= 6.1 Small icon for Spotlight search results and Settings (recommended) iPhone
copyAsset('icon', { attrib: { 'gap:platform': 'ios', src: node.attrib.src + 'ios/icon-29.png' } });
copyAsset('icon', { attrib: { 'gap:platform': 'ios', src: node.attrib.src + 'ios/icon-29-2x.png' } });
// iOS >= 7 Spotlight search results icon (recommended)
copyAsset('icon', { attrib: { 'gap:platform': 'ios', src: node.attrib.src + 'ios/icon-40.png' } });
copyAsset('icon', { attrib: { 'gap:platform': 'ios', src: node.attrib.src + 'ios/icon-40-2x.png' } });
// iOS <= 6.1 Small icon for Spotlight search results and Settings (recommended) iPad
copyAsset('icon', { attrib: { 'gap:platform': 'ios', src: node.attrib.src + 'ios/icon-50.png' } });
copyAsset('icon', { attrib: { 'gap:platform': 'ios', src: node.attrib.src + 'ios/icon-50-2x.png' } });
// iOS <= 6.1 App icon (required) iPhone
copyAsset('icon', { attrib: { 'gap:platform': 'ios', src: node.attrib.src + 'ios/icon-57.png' } });
copyAsset('icon', { attrib: { 'gap:platform': 'ios', src: node.attrib.src + 'ios/icon-57-2x.png' } });
// iOS >= 7 App icon (required) iPhone
copyAsset('icon', { attrib: { 'gap:platform': 'ios', src: node.attrib.src + 'ios/icon-60.png' } });
copyAsset('icon', { attrib: { 'gap:platform': 'ios', src: node.attrib.src + 'ios/icon-60-2x.png' } });
// iOS <= 6.1 App icon (required) iPad
copyAsset('icon', { attrib: { 'gap:platform': 'ios', src: node.attrib.src + 'ios/icon-72.png' } });
copyAsset('icon', { attrib: { 'gap:platform': 'ios', src: node.attrib.src + 'ios/icon-72-2x.png' } });
// iOS 7 App icon (required) iPad
copyAsset('icon', { attrib: { 'gap:platform': 'ios', src: node.attrib.src + 'ios/icon-76.png' } });
copyAsset('icon', { attrib: { 'gap:platform': 'ios', src: node.attrib.src + 'ios/icon-76-2x.png' } });
}
} else {
copyAsset ('icon', node);
}
});
projectConfig.doc.findall('*').filter(function (node) {
return (node.tag == 'gap:splash');
}).map(function (node) {
copyAsset ('splash', node);
});
@niiamon
Copy link

niiamon commented Feb 25, 2014

I fixed this. I had copied and pasted some directives into the config.xml from some other place and that was messing things up. But now splash screens do not work at all. I don't see the script trying to copy it.

@niiamon
Copy link

niiamon commented Feb 25, 2014

I finally fixed my problem. It appears that the script is looking for a gap:splash tag within config.xml as the following lines from icons_and_splash.js shows:

projectConfig.doc.findall('*').filter(function (node) {
return (node.tag == 'gap:splash');
}).map(function (node) {
copyAsset ('splash', node);
});

However, @LinusU recommended that we have this in the config.xml:

<gap:platform gap:platform="ios" src="res/splash/ios/screen-iphone-portrait.png" />

Obviously the script would fail to find the splash because there's a mismatch between what it expects the tag to be and what the tag actually is.

The simple solution I used was to instead use this tag within the config.xml file:

<gap:splash gap:platform="ios" src="res/splash/ios/screen-ipad-portrait.png" />

Combining that with the properly named files, I was able to get splash screens also working correctly.

@antoineF
Copy link

convert.sh for Mac :

#
# (OS X)
#
# What is this?
#
# It's a shell script that is using sips to create all the icon files from one source icon.
#
# Stick the script in your 'www/res/icons' folder with your source icon 'my-hires-icon.png' then trigger it from Terminal.
#

ICON=${1:-"my-hires-icon.png"}

mkdir android
sips $ICON -Z 36 -o android/icon-36-ldpi.png
sips $ICON -Z 48 -o android/icon-48-mdpi.png
sips $ICON -Z 72 -o android/icon-72-hdpi.png
sips $ICON -Z 96 -o android/icon-96-xhdpi.png

mkdir ios
sips $ICON -Z 29 -o ios/icon-29.png
sips $ICON -Z 40 -o ios/icon-40.png 
sips $ICON -Z 50 -o ios/icon-50.png 
sips $ICON -Z 57 -o ios/icon-57.png
sips $ICON -Z 58 -o ios/icon-29-2x.png
sips $ICON -Z 60 -o ios/icon-60.png
sips $ICON -Z 72 -o ios/icon-72.png
sips $ICON -Z 76 -o ios/icon-76.png  
sips $ICON -Z 80 -o ios/icon-40-2x.png
sips $ICON -Z 100 -o ios/icon-50-2x.png
sips $ICON -Z 114 -o ios/icon-57-2x.png     
sips $ICON -Z 120 -o ios/icon-60-2x.png
sips $ICON -Z 144 -o ios/icon-72-2x.png
sips $ICON -Z 152 -o ios/icon-76-2x.png

@klikin
Copy link

klikin commented Mar 1, 2014

Hi:

Got an error :(

$ cordova run android
Generating config.xml from defaults for platform "android"
Preparing android project

/usr/local/lib/node_modules/cordova/node_modules/q/q.js:126
throw e;
^
Error: Script "/project/.cordova/hooks/after_prepare/icons_and_splash.js" exited with status code 8. Aborting. Output:

/project/.cordova/hooks/after_prepare/icons_and_splash.js:6
var projectConfig = new cordova_util.config_parser(projectXml);
^
TypeError: undefined is not a function
at Object. (/project/.cordova/hooks/after_prepare/icons_and_splash.js:6:21)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Function.Module.runMain (module.js:497:10)
at startup (node.js:119:16)
at node.js:901:3

at /usr/local/lib/node_modules/cordova/src/hooker.js:138:34
at ChildProcess.exithandler (child_process.js:641:7)
at ChildProcess.EventEmitter.emit (events.js:98:17)
at maybeClose (child_process.js:735:16)
at Socket.<anonymous> (child_process.js:948:11)
at Socket.EventEmitter.emit (events.js:95:17)
at Pipe.close (net.js:466:12)

@jernejcic
Copy link

@klikin, did you figure this out? I'm getting the same thing but on Windows. It runs fine on my Mac though.

@slawo
Copy link

slawo commented Mar 20, 2014

config_parser has been removed in this commit: apache/cordova-cli@45e80ac#diff-34a6d62af0cf0b784f8444529f3130ef

replace

var projectConfig = new cordova_util.config_parser(projectXml);

with

var ConfigParser = cordova_util.config_parser
if(undefined === ConfigParser) {
    ConfigParser = require('cordova/src/ConfigParser');
}
var projectConfig = new ConfigParser(projectXml);

@hartman
Copy link

hartman commented Mar 26, 2014

i've made some changes in my version: https://gist.github.com/hartman/9784684

it includes slawo's change, but also supports the older method
has winphone support
warns about missing icons
warns when no icons are not defined in config.xml

@niiamon
Copy link

niiamon commented Apr 2, 2014

@hartman I get some breaking errors although the copy action for icons works well:

Update all icons for project: MyProject

copying from res/icons/android/icon-36-ldpi.png to the platforms/android/res/drawable-ldpi/icon.png

copying from res/icons/android/icon-48-mdpi.png to the platforms/android/res/drawable-mdpi/icon.png

copying from res/icons/android/icon-72-hdpi.png to the platforms/android/res/drawable-hdpi/icon.png

copying from res/icons/android/icon-96-xhdpi.png to the platforms/android/res/drawable-xhdpi/icon.png

copying from res/icons/ios/icon-29.png to the platforms/ios/MyProject/Resources/icons/icon-small.png

copying from res/icons/ios/icon-29-2x.png to the platforms/ios/MyProject/Resources/icons/icon-small@2x.png

copying from res/icons/ios/icon-40.png to the platforms/ios/MyProject/Resources/icons/icon-40.png

copying from res/icons/ios/icon-40-2x.png to the platforms/ios/MyProject/Resources/icons/icon-40@2x.png

copying from res/icons/ios/icon-50.png to the platforms/ios/MyProject/Resources/icons/icon-50.png

copying from res/icons/ios/icon-50-2x.png to the platforms/ios/MyProject/Resources/icons/icon-50@2x.png

copying from res/icons/ios/icon-57.png to the platforms/ios/MyProject/Resources/icons/icon.png

copying from res/icons/ios/icon-57-2x.png to the platforms/ios/MyProject/Resources/icons/icon@2x.png

copying from res/icons/ios/icon-60.png to the platforms/ios/MyProject/Resources/icons/icon-60.png

copying from res/icons/ios/icon-60-2x.png to the platforms/ios/MyProject/Resources/icons/icon-60@2x.png

copying from res/icons/ios/icon-72.png to the platforms/ios/MyProject/Resources/icons/icon-72.png

copying from res/icons/ios/icon-72-2x.png to the platforms/ios/MyProject/Resources/icons/icon-72@2x.png

copying from res/icons/ios/icon-76.png to the platforms/ios/MyProject/Resources/icons/icon-76.png

copying from res/icons/ios/icon-76-2x.png to the platforms/ios/MyProject/Resources/icons/icon-76@2x.png

/path/to/.cordova/hooks/after_prepare/icons_and_splash.js:136
throw err;
^
Error: ENOENT, stat 'res/icons/android/icon-36-ldpi.png'
Hook failed with error code 8: /path/to/.cordova/hooks/after_prepare/icons_and_splash.js

I am trying to figure out why this happens. The files are copied successfully and I can confirm that by looking at the icons in the platform folders. They are what you would expect.

The only way I have gotten it to work properly is to change all the throws to console.log to enable to code to fail silently. Any way to fix this?

@rsuper
Copy link

rsuper commented Apr 23, 2014

@niiamon The script can't find the file, so it throws that error.
To make it work, I added these 2 lines on line 132 and 133:

srcPath = projectRoot + '/www/' + srcPath;
dstPath = projectRoot + '/' + dstPath;

Just above - console.log ('copying from '+srcPath+' to the '+dstPath); - where it outputs which file is being copied.
Note: I used the version of @hartman

@Takeno
Copy link

Takeno commented May 1, 2014

@hartman @rsuper @niiamon
The best way to fix relative path issue in the script is:

  1. Include path module after line 10 var path = require('path'); (after fs declaration)
  2. Fix srcPath in line 98 var srcPath = path.join(projectRoot, 'www', node.attrib.src);
  3. Fix dstPath in line 106 var dstPath = path.join(projectRoot, 'platforms', platform, assetDir, fileName);

@AncAinu
Copy link

AncAinu commented May 6, 2014

I forked this gist and fixed issues I found: https://gist.github.com/AncAinu/40b7d85d3c6ed77150aa

@manuganji
Copy link

For ubuntu, instead of mkdir android you can use mkdir -p android to suppress the annoying message when the directory is already present.

@shedd
Copy link

shedd commented Jun 8, 2014

I ran into some issues with Cordova 3.5.0.

Cordova was refactored in this commit: apache/cordova-cli@b51e1c1

Therefore, some changes are required to get this to run under the latest version.

First, in addition to having cordova installed at the root of your project, also install:

npm install cordova-lib

Then, change:

var cordova_util = require('cordova/src/util');

to

var cordova_util = require('cordova-lib/src/cordova/util');

Also, make this change - https://gist.github.com/LinusU/7515016#comment-1195042 - for ConfigParser.

Then change:

ConfigParser = require('cordova/src/ConfigParser');

to

ConfigParser = require('cordova-lib/src/cordova/ConfigParser');

@shedd
Copy link

shedd commented Jun 8, 2014

I've forked the gist and included the updates from @hartman and @Takeno plus the revisions needed for Cordova 3.5.0.

Updated version here: https://gist.github.com/shedd/2bcbecec53ee8cddebfe

@shedd
Copy link

shedd commented Jun 8, 2014

If anyone is looking for a conversion script that handles splash screens, I found this option: https://github.com/tlvince/phonegap-icon-splash-generator

I ended up taking bits and pieces of the two scripts and hooking them both into my build process.

@MGalv
Copy link

MGalv commented Sep 2, 2014

For cordoba-lib version ~> 0.21.4, you should change this line:

CordovaConfigParser = require('cordova-lib/src/cordova/configparser');

for this one:

CordovaConfigParser = require('cordova-lib').configparser;

@3dcinetv
Copy link

It´s been 2 days now, please help me solve this error, please:
On my cmd I type:

D:\VB4>phonegap build android

Then it returns:
[phonegap] executing 'cordova build android'...
D:\VB4\res\icons\ is a directory (not copied)

Running command: "C:\Program Files\nodejs\node.exe" D:\VB4\hooks\after_prepare\i
cons_and_splash.js D:\VB4

module.js:338
throw err;
^

Error: Cannot find module 'cordova/src/util'
at Function.Module._resolveFilename (module.js:336:15)
at Function.Module._load (module.js:286:25)
at Module.require (module.js:365:17)
at require (module.js:384:17)
at Object. (D:\VB4\hooks\after_prepare\icons_and_splash.js:3:20)
at Module._compile (module.js:434:26)
at Object.Module._extensions..js (module.js:452:10)
at Module.load (module.js:355:32)
at Function.Module._load (module.js:310:12)
at Function.Module.runMain (module.js:475:10)

Error: Hook failed with error code 1: D:\VB4\hooks\after_prepare\icons_and_splas
h.js

@ekorslin
Copy link

Hi everyone,
I'm getting the following after attempting to run cordova emulate ios with my cordova app. Any suggestions for a fix?

/Users/ekorslin/Desktop/cordovaReactProject/hooks/after_prepare/icons_and_splash.js:127
        throw new Error('Cannot write file');
        ^

Error: Cannot write file
    at WriteStream.<anonymous> (/Users/ekorslin/Desktop/cordovaReactProject/hooks/after_prepare/icons_and_splash.js:127:15)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment