|
| 1 | +// |
| 2 | +// Notes Sametime Emojis |
| 3 | +// main.js |
| 4 | +// |
| 5 | +// (C) Nils Weber | nilsweb |
| 6 | +// Code licensed under MIT |
| 7 | +// |
| 8 | + |
| 9 | + |
| 10 | +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 11 | + |
| 12 | + |
| 13 | +const fs = require('fs') |
| 14 | +const path = require('path') |
| 15 | + |
| 16 | +const sharp = require('sharp') |
| 17 | +const xml = require('xml') |
| 18 | +const emojiData = require('emoji-datasource') |
| 19 | +const archiver = require('archiver') |
| 20 | + |
| 21 | +const lib = require('./lib') |
| 22 | + |
| 23 | + |
| 24 | +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 25 | + |
| 26 | + |
| 27 | +// Target Configuration. |
| 28 | +const targetConfiguration = { |
| 29 | + folder: path.join(__dirname, 'build'), |
| 30 | + targets: [ |
| 31 | + { set: 'apple', size: 16 }, |
| 32 | + { set: 'google', size: 16 }, |
| 33 | + { set: 'twitter', size: 16 }, |
| 34 | + { set: 'facebook', size: 16 }, |
| 35 | + { set: 'apple', size: 32 }, |
| 36 | + { set: 'google', size: 32 }, |
| 37 | + { set: 'twitter', size: 32 }, |
| 38 | + { set: 'facebook', size: 32 }, |
| 39 | + ], |
| 40 | +} |
| 41 | + |
| 42 | +// Source Configuration. |
| 43 | +const sourceConfiguration = { |
| 44 | + getPathForTarget: target => path.join(__dirname, 'node_modules/emoji-datasource/img', target, 'sheets/32.png'), |
| 45 | + emojiSize: 32, |
| 46 | + emojiPadding: 1, |
| 47 | + spritesheetCache: { } |
| 48 | +} |
| 49 | + |
| 50 | + |
| 51 | +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 52 | + |
| 53 | + |
| 54 | +// Get all emojis with name. |
| 55 | +const emojis = emojiData.filter(emoji => emoji.name !== null && emoji.category !== 'Skin Tones') |
| 56 | + |
| 57 | +// Group emojis by category. |
| 58 | +const categorizedEmojis = Array.from(new Set(emojis.map(emoji => emoji.category))) |
| 59 | + .map(category => ({ name: category, emojis: emojis.filter(emoji => emoji.category === category) })) |
| 60 | + .map(category => { |
| 61 | + const skinVariations = category.emojis |
| 62 | + .filter(emoji => emoji.skin_variations) |
| 63 | + .map(emoji => Object.values(emoji.skin_variations).map(skinVariation => ({ ...skinVariation, category: category.name, name: emoji.name }))) |
| 64 | + .reduce((acc, current) => acc.concat(current), []) |
| 65 | + category.emojis.push(...skinVariations) |
| 66 | + return category |
| 67 | + }) |
| 68 | + |
| 69 | +// Create build directory if it doesn't exist. |
| 70 | +if (!fs.existsSync(targetConfiguration.folder)) fs.mkdirSync(targetConfiguration.folder) |
| 71 | + |
| 72 | + |
| 73 | +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 74 | + |
| 75 | + |
| 76 | +// Process each target. |
| 77 | +targetConfiguration.targets.forEach(target => { |
| 78 | + |
| 79 | + // Create target path (where the finished emojis will be saved). |
| 80 | + const targetPath = path.join(targetConfiguration.folder, `${target.set}_${target.size}px`) |
| 81 | + |
| 82 | + // Delete output directory, if it exists. |
| 83 | + lib.deleteDirRecursive(targetPath) |
| 84 | + |
| 85 | + // Create target build directory. |
| 86 | + fs.mkdirSync(targetPath) |
| 87 | + |
| 88 | + // Load spritesheet for current target (if it hasn't been loeaded before) and cache it. |
| 89 | + if (!sourceConfiguration.spritesheetCache[target.set]) |
| 90 | + sourceConfiguration.spritesheetCache[target.set] = sharp(sourceConfiguration.getPathForTarget(target.set)) |
| 91 | + |
| 92 | + // Get spritesheet for current target. |
| 93 | + const spritesheet = sourceConfiguration.spritesheetCache[target.set] |
| 94 | + |
| 95 | + // Generate individual emoji images. |
| 96 | + categorizedEmojis.forEach(async (category) => { |
| 97 | + |
| 98 | + // Create target archive and write stream. |
| 99 | + const categoryArchive = archiver('zip') |
| 100 | + categoryArchive.pipe(fs.createWriteStream(path.join(targetPath, `${category.name}.zip`))) |
| 101 | + |
| 102 | + // Process all emojis in this category. |
| 103 | + const emojiBufferPromises = category.emojis.map(async (emoji) => { |
| 104 | + |
| 105 | + // Calcualte the actual size per emoji (relative to the entire spritesheet). |
| 106 | + const emojiOffset = sourceConfiguration.emojiSize + (sourceConfiguration.emojiPadding * 2) |
| 107 | + |
| 108 | + // Get the image buffer for the current emoji. |
| 109 | + const buffer = await spritesheet |
| 110 | + .clone() |
| 111 | + .extract({ |
| 112 | + left: emoji.sheet_x * emojiOffset + sourceConfiguration.emojiPadding, |
| 113 | + top: emoji.sheet_y * emojiOffset + sourceConfiguration.emojiPadding, |
| 114 | + width: sourceConfiguration.emojiSize, |
| 115 | + height: sourceConfiguration.emojiSize, |
| 116 | + }) |
| 117 | + .resize(target.size) |
| 118 | + .png() |
| 119 | + .toBuffer() |
| 120 | + |
| 121 | + return { name: emoji.image, buffer } |
| 122 | + }) |
| 123 | + |
| 124 | + // Add all emoji buffers to the archive. |
| 125 | + const emojiBuffers = await Promise.all(emojiBufferPromises) |
| 126 | + emojiBuffers.forEach(({ buffer, name }) => categoryArchive.append(buffer, { name })) |
| 127 | + |
| 128 | + // Generate XML files for palettes. |
| 129 | + const xmlObject = {} |
| 130 | + xmlObject.palette = category.emojis.map(emoji => ({ |
| 131 | + item: [ |
| 132 | + { name: { _cdata: emoji.name } }, |
| 133 | + { imgfile: emoji.image }, |
| 134 | + { imgWidth: targetConfiguration.emojiSize }, |
| 135 | + { imgHeight: targetConfiguration.emojiSize }, |
| 136 | + { alttext: { _cdata: emoji.name } }, |
| 137 | + { keyboard: emoji.short_name ? { _cdata: `:${emoji.short_name}:` } : undefined }, |
| 138 | + ] |
| 139 | + })) |
| 140 | + |
| 141 | + // Generate XML string and apoend it to the archive. |
| 142 | + const xmlString = xml(xmlObject, { declaration: true }) |
| 143 | + categoryArchive.append(xmlString, { name: 'palette.xml' }) |
| 144 | + |
| 145 | + // FInalize the archive |
| 146 | + categoryArchive.finalize() |
| 147 | + }) |
| 148 | +}) |
| 149 | + |
| 150 | + |
| 151 | +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 152 | + |
0 commit comments