├── .eslintignore ├── .gitignore ├── images ├── icon.png ├── image.png ├── splash.png └── Google │ └── 7Inch.png ├── .npmignore ├── CONTRIBUTING.md ├── .editorconfig ├── mediagen-config.json ├── test └── specs │ ├── mediagen-config.json │ ├── app.spec.js │ └── screenshots.spec.js ├── .travis.yml ├── LICENSE ├── lib ├── store_images.js ├── generate_image_sets.js ├── resize.js ├── splash_images.js └── generate_icon_sets.js ├── ISSUE_TEMPLATE.md ├── Jenkinsfile ├── package.json ├── CHANGELOG.md ├── .eslintrc ├── README.md ├── screenshots.js └── tns-media-gen.js /.eslintignore: -------------------------------------------------------------------------------- 1 | **/*{.,-}min.js 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .tmp/ 3 | app/ 4 | dist/ 5 | node_modules/ 6 | media/ 7 | -------------------------------------------------------------------------------- /images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hypery2k/nativescript-media-generator/HEAD/images/icon.png -------------------------------------------------------------------------------- /images/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hypery2k/nativescript-media-generator/HEAD/images/image.png -------------------------------------------------------------------------------- /images/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hypery2k/nativescript-media-generator/HEAD/images/splash.png -------------------------------------------------------------------------------- /images/Google/7Inch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hypery2k/nativescript-media-generator/HEAD/images/Google/7Inch.png -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .tmp/ 3 | app/ 4 | dist/ 5 | node_modules/ 6 | images/ 7 | media/ 8 | screenshots/ 9 | test/ 10 | Jenkinsfile 11 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | First search if the issue is already described! 2 | 3 | If not create a new issue: 4 | 5 | * Tell about your environment: 6 | * node version 7 | * cordova version 8 | * used platform and version 9 | * Describe your issue 10 | * describe your steps leading to the issue 11 | * attach error logs or screenshots 12 | * if possible provide test case or screenshots 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | # Change these settings to your own preference 10 | indent_style = space 11 | indent_size = 2 12 | 13 | # We recommend you to keep these unchanged 14 | end_of_line = lf 15 | insert_final_newline = true 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | max_line_length = 140 19 | 20 | [*.{js, ts}] 21 | quote_type = single 22 | 23 | [*.md] 24 | trim_trailing_whitespace = false 25 | 26 | [{package.json,.travis.yml}] 27 | indent_style = space 28 | indent_size = 2 29 | -------------------------------------------------------------------------------- /mediagen-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "icon": { 3 | "filename": "icon.png", 4 | "background": "fff" 5 | }, 6 | "splash": { 7 | "filename": "splash.png", 8 | "background": "fff" 9 | }, 10 | "customImages": [ 11 | { 12 | "width": 120, 13 | "height": 120, 14 | "path": "../../Media/custom", 15 | "filename": "outputFilename.png", 16 | "source": { 17 | "filename": "image.png", 18 | "background": "fff" 19 | } 20 | } 21 | ], 22 | "screenshots": [ 23 | { 24 | "url": "http://www.google.com", 25 | "name": "homepage" 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /test/specs/mediagen-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "icon": { 3 | "filename": "icon.png", 4 | "background": "fff" 5 | }, 6 | "splash": { 7 | "filename": "splash.png", 8 | "background": "fff" 9 | }, 10 | "customImages": [ 11 | { 12 | "width": 120, 13 | "height": 120, 14 | "path": "../Media/custom", 15 | "filename": "outputFilename.png", 16 | "source": { 17 | "filename": "image.png", 18 | "background": "fff" 19 | } 20 | } 21 | ], 22 | "screenshots": [ 23 | { 24 | "url": "http://www.google.com", 25 | "name": "homepage" 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: osx 2 | osx_image: xcode8.3 3 | sudo: false 4 | language: objective-c 5 | 6 | env: 7 | matrix: 8 | - NODE_VERSION="4" NPM_VERSION=3 9 | - NODE_VERSION="5" NPM_VERSION=3 10 | - NODE_VERSION="5" NPM_VERSION=4 11 | - NODE_VERSION="6" NPM_VERSION=3 12 | - NODE_VERSION="6" NPM_VERSION=4 13 | 14 | script: 15 | - npm install 16 | - npm run build 17 | - npm run test 18 | before_install: 19 | - source ~/.nvm/nvm.sh && nvm install $NODE_VERSION && nvm use $NODE_VERSION 20 | - PATH="`npm bin`:`npm bin -g`:$PATH" 21 | - npm install -g npm@$NPM_VERSION 22 | - brew update > /dev/null; 23 | - brew install jpeg python3 imagemagick 24 | # Show environment invo 25 | - node --version 26 | - npm --version 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Martin Reinhardt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/specs/app.spec.js: -------------------------------------------------------------------------------- 1 | var mediaGen = require('../../tns-media-gen'); 2 | 3 | describe('main app', function() { 4 | describe('resize', function() { 5 | it('should not error out', function(done) { 6 | mediaGen.__resize(10, 10, "#fff", "images/icon.png", "output.png", "../../.tmp") 7 | .then(function(result) { 8 | expect(result).toBeDefined(); 9 | done(); 10 | }) 11 | .catch(done); 12 | }) 13 | }); 14 | 15 | describe('generate', function() { 16 | it('should not error out', function(done) { 17 | mediaGen.__generate() 18 | .then(function(result) { 19 | done(); 20 | }) 21 | .catch(done) 22 | }) 23 | }); 24 | 25 | describe('genConfig', function() { 26 | it('should not error out', function(done) { 27 | mediaGen.__genConfig() 28 | .then(function(result) { 29 | expect(result).toBe('success'); 30 | done(); 31 | }) 32 | .catch(done); 33 | }) 34 | }) 35 | }); -------------------------------------------------------------------------------- /lib/store_images.js: -------------------------------------------------------------------------------- 1 | module.exports = function storeImagesGenerator(config, mediaPath) { 2 | return [ 3 | //Android Store Icons 4 | { 5 | width: 512, 6 | height: 512, 7 | path: "../../" + mediaPath + "/android/store", 8 | filename: "512.png", 9 | source: process.argv[2] || config.icon || config.image 10 | }, { 11 | width: 1024, 12 | height: 500, 13 | path: "../../" + mediaPath + "/android/store", 14 | filename: "1024x500.png", 15 | source: process.argv[2] || config.splash || config.image 16 | }, { 17 | width: 180, 18 | height: 120, 19 | path: "../../" + mediaPath + "/android/store", 20 | filename: "180x120.png", 21 | source: process.argv[2] || config.splash || config.image 22 | }, 23 | 24 | //Apple store icons 25 | { 26 | width: 1024, 27 | height: 1024, 28 | path: "../../" + mediaPath + "/ios/store", 29 | filename: "1024x1024-AppIcon.jpg", 30 | source: process.argv[2] || config.icon || config.image 31 | } 32 | ]; 33 | } -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | > ℹ Please fill out this template when filing an issue. 2 | > All lines beginning with an ℹ symbol instruct you with what info we expect. You can delete those lines once you've filled in the info. 3 | > 4 | > Per our [*CONTRIBUTING guidelines](https://github.com/hypery2k/nativescript-media-generator/master/CONTRIBUTING.md), we use GitHub for 5 | > bugs and feature requests, not general support. Other issues should be opened on Stack Overflow with the tag `nativescript`. 6 | > 7 | > Please remove this line and everything above it before submitting. 8 | 9 | * [ ] I've read, understood, and done my best to follow the [*CONTRIBUTING guidelines](https://github.com/hypery2k/nativescript-media-generator/master/CONTRIBUTING.md). 10 | 11 | ## What did you do? 12 | 13 | ℹ Please replace this with what you did. 14 | 15 | ## What did you expect to happen? 16 | 17 | ℹ Please replace this with what you expected to happen. 18 | 19 | ## What happened instead? 20 | 21 | ℹ Please replace this with of what happened instead. 22 | 23 | ## Your Environment 24 | 25 | **NativeScript version:** 26 | **Java version:** 27 | **NodeJS and NPM version:** 28 | **Platform(s) running:** 29 | 30 | ## Demo Project 31 | 32 | ℹ Please link to or upload a project we can download that reproduces the issue. 33 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | properties properties: [ 2 | [$class: 'BuildDiscarderProperty', strategy: [$class: 'LogRotator', artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '30', numToKeepStr: '10']], 3 | [$class: 'GithubProjectProperty', displayName: '', projectUrlStr: 'https://github.com/hypery2k/nativescript-media-generator'], 4 | ] 5 | 6 | @Library('mare-build-library') 7 | def nodeJS = new de.mare.ci.jenkins.NodeJS() 8 | 9 | node { 10 | def buildNumber = env.BUILD_NUMBER 11 | def branchName = env.BRANCH_NAME 12 | def mvnHome = '/opt/dev/apache-maven-3.3.1' 13 | def workspace = env.WORKSPACE 14 | def buildUrl = env.BUILD_URL 15 | env.PATH="${env.JAVA_HOME}/bin:${mvnHome}/bin:${env.PATH}" 16 | 17 | // PRINT ENVIRONMENT TO JOB 18 | echo "workspace directory is $workspace" 19 | echo "build URL is $buildUrl" 20 | echo "build Number is $buildNumber" 21 | echo "branch name is $branchName" 22 | echo "PATH is $env.PATH" 23 | 24 | try { 25 | 26 | stage('Checkout') { 27 | checkout scm 28 | } 29 | 30 | stage('Build') { 31 | sh "npm install" 32 | } 33 | 34 | stage('Test') { 35 | wrap([$class: 'Xvfb']) { 36 | sh "npm run test" 37 | junit 'dist/reports/TEST-*.xml' 38 | } 39 | } 40 | 41 | stage('Publish NPM snapshot') { 42 | nodeJS.publishSnapshot('', buildNumber, branchName) 43 | } 44 | 45 | } catch (e) { 46 | mail subject: "${env.JOB_NAME} (${env.BUILD_NUMBER}): Error on build", to: 'github@martinreinhardt-online.de', body: "Please go to ${env.BUILD_URL}." 47 | throw e 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/generate_image_sets.js: -------------------------------------------------------------------------------- 1 | var q = require('q'), 2 | path = require('path'), 3 | fs = require('fs'), 4 | gm = require('gm').subClass({ imageMagick: true }); 5 | 6 | module.exports = function generateImageSets(filename, source, iOSPath) { /*eslint complexity: [error, 22]*/ 7 | return [ 8 | //IOS Images 9 | { 10 | width: "24.5%", // max 466 11 | path: 'iOS/Assets.xcassets' + (iOSPath ? '/' + iOSPath : ''), 12 | filename: filename, 13 | source: source 14 | }, { 15 | width: "49%", // max 932 16 | path: 'iOS/Assets.xcassets' + (iOSPath ? '/' + iOSPath : ''), 17 | filename: filename.replace('.', '@2x.'), 18 | source: source 19 | }, { 20 | width: "73.6%", // max 1398 21 | path: 'iOS/Assets.xcassets' + (iOSPath ? '/' + iOSPath : ''), 22 | filename: filename.replace('.', '@3x.'), 23 | source: source 24 | }, 25 | 26 | //Android 27 | { 28 | width: "75%", // max 1425 29 | path: "Android/drawable-hdpi", 30 | filename: filename, 31 | source: source 32 | }, { 33 | width: "50%", // max 950 34 | path: "Android/drawable-mdpi", 35 | filename: filename, 36 | source: source 37 | }, { 38 | width: "100%", // max 1900 39 | path: "Android/drawable-xhdpi", 40 | filename: filename, 41 | source: source 42 | }, { 43 | width: "70%", // max 1331 44 | path: "Android/drawable-xxhdpi", 45 | filename: filename, 46 | source: source 47 | }, { 48 | width: "93.4%", // max 1775 49 | path: "Android/drawable-xxxhdpi", 50 | filename: filename, 51 | source: source 52 | } 53 | 54 | ] 55 | } -------------------------------------------------------------------------------- /test/specs/screenshots.spec.js: -------------------------------------------------------------------------------- 1 | var screenshots = require('../../screenshots'), 2 | fs = require('fs'); 3 | 4 | var settings = { 5 | url: 'http://www.google.com', 6 | width: 1280, 7 | height: 720, 8 | path: "images/Google", 9 | fileName: '7Inch.png' 10 | }; 11 | 12 | xdescribe('Screenshots', function () { 13 | describe('generate', function () { 14 | it('should return no errors', function (done) { 15 | screenshots.generate(settings.url, settings.width, settings.height, 1, settings.path, settings.fileName) 16 | .then(function (result) { 17 | console.log('==>' + result) 18 | expect(result.success).toBeDefined(); 19 | }) 20 | .catch(function (error) { 21 | throw new error; 22 | }) 23 | .done(function () { done() }); 24 | }, 30000); 25 | 26 | it('should have created a file', function (done) { 27 | fs.exists(settings.path + "/" + settings.fileName, function (result) { 28 | expect(result).toBeTruthy(); 29 | done(); 30 | }); 31 | }, 30000); 32 | 33 | it('should die when nothing is supplied', function (done) { 34 | screenshots.generate() 35 | .then(function (result) { 36 | fail("Should not have worked"); 37 | }) 38 | .catch(function (err) { 39 | expect(err).toBeDefined(); 40 | done(); 41 | }); 42 | }, 30000); 43 | }); 44 | 45 | describe('generate all', function () { 46 | screenshots.pages.push({ url: "http://www.google.com", name: "home" }); 47 | 48 | it('should return', function (done) { 49 | expect(screenshots.generateAll()).toBeTruthy(); 50 | done(); 51 | }, 30000); 52 | 53 | it('should have created a file', function (done) { 54 | fs.exists("Media/android/screenshots/10in/android-10in-1280x720-google.png", function (result) { 55 | expect(result).toBeTruthy(); 56 | done(); 57 | }); 58 | }, 30000); 59 | }); 60 | 61 | }); 62 | -------------------------------------------------------------------------------- /lib/resize.js: -------------------------------------------------------------------------------- 1 | var q = require('q'), 2 | path = require('path'), 3 | fs = require('fs'), 4 | gm = require('gm').subClass({ imageMagick: true }), 5 | mkdirp = require('mkdirp'); 6 | 7 | module.exports = function resize(width, height, bgColour, imagePath, outputFilename, outputPath) { 8 | var deferred = q.defer(), 9 | filepath = path.join(process.cwd(), imagePath); 10 | gm(filepath).size(function(error, size) { 11 | 12 | if (error) { 13 | console.error("GM Error", error); 14 | deferred.reject(error); 15 | } else { 16 | 17 | // current image size 18 | var imageWidth = size.width; 19 | var imageHeight = size.height; 20 | var ratio = width / height, 21 | imageRatio = imageWidth / imageHeight; 22 | var image = this; 23 | 24 | if (isNaN(parseFloat(width))) { 25 | var percentage = '' + width; 26 | width = imageWidth * (parseInt(percentage.substring(0, percentage.indexOf("%")), 0) / 100); 27 | } 28 | // center placement 29 | if (ratio >= 1) { 30 | //Landscape or square 31 | var newWidth = height * imageRatio; 32 | 33 | if (newWidth >= width) { 34 | this.resize(width); 35 | } else { 36 | this.resize(null, height); 37 | } 38 | 39 | 40 | } else { 41 | var newHeight = width / imageRatio; 42 | 43 | if (newHeight >= height) { 44 | this.resize(null, height); 45 | } else { 46 | this.resize(width); 47 | } 48 | 49 | } 50 | 51 | var x = (width / 2) - (imageWidth / 2); 52 | var y = (height / 2) - (imageHeight / 2); 53 | 54 | mkdirp(path.join(process.cwd(), "app", "App_Resources", outputPath), function() { 55 | image.background(bgColour) 56 | .gravity('Center') 57 | .extent(width, height) 58 | .write(path.join(process.cwd(), "app", "App_Resources", outputPath, outputFilename), function(error) { 59 | if (error) { 60 | console.error("Write file error", error); 61 | deferred.reject(error); 62 | } else { 63 | console.log(this.outname); 64 | deferred.resolve(this.outname); 65 | } 66 | }); 67 | }); 68 | } 69 | 70 | }); 71 | return deferred.promise; 72 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nativescript-media-generator", 3 | "version": "1.0.0", 4 | "description": "Generates NativeScript Media (Splashscreens, Icons,Images & Screenshots)", 5 | "main": "tns-media-gen.js", 6 | "bin": { 7 | "tns-media-gen": "tns-media-gen.js" 8 | }, 9 | "preferGlobal": true, 10 | "scripts": { 11 | "clean": "npm i rimraf && rimraf node_modules dist app && npm i", 12 | "build": "npm run eslint", 13 | "pretest": "mkdir -p app/App_Resources/iOS && mkdir -p app/App_Resources/android", 14 | "test": "npm run pretest && jasmine-node --captureExceptions --color --junitreport --output dist/reports/ test/specs/", 15 | "eslint": "eslint \"*.js\"", 16 | "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r0 ", 17 | "changelog:add": "git add CHANGELOG.md && git commit -m 'updated CHANGELOG.md'", 18 | "release:pre": "npm run clean && npm run test", 19 | "release:post": "npm run changelog && npm run changelog:add", 20 | "release:major": "npm run release:pre && npm version major && npm run release:post && git push origin && git push origin --tags && npm run version-and-push", 21 | "release:minor": "npm run release:pre && npm version minor && npm run release:post && git push origin && git push origin --tags && npm run version-and-push", 22 | "release:patch": "npm run release:pre && npm version patch && npm run release:post && git push origin && git push origin --tags && npm run version-and-push", 23 | "version-and-push": "git push origin && git push origin --tags && npm run merge-and-publish", 24 | "merge-and-publish": "git checkout master && git merge develop && npm publish && git reset --hard && git clean -f" 25 | }, 26 | "nativescript": { 27 | "platforms": { 28 | "ios": "1.1.0", 29 | "android": "1.1.0" 30 | } 31 | }, 32 | "dependencies": { 33 | "async": "2.6.0", 34 | "filewalker": "0.1.3", 35 | "fs": "0.0.2", 36 | "gm": "1.23.1", 37 | "mkdirp": "0.5.1", 38 | "path": "0.12.7", 39 | "phantom": "4.0.12", 40 | "phantomjs-prebuilt": "2.1.16", 41 | "q": "1.5.1" 42 | }, 43 | "devDependencies": { 44 | "conventional-changelog-cli": "1.3.12", 45 | "cross-env": "5.1.3", 46 | "eslint": "4.15.0", 47 | "eslint-loader": "1.9.0", 48 | "eslint-plugin-es6-recommended": "0.1.2", 49 | "eslint-plugin-import": "2.8.0", 50 | "jasmine-node": "1.14.5", 51 | "rimraf": "2.6.2" 52 | }, 53 | "repository": { 54 | "type": "git", 55 | "url": "https://github.com/hypery2k/nativescript-media-generator.git" 56 | }, 57 | "keywords": [ 58 | "NativeScript", 59 | "icons", 60 | "splash", 61 | "screens", 62 | "images" 63 | ], 64 | "author": "Martin Reinhardt", 65 | "license": "MIT", 66 | "bugs": "https://github.com/hypery2k/nativescript-media-generator/issues", 67 | "homepage": "https://github.com/hypery2k/nativescript-media-generator", 68 | "engine-strict": true, 69 | "engines": { 70 | "node": ">= 4.4", 71 | "npm": ">= 3" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | # [1.0.0](https://github.com/hypery2k/nativescript-media-generator/compare/v0.2.4...v1.0.0) (2018-01-08) 3 | 4 | 5 | ### Bug Fixes 6 | 7 | * **config-error:** Resolve config not found error ([f2b6684](https://github.com/hypery2k/nativescript-media-generator/commit/f2b6684)), closes [#14](https://github.com/hypery2k/nativescript-media-generator/issues/14) 8 | * **convert-error:** Resolve resize error without giving width ([e73bed6](https://github.com/hypery2k/nativescript-media-generator/commit/e73bed6)), closes [#14](https://github.com/hypery2k/nativescript-media-generator/issues/14) 9 | * **node-env:** Correct command not found error ([4c264bc](https://github.com/hypery2k/nativescript-media-generator/commit/4c264bc)), closes [#14](https://github.com/hypery2k/nativescript-media-generator/issues/14) 10 | * **phantom-usage:** Use phantom 3.x to support node4+ ([2af5e89](https://github.com/hypery2k/nativescript-media-generator/commit/2af5e89)) 11 | 12 | 13 | ### Features 14 | 15 | * **iPhoneX:** Support for iPhone X ([2c46388](https://github.com/hypery2k/nativescript-media-generator/commit/2c46388)) 16 | 17 | 18 | 19 | 20 | ## [0.2.4](https://github.com/hypery2k/nativescript-media-generator/compare/v0.2.3...v0.2.4) (2017-03-02) 21 | 22 | 23 | ### Bug Fixes 24 | 25 | * **build:** Fix build error ([4ce81ae](https://github.com/hypery2k/nativescript-media-generator/commit/4ce81ae)) 26 | * **build:** Fix build error ([cbf9d82](https://github.com/hypery2k/nativescript-media-generator/commit/cbf9d82)) 27 | * **cli:** Fix binary command error ([a54e067](https://github.com/hypery2k/nativescript-media-generator/commit/a54e067)), closes [#13](https://github.com/hypery2k/nativescript-media-generator/issues/13) 28 | 29 | 30 | 31 | 32 | ## [0.2.3](https://github.com/hypery2k/nativescript-media-generator/compare/v0.2.2...v0.2.3) (2017-01-30) 33 | 34 | 35 | ### Bug Fixes 36 | 37 | * **node4:** Use older node for node4+ support ([a8ce11c](https://github.com/hypery2k/nativescript-media-generator/commit/a8ce11c)) 38 | * **resizing:** Correct percentages for images ([27f7ad3](https://github.com/hypery2k/nativescript-media-generator/commit/27f7ad3)) 39 | 40 | 41 | 42 | 43 | ## [0.2.2](https://github.com/hypery2k/nativescript-media-generator/compare/v0.2.1...v0.2.2) (2017-01-11) 44 | 45 | 46 | ### Bug Fixes 47 | 48 | * **splash-screen:** Correct android splash screen size ([f4f2d71](https://github.com/hypery2k/nativescript-media-generator/commit/f4f2d71)) 49 | 50 | 51 | 52 | 53 | ## [0.2.1](https://github.com/hypery2k/nativescript-media-generator/compare/6aedc62...v0.2.1) (2016-12-13) 54 | 55 | 56 | ### Bug Fixes 57 | 58 | * **image-gen:** Correct property usage for image generation ([b21d7ce](https://github.com/hypery2k/nativescript-media-generator/commit/b21d7ce)) 59 | * **module_missing:** Add missing async module ([6aedc62](https://github.com/hypery2k/nativescript-media-generator/commit/6aedc62)) 60 | * **test:** Fix test errors ([a53c3b2](https://github.com/hypery2k/nativescript-media-generator/commit/a53c3b2)) 61 | * **test:** FIx test errors ([3223ff6](https://github.com/hypery2k/nativescript-media-generator/commit/3223ff6)) 62 | 63 | 64 | ### Features 65 | 66 | * **additional_images:** Allow additional images ([39673ca](https://github.com/hypery2k/nativescript-media-generator/commit/39673ca)) 67 | * **images:** Support for images ([b2ba106](https://github.com/hypery2k/nativescript-media-generator/commit/b2ba106)) 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /lib/splash_images.js: -------------------------------------------------------------------------------- 1 | module.exports = function splashImagesGenerator(config, mediaPath) { 2 | return [ 3 | //iOS Splash 4 | { 5 | width: 640, 6 | height: 1136, 7 | path: "iOS/Assets.xcassets/LaunchImage.launchimage", 8 | filename: "Default-568h@2x.png", 9 | source: process.argv[2] || config.splash || config.image 10 | }, { 11 | width: 1024, 12 | height: 768, 13 | path: "iOS/Assets.xcassets/LaunchImage.launchimage", 14 | filename: "Default-Landscape.png", 15 | source: process.argv[2] || config.splash || config.image 16 | }, { 17 | width: 1536, 18 | height: 2048, 19 | path: "iOS/Assets.xcassets/LaunchImage.launchimage", 20 | filename: "Default-Portrait@2x.png", 21 | source: process.argv[2] || config.splash || config.image 22 | }, { 23 | width: 768, 24 | height: 1024, 25 | path: "iOS/Assets.xcassets/LaunchImage.launchimage", 26 | filename: "Default-Portrait.png", 27 | source: process.argv[2] || config.splash || config.image 28 | }, { 29 | width: 640, 30 | height: 960, 31 | path: "iOS/Assets.xcassets/LaunchImage.launchimage", 32 | filename: "Default@2x.png", 33 | source: process.argv[2] || config.splash || config.image 34 | }, { 35 | width: 320, 36 | height: 480, 37 | path: "iOS/Assets.xcassets/LaunchImage.launchimage", 38 | filename: "Default.png", 39 | source: process.argv[2] || config.splash || config.image 40 | }, { 41 | width: 750, 42 | height: 1344, 43 | path: "iOS/Assets.xcassets/LaunchImage.launchimage", 44 | filename: "Default-667h.png", 45 | source: process.argv[2] || config.splash || config.image 46 | }, { 47 | width: 1242, 48 | height: 2208, 49 | path: "iOS/Assets.xcassets/LaunchImage.launchimage", 50 | filename: "Default-736h.png", 51 | source: process.argv[2] || config.splash || config.image 52 | }, { 53 | width: 2208, 54 | height: 1242, 55 | path: "iOS/Assets.xcassets/LaunchImage.launchimage", 56 | filename: "Default-Landscape-736h.png", 57 | source: process.argv[2] || config.splash || config.image 58 | }, { 59 | width: 768, 60 | height: 1024, 61 | path: "iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset", 62 | filename: "LaunchScreen-AspectFill.png", 63 | source: process.argv[2] || config.splash || config.image 64 | }, { 65 | width: 1536, 66 | height: 2048, 67 | path: "iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset", 68 | filename: "LaunchScreen-AspectFill@2x.png", 69 | source: process.argv[2] || config.splash || config.image 70 | }, { 71 | width: 384, 72 | height: 512, 73 | path: "iOS/Assets.xcassets/LaunchScreen.Center.imageset", 74 | filename: "LaunchScreen-Center.png", 75 | source: process.argv[2] || config.splash || config.image 76 | }, { 77 | width: 768, 78 | height: 1024, 79 | path: "iOS/Assets.xcassets/LaunchScreen.Center.imageset", 80 | filename: "LaunchScreen-Center@2x.png", 81 | source: process.argv[2] || config.splash || config.image 82 | }, { 83 | width: 1125, 84 | height: 2436, 85 | path: "iOS/Assets.xcassets/LaunchImage.launchimage", 86 | filename: "Default-1125h.png", 87 | source: process.argv[2] || config.splash || config.image 88 | }, { 89 | width: 2436, 90 | height: 1125, 91 | path: "iOS/Assets.xcassets/LaunchImage.launchimage", 92 | filename: "Default-Landscape-X.png", 93 | source: process.argv[2] || config.splash || config.image 94 | }, 95 | // Android 96 | { 97 | width: 480, 98 | path: "Android/drawable-mdpi", 99 | filename: "splash.png", 100 | source: process.argv[2] || config.splash || config.image 101 | }, 102 | { 103 | width: 800, 104 | path: "Android/drawable-hdpi", 105 | filename: "splash.png", 106 | source: process.argv[2] || config.splash || config.image 107 | }, { 108 | width: 960, 109 | path: "Android/drawable-xhdpi", 110 | filename: "splash.png", 111 | source: process.argv[2] || config.splash || config.image 112 | }, { 113 | width: 1600, 114 | path: "Android/drawable-xxhdpi", 115 | filename: "splash.png", 116 | source: process.argv[2] || config.splash || config.image 117 | }, { 118 | width: 1920, 119 | path: "Android/drawable-xxxhdpi", 120 | filename: "splash.png", 121 | source: process.argv[2] || config.splash || config.image 122 | } 123 | 124 | ]; 125 | } -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | plugins: [ 2 | "import", 3 | "es6-recommended" 4 | ] 5 | 6 | parserOptions: { 7 | ecmaVersion: 6, 8 | sourceType: "module" 9 | } 10 | 11 | ecmaFeatures: 12 | modules: true 13 | jsx: true 14 | 15 | env: 16 | amd: true 17 | browser: true 18 | es6: true 19 | jquery: false 20 | node: true 21 | 22 | # http://eslint.org/docs/rules/ 23 | rules: 24 | import/no-unresolved: [2, {commonjs: true, amd: true}] 25 | import/named: 2 26 | import/namespace: 2 27 | import/default: 2 28 | import/export: 2 29 | # Possible Errors 30 | comma-dangle: [2, never] 31 | no-cond-assign: 2 32 | no-console: 0 33 | no-constant-condition: 2 34 | no-control-regex: 2 35 | no-debugger: 2 36 | no-dupe-args: 2 37 | no-dupe-keys: 2 38 | no-duplicate-case: 2 39 | no-empty-character-class: 2 40 | no-ex-assign: 2 41 | no-extra-boolean-cast: 2 42 | no-extra-parens: 0 43 | no-extra-semi: 2 44 | no-func-assign: 2 45 | no-inner-declarations: [2, functions] 46 | no-invalid-regexp: 2 47 | no-irregular-whitespace: 2 48 | no-negated-in-lhs: 2 49 | no-obj-calls: 2 50 | no-regex-spaces: 2 51 | no-sparse-arrays: 2 52 | no-unexpected-multiline: 2 53 | no-unreachable: 2 54 | use-isnan: 2 55 | valid-jsdoc: 0 56 | valid-typeof: 2 57 | 58 | # Best Practices 59 | accessor-pairs: 2 60 | block-scoped-var: 0 61 | complexity: [2, 8] 62 | consistent-return: 0 63 | curly: 0 64 | default-case: 0 65 | dot-location: 0 66 | dot-notation: 0 67 | eqeqeq: 2 68 | guard-for-in: 2 69 | no-alert: 2 70 | no-caller: 2 71 | no-case-declarations: 2 72 | no-div-regex: 2 73 | no-else-return: 0 74 | no-empty-pattern: 2 75 | no-eq-null: 2 76 | no-eval: 2 77 | no-extend-native: 2 78 | no-extra-bind: 2 79 | no-fallthrough: 2 80 | no-floating-decimal: 0 81 | no-implicit-coercion: 0 82 | no-implied-eval: 2 83 | no-invalid-this: 0 84 | no-iterator: 2 85 | no-labels: 0 86 | no-lone-blocks: 2 87 | no-loop-func: 2 88 | no-magic-number: 0 89 | no-multi-spaces: 0 90 | no-multi-str: 0 91 | no-native-reassign: 2 92 | no-new-func: 2 93 | no-new-wrappers: 2 94 | no-new: 2 95 | no-octal-escape: 2 96 | no-octal: 2 97 | no-proto: 2 98 | no-redeclare: 2 99 | no-return-assign: 2 100 | no-script-url: 2 101 | no-self-compare: 2 102 | no-sequences: 0 103 | no-throw-literal: 0 104 | no-unused-expressions: 2 105 | no-useless-call: 2 106 | no-useless-concat: 2 107 | no-void: 2 108 | no-warning-comments: 0 109 | no-with: 2 110 | radix: 2 111 | vars-on-top: 0 112 | wrap-iife: 2 113 | yoda: 0 114 | 115 | # Strict 116 | strict: 0 117 | 118 | # Variables 119 | init-declarations: 0 120 | no-catch-shadow: 2 121 | no-delete-var: 2 122 | no-label-var: 2 123 | no-shadow-restricted-names: 2 124 | no-shadow: 0 125 | no-undef-init: 2 126 | no-undef: 0 127 | no-undefined: 0 128 | no-unused-vars: 0 129 | no-use-before-define: 0 130 | 131 | # Node.js and CommonJS 132 | callback-return: 2 133 | global-require: 2 134 | handle-callback-err: 2 135 | no-mixed-requires: 0 136 | no-new-require: 0 137 | no-path-concat: 2 138 | no-process-exit: 2 139 | no-restricted-modules: 0 140 | no-sync: 0 141 | 142 | # Stylistic Issues 143 | array-bracket-spacing: 0 144 | block-spacing: 0 145 | brace-style: 0 146 | camelcase: 0 147 | comma-spacing: 0 148 | comma-style: 0 149 | computed-property-spacing: 0 150 | consistent-this: 0 151 | eol-last: 0 152 | func-names: 0 153 | func-style: 0 154 | id-length: 0 155 | id-match: 0 156 | indent: 0 157 | jsx-quotes: 0 158 | key-spacing: 0 159 | linebreak-style: 0 160 | lines-around-comment: 0 161 | max-depth: 0 162 | max-len: 0 163 | max-nested-callbacks: 0 164 | max-params: 0 165 | max-statements: [2, 30] 166 | new-cap: 0 167 | new-parens: 0 168 | newline-after-var: 0 169 | no-array-constructor: 0 170 | no-bitwise: 0 171 | no-continue: 0 172 | no-inline-comments: 0 173 | no-lonely-if: 0 174 | no-mixed-spaces-and-tabs: 0 175 | no-multiple-empty-lines: 0 176 | no-negated-condition: 0 177 | no-nested-ternary: 0 178 | no-new-object: 0 179 | no-plusplus: 0 180 | no-restricted-syntax: 0 181 | no-spaced-func: 0 182 | no-ternary: 0 183 | no-trailing-spaces: 0 184 | no-underscore-dangle: 0 185 | no-unneeded-ternary: 0 186 | object-curly-spacing: 0 187 | one-var: 0 188 | operator-assignment: 0 189 | operator-linebreak: 0 190 | padded-blocks: 0 191 | quote-props: 0 192 | quotes: 0 193 | require-jsdoc: 0 194 | semi-spacing: 0 195 | semi: 0 196 | sort-vars: 0 197 | space-after-keywords: 0 198 | space-before-blocks: 0 199 | space-before-function-paren: 0 200 | space-before-keywords: 0 201 | space-in-parens: 0 202 | space-infix-ops: 0 203 | space-return-throw-case: 0 204 | space-unary-ops: 0 205 | spaced-comment: 0 206 | wrap-regex: 0 207 | 208 | # ECMAScript 6 209 | arrow-body-style: 0 210 | arrow-parens: 0 211 | arrow-spacing: 0 212 | constructor-super: 0 213 | generator-star-spacing: 0 214 | no-arrow-condition: 0 215 | no-class-assign: 0 216 | no-const-assign: 0 217 | no-dupe-class-members: 0 218 | no-this-before-super: 0 219 | no-var: 0 220 | object-shorthand: 0 221 | prefer-arrow-callback: 0 222 | prefer-const: 0 223 | prefer-reflect: 0 224 | prefer-spread: 0 225 | prefer-template: 0 226 | require-yield: 0 227 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NativeScript Media Generator 2 | 3 | [![Greenkeeper badge](https://badges.greenkeeper.io/hypery2k/nativescript-media-generator.svg)](https://greenkeeper.io/) 4 | [![Build Status](https://travis-ci.org/hypery2k/nativescript-media-generator.svg?branch=master)](https://travis-ci.org/hypery2k/nativescript-media-generator) [![npm version](https://badge.fury.io/js/nativescript-media-generator.svg)](http://badge.fury.io/js/nativescript-media-generator) [![Dependency Status](https://david-dm.org/hypery2k/nativescript-media-generator.svg)](https://david-dm.org/hypery2k/nativescript-media-generator) [![devDependency Status](https://david-dm.org/hypery2k/nativescript-media-generator/dev-status.svg)](https://david-dm.org/hypery2k/nativescript-media-generator#info=devDependencies) 5 | 6 | [![Bountysource](https://www.bountysource.com/badge/tracker?tracker_id=11673029)](https://www.bountysource.com/trackers/11673029-hypery2k-nativescript-media-generatorerator?utm_source=11673029&utm_medium=shield&utm_campaign=TRACKER_BADGE) [![Flattr this git repo](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=mreinhardt&url=https://github.com/hypery2k/nativescript-media-generator&title=badges&language=&tags=github&category=software) 7 | 8 | > CLI Utility that generates NativeScript image assets required for images, icons, and splash screens. 9 | 10 | [![NPM](https://nodei.co/npm/nativescript-media-generator.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/nativescript-media-generator/) 11 | 12 | It requires your logo to have a solid background colour but does not distort or lose any image content so everything is at the maximum size without loss. 13 | 14 | ## Installation 15 | 16 | Need imagemagick 17 | 18 | On Debian/Ubuntu: 19 | 20 | ```bash 21 | sudo apt-get install imagemagick 22 | ``` 23 | 24 | on OS X: 25 | 26 | ```bash 27 | brew install imagemagick 28 | ``` 29 | 30 | ```bash 31 | $ npm install -g nativescript-media-generator 32 | ``` 33 | 34 | Once installed, cd to the root of your NativeScript application and run: 35 | ```bash 36 | $ tns-media-gen 37 | ``` 38 | 39 | 40 | Or if you want to use the development version (nightly build), which maybe not stable!: 41 | 42 | ``` 43 | tns plugin add nativescript-media-generator@next 44 | ``` 45 | 46 | ### Usage 47 | 48 | If using this plugin, we recommand an **1900x2850** Portrait Area as basis layout. 49 | 50 | ```bash 51 | $ tns-media-gen logo.jpg fff 52 | ``` 53 | 54 | To create the config 55 | 56 | ```bash 57 | $ tns-media-gen init 58 | ``` 59 | 60 | If you have created a `mediagen-config.json` file (see below), you can just run: 61 | ```bash 62 | $ tns-media-gen 63 | ``` 64 | 65 | This will overwrite all logos and splash screen images in the `/app` directory with the correct sizes and in the correct location for NativeScript (As at 3.5) 66 | > The recommended image or logo size is 2000px x 2000px. Its not a problem if the logo isn't square. 67 | 68 | It also creates a `/Media` directory that has images for the Apple and Android stores such as an app icon. 69 | 70 | ## Custom Assets 71 | You can create additional custom images if you need to submit to alternative app stores or have other needs that we haven't thought of yet. 72 | 73 | Simply go to your project directory and run 74 | ```bash 75 | $ tns-media-gen init 76 | ``` 77 | 78 | It will create an example file called `mediagen-config.json` which you can now edit. Add as many or few files as you need to the array. 79 | 80 | > Note: The default path for files is the `/platforms` directory, you might need to use `../` as in the example below 81 | 82 | ### Example `mediagen-config.json` 83 | ```javascript 84 | { 85 | "mediaPath": "media", 86 | "icon": { 87 | "filename": "etc/images/icon.png", 88 | "background": "fff" 89 | }, 90 | "splash": { 91 | "filename": "etc/images/splash.png", 92 | "background": "fff" 93 | }, 94 | "images": [ 95 | { 96 | "filename": "icon.png", 97 | "alias": "logo.png" 98 | }, 99 | { 100 | "filename": "icon.png", 101 | "alias": "logo_login.png" 102 | } 103 | ] 104 | } 105 | ``` 106 | 107 | ## Screenshots (Experimental) 108 | Screenshots are a new feature that use PhantomJS to quickly generate a number of the screenshot assets you need when submitting to the app store. You need your local development server running when you execute `mediagen` so it can access the NativeScript versions of the files. Of course this won't be able to access a number of things in lots of apps but hopefully it should get you up on the app store pretty quickly. 109 | This is still pretty experimental, and also please be aware that it may seem to hang for a minute or so before you see the screenshots being generated (it does need to visit the page each time!) 110 | 111 | 112 | ## Config 113 | The config variables are below: 114 | - mediaPath: path to the output folder 115 | - icon: icon image 116 | - filename: path to source filename 117 | - background: solid colour in hex 118 | - splash: splash image 119 | - filename: path to source filename 120 | - background: solid colour in hex 121 | - images: an array of images which should generate for both platforms (**NOTE**: The biggest resolutution (100%) needed is 640dpi) 122 | - filename: the output file name with extension 123 | - alias to use (target filename) 124 | - custom images: an array of custom image objects for additional media if desired 125 | - width: the width of the image in pixels 126 | - height: the height of the image in pixels 127 | - path: the directory to save the output 128 | - filename: the output file name with extension 129 | - source: the source file, same definition as an icon or splash above 130 | - filename: path to source filename 131 | - background: solid colour in hex 132 | - screenshots: An array of screenshot objects 133 | - url: url to your local development server 134 | - name: name of the page for easy reference later on 135 | -------------------------------------------------------------------------------- /lib/generate_icon_sets.js: -------------------------------------------------------------------------------- 1 | var q = require('q'), 2 | path = require('path'), 3 | fs = require('fs'), 4 | gm = require('gm').subClass({ imageMagick: true }); 5 | 6 | module.exports = function generateIconSets(filename, source, iOSPath) { /*eslint complexity: [error, 22]*/ 7 | return [ 8 | //IOS Icons 9 | { 10 | width: 180, 11 | height: 180, 12 | path: 'iOS/Assets.xcassets' + (iOSPath ? '/' + iOSPath : ''), 13 | filename: filename.replace('.', '-60@3x.'), 14 | source: source 15 | }, { 16 | width: 120, 17 | height: 120, 18 | path: 'iOS/Assets.xcassets' + (iOSPath ? '/' + iOSPath : ''), 19 | filename: filename.replace('.', '-60@2x.'), 20 | source: source 21 | }, { 22 | width: 40, 23 | height: 40, 24 | path: 'iOS/Assets.xcassets' + (iOSPath ? '/' + iOSPath : ''), 25 | filename: filename.replace('.', '-40.'), 26 | source: source 27 | }, { 28 | width: 80, 29 | height: 80, 30 | path: 'iOS/Assets.xcassets' + (iOSPath ? '/' + iOSPath : ''), 31 | filename: filename.replace('.', '-40@2x.'), 32 | source: source 33 | }, { 34 | width: 120, 35 | height: 120, 36 | path: 'iOS/Assets.xcassets' + (iOSPath ? '/' + iOSPath : ''), 37 | filename: filename.replace('.', '-40@3x.'), 38 | source: source 39 | }, { 40 | width: 50, 41 | height: 50, 42 | path: 'iOS/Assets.xcassets' + (iOSPath ? '/' + iOSPath : ''), 43 | filename: filename.replace('.', '-50.'), 44 | source: source 45 | }, { 46 | width: 100, 47 | height: 100, 48 | path: 'iOS/Assets.xcassets' + (iOSPath ? '/' + iOSPath : ''), 49 | filename: filename.replace('.', '-50@2x.'), 50 | source: source 51 | }, { 52 | width: 60, 53 | height: 60, 54 | path: 'iOS/Assets.xcassets' + (iOSPath ? '/' + iOSPath : ''), 55 | filename: filename.replace('.', '-60.'), 56 | source: source 57 | }, { 58 | width: 72, 59 | height: 72, 60 | path: 'iOS/Assets.xcassets' + (iOSPath ? '/' + iOSPath : ''), 61 | filename: filename.replace('.', '-72.'), 62 | source: source 63 | }, { 64 | width: 144, 65 | height: 144, 66 | path: 'iOS/Assets.xcassets' + (iOSPath ? '/' + iOSPath : ''), 67 | filename: filename.replace('.', '-72@2x.'), 68 | source: source 69 | }, { 70 | width: 76, 71 | height: 76, 72 | path: 'iOS/Assets.xcassets' + (iOSPath ? '/' + iOSPath : ''), 73 | filename: filename.replace('.', '-76.'), 74 | source: source 75 | }, { 76 | width: 152, 77 | height: 152, 78 | path: 'iOS/Assets.xcassets' + (iOSPath ? '/' + iOSPath : ''), 79 | filename: filename.replace('.', '-76@2x.'), 80 | source: source 81 | }, { 82 | width: 167, 83 | height: 167, 84 | path: 'iOS/Assets.xcassets' + (iOSPath ? '/' + iOSPath : ''), 85 | filename: filename.replace('.', '-83.5@2x.'), 86 | source: source 87 | }, { 88 | width: 120, 89 | height: 120, 90 | path: 'iOS/Assets.xcassets' + (iOSPath ? '/' + iOSPath : ''), 91 | filename: filename.replace('.', '-120.'), 92 | source: source 93 | }, { 94 | width: 29, 95 | height: 29, 96 | path: 'iOS/Assets.xcassets' + (iOSPath ? '/' + iOSPath : ''), 97 | filename: filename.replace('.', '-29.'), 98 | source: source 99 | }, { 100 | width: 58, 101 | height: 58, 102 | path: 'iOS/Assets.xcassets' + (iOSPath ? '/' + iOSPath : ''), 103 | filename: filename.replace('.', '-29@2x.'), 104 | source: source 105 | }, { 106 | width: 87, 107 | height: 87, 108 | path: 'iOS/Assets.xcassets' + (iOSPath ? '/' + iOSPath : ''), 109 | filename: filename.replace('.', '-29@3x.'), 110 | source: source 111 | }, { 112 | width: 57, 113 | height: 57, 114 | path: 'iOS/Assets.xcassets' + (iOSPath ? '/' + iOSPath : ''), 115 | filename: filename.replace('.', '-57.'), 116 | source: source 117 | }, { 118 | width: 114, 119 | height: 114, 120 | path: 'iOS/Assets.xcassets' + (iOSPath ? '/' + iOSPath : ''), 121 | filename: filename.replace('.', '-57@2x.'), 122 | source: source 123 | }, 124 | 125 | //Android 126 | { 127 | width: 72, 128 | height: 72, 129 | path: "Android/drawable-hdpi", 130 | filename: filename, 131 | source: source 132 | }, { 133 | width: 48, 134 | height: 48, 135 | path: "Android/drawable-mdpi", 136 | filename: filename, 137 | source: source 138 | }, { 139 | width: 96, 140 | height: 96, 141 | path: "Android/drawable-xhdpi", 142 | filename: filename, 143 | source: source 144 | }, { 145 | width: 144, 146 | height: 144, 147 | path: "Android/drawable-xxhdpi", 148 | filename: filename, 149 | source: source 150 | }, { 151 | width: 345, 152 | height: 345, 153 | path: "Android/drawable-xxxhdpi", 154 | filename: filename, 155 | source: source 156 | } 157 | 158 | ] 159 | } -------------------------------------------------------------------------------- /screenshots.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var phantom = require('phantom'), 4 | path = require('path'), 5 | q = require('q'), 6 | mkdirp = require('mkdirp'), 7 | async = require('async'), 8 | pkg = require('./package.json'), 9 | config = require(process.cwd() + "/mediagen-config"); 10 | 11 | var ph; 12 | var mediaPath = config.mediaPath || 'Media'; 13 | 14 | function evaluate(page, func) { 15 | var args = [].slice.call(arguments, 2); 16 | var fn = "function() { return (" + func.toString() + ").apply(this, " + JSON.stringify(args) + ");}"; 17 | return page.evaluate(fn); 18 | } 19 | 20 | var output = { 21 | generate: function (url, width, height, devicePixelRatio, savePath, saveFilename) { 22 | var deferred = q.defer(); 23 | 24 | if (!url || !width || !height || !savePath || !saveFilename) { 25 | deferred.reject({ 26 | error: "A required argument is missing" 27 | }); 28 | return deferred.promise; 29 | } 30 | if (!ph) { 31 | phantom.create(function (newPh) { 32 | ph = newPh; 33 | doGen(); 34 | }) 35 | } else { 36 | doGen(); 37 | } 38 | 39 | function doGen() { 40 | ph.createPage(function (page) { 41 | page.setViewportSize(width, height, function () { 42 | page.open(url, function () { 43 | if (devicePixelRatio && devicePixelRatio !== 1) { 44 | evaluate(page, function (devicePixelRatio) { 45 | document.body.style.webkitTransform = "scale(" + devicePixelRatio + ")"; 46 | document.body.style.webkitTransformOrigin = "0% 0%"; 47 | document.body.style.width = (100 / devicePixelRatio) + "%"; 48 | }, devicePixelRatio); 49 | } 50 | mkdirp(path.resolve(__dirname, savePath), function (err) { 51 | if (err) { 52 | deferred.reject(err); 53 | } 54 | page.render(savePath + "/" + saveFilename, { 55 | format: 'jpg', 56 | quality: '60' 57 | }, function (err) { 58 | console.log("Generated screenshot", url, savePath, saveFilename); 59 | if (err) { 60 | deferred.reject(err); 61 | } else { 62 | deferred.resolve({ 63 | success: true 64 | }); 65 | } 66 | page.close(); 67 | }); 68 | }); 69 | }); 70 | }); 71 | }); 72 | } 73 | 74 | return deferred.promise; 75 | }, 76 | generateAll: function () { 77 | var deferred = q.defer(); 78 | async.eachLimit(output.screenshots, 1, function (item, cb1) { 79 | async.eachLimit(output.pages, 1, function (page, cb2) { 80 | output.generate(page.url, item.width, item.height, item.devicePixelRatio, item.savePath, item.filename + page.name + ".jpg") 81 | .then(function (result) { 82 | cb2(); 83 | }) 84 | .catch(cb2) 85 | }, function (err) { 86 | cb1(err); 87 | }) 88 | }, function (err) { 89 | if (ph) ph.exit(); 90 | if (err) { 91 | deferred.reject(err); 92 | } else { 93 | deferred.resolve(true); 94 | } 95 | }); 96 | return deferred.promise; 97 | }, 98 | pages: [], 99 | screenshots: [{ 100 | filename: "android-10in-1280x720-land", 101 | width: 1280, 102 | height: 720, 103 | savePath: mediaPath + '/android/screenshots/10in' 104 | }, { 105 | filename: "android-10in-2048x1152-land", 106 | width: 2048, 107 | height: 1152, 108 | savePath: mediaPath + '/android/screenshots/10in' 109 | }, { 110 | filename: "android-7in-1280x800-land", 111 | width: 1280, 112 | height: 800, 113 | savePath: mediaPath + '/android/screenshots/7in' 114 | }, { 115 | filename: "android-10in-1280x720-port", 116 | width: 720, 117 | height: 1280, 118 | savePath: mediaPath + '/android/screenshots/10in' 119 | }, { 120 | filename: "android-10in-2048x1152-port", 121 | width: 1152, 122 | height: 2048, 123 | savePath: mediaPath + '/android/screenshots/10in' 124 | }, { 125 | filename: "android-7in-1280x800-port", 126 | width: 800, 127 | height: 1280, 128 | savePath: mediaPath + '/android/screenshots/7in' 129 | }, { 130 | filename: "android-4in-1280x720-land", 131 | width: 1920, 132 | height: 1080, 133 | savePath: mediaPath + '/android/screenshots/4in' 134 | }, { 135 | filename: "android-4in-1280x720-port", 136 | width: 1080, 137 | height: 1920, 138 | savePath: mediaPath + '/android/screenshots/4in' 139 | }, { 140 | filename: "ipad-1024x768-land", 141 | width: 1024, 142 | height: 768, 143 | savePath: mediaPath + '/ios/screenshots/ipad' 144 | }, { 145 | filename: "ipadretina-2048x1536-land", 146 | width: 2048, 147 | height: 1536, 148 | devicePixelRatio: 2, 149 | savePath: mediaPath + '/ios/screenshots/ipadRetina' 150 | }, { 151 | filename: "iphone4-960x640-land", 152 | width: 960, 153 | height: 640, 154 | savePath: mediaPath + '/ios/screenshots/iphone4' 155 | }, { 156 | filename: "iphone5-1136x640-land", 157 | width: 1136, 158 | height: 640, 159 | savePath: mediaPath + '/ios/screenshots/iphone5' 160 | }, { 161 | filename: "ipad-1024x768-port", 162 | width: 768, 163 | height: 1024, 164 | savePath: mediaPath + '/ios/screenshots/ipad' 165 | }, { 166 | filename: "ipadretina-2048x1536-port", 167 | width: 1536, 168 | height: 2048, 169 | devicePixelRatio: 2, 170 | savePath: mediaPath + '/ios/screenshots/ipadRetina' 171 | }, { 172 | filename: "iphone4-640x960-port", 173 | width: 640, 174 | height: 960, 175 | savePath: mediaPath + '/ios/screenshots/iphone4' 176 | }, { 177 | filename: "iphone5-640x1136-port", 178 | width: 640, 179 | height: 1136, 180 | savePath: mediaPath + '/ios/screenshots/iphone5' 181 | }, { 182 | filename: "iphone6p-1242x2208-port", 183 | width: 1242, 184 | height: 2208, 185 | savePath: mediaPath + '/ios/screenshots/iphone6p' 186 | }, { 187 | filename: "iphone6p-2208x1242-land", 188 | width: 2208, 189 | height: 1242, 190 | savePath: mediaPath + '/ios/screenshots/iphone6p' 191 | }, { 192 | filename: "iphone6-1334x750-land", 193 | width: 1334, 194 | height: 750, 195 | savePath: mediaPath + '/ios/screenshots/iphone6' 196 | }, { 197 | filename: "iphone6-750x1334-port", 198 | width: 750, 199 | height: 1334, 200 | savePath: mediaPath + '/ios/screenshots/iphone6' 201 | }] 202 | }; 203 | module.exports = output; 204 | -------------------------------------------------------------------------------- /tns-media-gen.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | "use strict"; 4 | 5 | var gm = require('gm').subClass({ imageMagick: true }), 6 | mkdirp = require('mkdirp'), 7 | path = require('path'), 8 | fs = require('fs'), 9 | screenshots = require('./screenshots'), 10 | q = require('q'), 11 | resize = require('./lib/resize'), 12 | generateImageSets = require('./lib/generate_image_sets'), 13 | generateIconSets = require('./lib/generate_icon_sets'), 14 | storeImagesGenerator = require('./lib/store_images'), 15 | splashImagesGenerator = require('./lib/splash_images'), 16 | config, 17 | mediaPath; 18 | 19 | 20 | function generate() { 21 | var deferred = q.defer(); 22 | 23 | fs.readdir(path.join(process.cwd(), "app", "App_Resources", "iOS"), function(err, result) { /*eslint complexity: [error, 55]*/ 24 | 25 | if (err) { 26 | throw err; 27 | } else { 28 | 29 | if (!config && config.image) config.image = { 30 | filename: "./icon.png" 31 | }; 32 | 33 | var storeImages = storeImagesGenerator(config, mediaPath), 34 | splashImages = splashImagesGenerator(config, mediaPath), 35 | source = process.argv[2] || config.icon || config.image, 36 | images = generateIconSets('icon-20.png', source, 'AppIcon.appiconset') 37 | 38 | images.push({ 39 | width: 1024, 40 | height: 1024, 41 | path: 'iOS/Assets.xcassets/AppIcon.appiconset', 42 | filename: "icon-1024.png", 43 | source: source 44 | }); 45 | images.push({ 46 | width: 180, 47 | height: 180, 48 | path: 'iOS/Assets.xcassets/AppIcon.appiconset', 49 | filename: "icon-60@3x.png", 50 | source: source 51 | }); 52 | 53 | images = images.concat(splashImages); 54 | 55 | if (config.screenshots && config.screenshots.length) { 56 | screenshots.pages = config.screenshots; 57 | } 58 | 59 | 60 | if (config.images) { 61 | config.images.forEach(function(item) { 62 | var additionalImages = generateImageSets(item.alias ? item.alias : item.filename, { filename: item.filename }); 63 | additionalImages.forEach(function(additionalImage) { 64 | images.push(additionalImage); 65 | }); 66 | }); 67 | } 68 | 69 | if (config.customImages) { 70 | config.customImages.forEach(function(item) { 71 | images.push(item); 72 | }); 73 | } 74 | 75 | 76 | if (!process.argv[2] && !config.image && (!config.icon && !config.splash)) { 77 | console.log('You must specify a filename as the second argument, or in a config file'); 78 | } else if (!process.argv[3] && !config.background && (!config.icon && !config.splash)) { 79 | console.log("Please specify a background colour in hex values as the third argument, or in a config file"); 80 | } else { 81 | 82 | var totalImages = images.length + (screenshots.pages.length * screenshots.screenshots.length); 83 | console.log("------------------------------"); 84 | console.log(" nativescript-media-generator"); 85 | console.log("------------------------------"); 86 | console.log("Generating " + totalImages + " images so you don't have to"); 87 | console.log("------------------------------"); 88 | screenshots.generateAll(); 89 | images.forEach(function(image) { 90 | var background, sourceImage; 91 | if (process.argv[2]) { 92 | /* 93 | Legacy Configuration 94 | */ 95 | if (process.argv[3]) { 96 | background = process.argv[3]; 97 | } else { 98 | background = config.background; 99 | } 100 | if (process.argv[2]) { 101 | sourceImage = process.argv[2]; 102 | } else { 103 | sourceImage = config.image; 104 | } 105 | } else { 106 | if (image.source) { 107 | sourceImage = image.source.filename; 108 | background = image.source.background; 109 | } 110 | } 111 | if (sourceImage) { 112 | resize(image.width, image.height, '#' + background, sourceImage, image.filename, image.path); 113 | 114 | } 115 | }); 116 | deferred.resolve(); 117 | } 118 | } 119 | }); 120 | return deferred.promise; 121 | } 122 | 123 | function genConfig() { 124 | var deferred = q.defer(); 125 | var destFile = path.join(process.cwd(), "mediagen-config.json"); 126 | 127 | fs.writeFile(destFile, JSON.stringify({ 128 | "icon": { "filename": "icon.png", "background": "fff" }, 129 | "splash": { "filename": "splash.png", "background": "fff" }, 130 | "customImages": [{ 131 | "width": 120, 132 | "height": 120, 133 | "path": "../../" + mediaPath + "/custom", 134 | "filename": "outputFilename.png", 135 | "source": { "filename": "image.png", "background": "fff" } 136 | }], 137 | "screenshots": [{ "url": "http://www.google.com", "name": "homepage" }] 138 | }, null, 4), function() { 139 | deferred.resolve("success"); 140 | }); 141 | console.log("Created `mediagen-config.json` file in the current directory."); 142 | return deferred.promise; 143 | } 144 | 145 | try { /*eslint global-require: off*/ 146 | config = require(process.cwd() + "/mediagen-config"); 147 | mediaPath = config.mediaPath || 'Media'; 148 | } catch (e) { 149 | if (process.argv[2] !== "init") { 150 | console.log("Could not find configuration file. To create one run `$ mediagen init`"); 151 | } 152 | } 153 | 154 | switch (process.argv[2]) { 155 | case "init": 156 | genConfig(); 157 | break; 158 | default: 159 | generate(); 160 | } 161 | 162 | module.exports = { 163 | __resize: resize, 164 | __generate: generate, 165 | __genConfig: genConfig 166 | }; --------------------------------------------------------------------------------