/** * Copyright (c) 2019-present Tomek Wójcik * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ const crypto = require('crypto'); const fs = require('fs'); const path = require('path'); const glob = require('glob'); const Mustache = require('mustache'); const {PNG} = require('pngjs'); const URI = require('urijs'); const DEFAULT_OPTIONS = { files: [], output: 'sprite/', urlPrefix: '/' }; const spritemaker = (options) => { options = Object.assign({}, DEFAULT_OPTIONS, options); const images = []; const bounds = { width: 0, height: 0 }; const spriteMap = []; options.files.forEach((pattern, index) => { glob.sync(pattern).forEach((imagePath, index) => { const data = fs.readFileSync(imagePath); const image = PNG.sync.read(data); bounds.height += image.height; if (image.width > bounds.width) { bounds.width = image.width; } images.push([imagePath, image]); }); }); const outputImage = new PNG({ height: bounds.height, width: bounds.width }); let currentDeltaY = 0; images.forEach(([imagePath, image], index) => { PNG.bitblt( image, outputImage, 0, 0, image.width, image.height, 0, currentDeltaY ); spriteMap.push({ name: path.basename(imagePath, '.png'), rect: { x: 0, y: currentDeltaY * -1, width: image.width, height: image.height } }); currentDeltaY += image.height; }); const hash = crypto.Hash('md5'); hash.update(outputImage.data); const outputBasename = `sprite`; const outputName = `${outputBasename}-${hash.digest('hex')}.png`; const outbutBuffer = PNG.sync.write(outputImage, { colorType: 6 }); fs.writeFileSync( path.resolve(options.output, outputName), outbutBuffer ); const template = fs.readFileSync( path.resolve(__dirname, 'scss.mustache'), {encoding: 'utf-8'} ); const context = { bounds: bounds, images: spriteMap, url: path.join(options.urlPrefix, outputName) }; const scss = Mustache.render(template, context); const scssName = `${outputBasename}.scss`; fs.writeFileSync( path.resolve(options.output, scssName), scss ); return [outputName, scssName]; }; module.exports = { spritemaker: spritemaker };