UNPKG

appiconkit

Version:

An app icon/image assets generator for Apple platforms and favicon for web.

315 lines (314 loc) 17.2 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.IconGenerator = void 0; var fs_1 = __importDefault(require("fs")); var path_1 = __importDefault(require("path")); var jimp_1 = __importDefault(require("jimp")); /** * Generator class for iconset/imageset. */ var IconGenerator = /** @class */ (function () { /** * Constructs an IconGenerator object. */ function IconGenerator() { // no op } /** * Generates .appiconset with a given image. Image must be larger than 1024x1024. * @param inputPath path of input image * @param outputPath path of output app icon * @param platform platform of app icon (ios | watchos | macos) */ IconGenerator.prototype.generateIconSet = function (inputPath, outputPath, platform) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, this.generateImageAssets(inputPath, outputPath, { type: 'icon', platform: platform !== null && platform !== void 0 ? platform : 'ios', })]; }); }); }; /** * Generates .imageset with a given image. * Parameters width and height are for generated @1x image. If both weight and * height are given, the image will be resized to exactly weight and height. If * one of width and height is given, it will automatically choose the other * dimension for the resized images. If none of them is given, the original image * is used as @3x. * @param inputPath path of input image * @param outputPath path of output image set * @param width @1x width of the output image * @param height @1x height of the output image */ IconGenerator.prototype.generateImageSet = function (inputPath, outputPath, width, height) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, this.generateImageAssets(inputPath, outputPath, { type: 'icon', platform: 'ios', width: width !== null && width !== void 0 ? width : jimp_1.default.AUTO, height: height !== null && height !== void 0 ? height : jimp_1.default.AUTO, })]; }); }); }; /** * Generates .appiconset or .imageset or favicon with a given image. * @param inputPath path of input image * @param outputPath path of output images * @param generatorOption options for image generator */ IconGenerator.prototype.generateImageAssets = function (inputPath, outputPath, generatorOption) { var _a, _b, _c; return __awaiter(this, void 0, void 0, function () { var options, type, platform, programWidth, programHeight, programFormat, fileName, webOutputPath, platformMap, pathDir, config, image, output, contents, resizeOptions, _i, _d, imageConfig, width, height, scale, fileNameSuffix, sizes, baseName, extName, newFileName, resizeOption; return __generator(this, function (_e) { switch (_e.label) { case 0: options = generatorOption !== null && generatorOption !== void 0 ? generatorOption : { type: 'icon', platform: 'ios', }; type = options.type; platform = options.platform; programWidth = (_a = options.width) !== null && _a !== void 0 ? _a : jimp_1.default.AUTO; programHeight = (_b = options.height) !== null && _b !== void 0 ? _b : jimp_1.default.AUTO; programFormat = (_c = options.format) !== null && _c !== void 0 ? _c : 'png'; fileName = path_1.default.basename(inputPath).split('.')[0]; if (options.platform === 'web') { webOutputPath = path_1.default.join(outputPath, fileName); return [2 /*return*/, this.generateWebIconImages(inputPath, webOutputPath)]; } platformMap = { 'ios': 'ios', 'iphone': 'ios', 'ipad': 'ios', 'watchos': 'watchos', 'watch': 'watchos', 'macos': 'macos', 'mac': 'macos', 'web': 'web', }; pathDir = type === 'icon' ? platformMap[platform] : 'imageset'; config = require("../config/" + pathDir + "/Contents.json"); if (!config) { return [2 /*return*/, Promise.reject(new Error('Error: Config file does not exist.'))]; } return [4 /*yield*/, jimp_1.default.read(inputPath)]; case 1: image = _e.sent(); if (!image) { return [2 /*return*/, Promise.reject(new Error('Error: Invalid input image.'))]; } if (type === 'icon' && (image.getWidth() < 1024 || image.getHeight() < 1024)) { console.warn('Warning: Icon size must be greater than 1024x1024.'); } if (type === 'icon' || type === 'iconset') { output = path_1.default.join(outputPath, 'AppIcon.appiconset'); } else { output = path_1.default.join(outputPath, fileName + ".imageset"); } if (!this.createDirectoryIfNeeded(output)) { return [2 /*return*/, Promise.reject(new Error("Error: cannot create path " + output + "."))]; } contents = config; resizeOptions = new Array(); for (_i = 0, _d = contents.images; _i < _d.length; _i++) { imageConfig = _d[_i]; width = void 0; height = void 0; scale = Number(imageConfig.scale.slice(0, -1)); fileNameSuffix = void 0; if (type === 'icon' || type === 'iconset') { sizes = imageConfig.size.split('x'); width = Number(sizes[0]) * scale; height = Number(sizes[1]) * scale; fileNameSuffix = "-" + width; } else { if (programHeight === jimp_1.default.AUTO && programWidth === jimp_1.default.AUTO) { programWidth = image.bitmap.width / 3; } width = (programWidth === jimp_1.default.AUTO) ? jimp_1.default.AUTO : programWidth * scale; height = (programHeight === jimp_1.default.AUTO) ? jimp_1.default.AUTO : programHeight * scale; fileNameSuffix = "@" + scale + "x"; } baseName = path_1.default.basename(inputPath).split('.')[0]; extName = path_1.default.extname(inputPath); if (programFormat !== 'default') { extName = "." + programFormat; } newFileName = "" + baseName + fileNameSuffix; resizeOption = { width: width, height: height, filename: newFileName, format: programFormat, }; imageConfig['filename'] = "" + newFileName + extName; resizeOptions.push(resizeOption); } fs_1.default.writeFileSync(path_1.default.join(output, 'Contents.json'), JSON.stringify(contents, null, 2)); return [2 /*return*/, this.generateImages(inputPath, output, resizeOptions)]; } }); }); }; /** * Generate web manifest icon images. * @param inputPath path of input image. * @param outputPath path of output image. */ IconGenerator.prototype.generateWebIconImages = function (inputPath, outputPath) { return __awaiter(this, void 0, void 0, function () { var config, htmlCode, options, _i, _a, appleIconDimension, _b, _c, favIconDimension, codePath; return __generator(this, function (_d) { config = require('../config/web/config.json'); if (!config) { return [2 /*return*/, Promise.reject(new Error('Error: Config file does not exist.'))]; } htmlCode = ''; options = new Array(); for (_i = 0, _a = config.appleIconDimensions; _i < _a.length; _i++) { appleIconDimension = _a[_i]; options.push({ width: appleIconDimension, height: appleIconDimension, filename: "apple-touch-icon-" + appleIconDimension + "x" + appleIconDimension, format: 'png', }); htmlCode = htmlCode.concat("<link rel=\"apple-touch-icon-precomposed\" sizes=\"" + appleIconDimension + "x" + appleIconDimension + "\" href=\"apple-touch-icon-" + appleIconDimension + "x" + appleIconDimension + ".png\" />\n"); } for (_b = 0, _c = config.favIconDimensions; _b < _c.length; _b++) { favIconDimension = _c[_b]; options.push({ width: favIconDimension, height: favIconDimension, filename: "favicon-" + favIconDimension + "x" + favIconDimension, format: 'png', }); htmlCode = htmlCode.concat("<link rel=\"icon\" type=\"image/png\" href=\"favicon-" + favIconDimension + "x" + favIconDimension + ".png\" sizes=\"" + favIconDimension + "x" + favIconDimension + "\" />\n"); } if (!this.createDirectoryIfNeeded(outputPath)) { return [2 /*return*/, Promise.reject(new Error("Error: cannot create directory at output path " + outputPath + "."))]; } codePath = path_1.default.join(outputPath, 'code.txt'); fs_1.default.writeFileSync(codePath, htmlCode); console.log("Generated HTML code for favicons is saved to " + codePath); return [2 /*return*/, this.generateImages(inputPath, outputPath, options)]; }); }); }; /** * Generate resized images with generic options. * @param inputPath path of input image. * @param outputPath path of output image. * @param options generic options for resizing images. */ IconGenerator.prototype.generateImages = function (inputPath, outputPath, options) { var _a; return __awaiter(this, void 0, void 0, function () { var image, extName, _i, options_1, option, newFileName, newExtName, newPath; return __generator(this, function (_b) { switch (_b.label) { case 0: return [4 /*yield*/, jimp_1.default.read(inputPath)]; case 1: image = _b.sent(); if (!image) { return [2 /*return*/, Promise.reject(new Error('Error: Invalid input image.'))]; } if (!this.createDirectoryIfNeeded(outputPath)) { return [2 /*return*/, Promise.reject(new Error("Error: Cannot create directory at output path " + outputPath + "."))]; } extName = path_1.default.extname(inputPath); _i = 0, options_1 = options; _b.label = 2; case 2: if (!(_i < options_1.length)) return [3 /*break*/, 5]; option = options_1[_i]; newFileName = void 0; newExtName = void 0; if (option.format === 'default') { newExtName = extName; } else { newExtName = "." + ((_a = option.format) !== null && _a !== void 0 ? _a : 'png'); } if (option.filename !== undefined) { newFileName = "" + option.filename + newExtName; } else { newFileName = "Icon-" + option.width + newExtName; } newPath = path_1.default.join(outputPath, newFileName); return [4 /*yield*/, image.clone().resize(option.width, option.height).writeAsync(newPath)]; case 3: _b.sent(); _b.label = 4; case 4: _i++; return [3 /*break*/, 2]; case 5: return [2 /*return*/]; } }); }); }; IconGenerator.prototype.createDirectoryIfNeeded = function (path) { if (!fs_1.default.existsSync(path)) { try { fs_1.default.mkdirSync(path); } catch (error) { return false; } } if (!fs_1.default.lstatSync(path).isDirectory()) { return false; } return true; }; return IconGenerator; }()); exports.IconGenerator = IconGenerator;