/** * Generates a sprite sheet and emoji JSON file for use by the plugin. * Takes one argument, the root directory of your emoji files. * This makes a few assumptions: * - All emojis are PNG files. * - All emojis are the same size. * - All emojis are static. * - No filenames have spaces. */ import { exec as nodeExec } from "node:child_process"; import { argv } from "node:process"; import { promisify } from "node:util"; import * as fs from "node:fs"; import { join as joinPath } from "node:path"; const writeFile = promisify(fs.writeFile); const readDir = promisify(fs.readdir); const exec = promisify(nodeExec); let directory = "."; let outname = "spritesheet"; let scale = 0; if (argv.length < 2) { throw "Not enough arguments, need a folder to process"; } if (argv[0].indexOf("node") !== -1 && argv[1].indexOf("generate.js") !== -1) { if (argv.length < 3) throw "Not enough arguments, need a folder to process"; directory = argv[2]; if (argv.length > 3) outname = argv[3]; if (argv.length > 4) scale = parseInt(argv[4]); } else { directory = argv[1]; if (argv.length > 2) outname = argv[2]; if (argv.length > 3) scale = parseInt(argv[3]); } async function getFiles() { return (await readDir(directory)) .filter((f) => f.endsWith(".png")) .sort((a, b) => a.localeCompare(b)); } console.log(`Directory: ${directory} Output files: ${outname}.png, ${outname}.json Scale: ${scale}`); const files = await getFiles(); const { size } = await createSpriteSheet(files); await createJSON(files, size); /** * @param {string[]} files */ async function createSpriteSheet(files) { const size = Math.ceil(Math.sqrt(files.length)); // i don't wanna pull in any more dependencies, so lol if (!scale) { const { stdout: rawImageSize } = await exec( [ "magick", "identify", "-ping", "-format", "'%w'", joinPath(directory, files[0]), ].join(" ") ); scale = parseInt(rawImageSize); } const { stdout, stderr } = await exec( [ "magick", "montage", "-tile", `${size}x${size}`, "-geometry", "+0+0", "-scale", `${scale}`, "-background", "transparent", ...files.map((f) => joinPath(directory, f)), `${outname}.png`, ].join(" ") ); if (stderr) { console.error("Error from ImageMagick:", stderr); return; } if (stdout) console.log(stdout); const { stdout2, stderr2 } = await exec( [ "magick", "montage", "-tile", `${size}x${size}`, "-geometry", "+0+0", "-scale", `${scale * 2}`, "-background", "transparent", ...files.map((f) => joinPath(directory, f)), `${outname}_2x.png`, ].join(" ") ); if (stderr2) { console.error("Error from ImageMagick:", stderr2); return; } if (stdout2) console.log(stdout2); return { size }; } /** * @param {string[]} files The image filenames. * @param {number} size The size of the sprite sheet in number of sprites horizontally and vertically. */ async function createJSON(files, size) { let json = { source: `${outname}.png`, hidpiSource: `${outname}_2x.png`, size: scale, emojis: {}, }; files.forEach((filename, index) => { const shortcode = filename.split(".", 2)[0]; const xPos = Math.floor(index % size); const yPos = Math.floor(index / size); json.emojis[shortcode] = [xPos * scale, yPos * scale]; }); await writeFile(`${outname}.json`, JSON.stringify(json, undefined, " ")); }