Source: image/imagemagick.js

/**
 * @name imagemagick.js<lib/image>
 * @author Kei Funagayama <funagayama_kei@cyberagent.co.jp>
 * copyright (c) Cyberagent Inc.
 * @overview imagemagick library
 */

var path = require('path');

var Bucks = require('bucks');
var im = require('imagemagick');
var _ = require('lodash');

var logger = require('../logger');
var fsys = require('../fsys');
var constant = require('../constant');

var DEFAULT_SEPARATOR = constant.DEFAULT_SEPARATOR;

/**
 * @name imagemagick
 * @namespace imagemagick
 */
var functions = {};

_.each(im, function (val, idx) {
    functions[idx] = val;
});


/**
 * Get the size of the image
 *
 * @name getSize
 * @memberof imagemagick
 * @method
 * @param {String} file target file path
 * @param {function} callback
 * @return {Object} {width: {String}, height: {String}}
 * @example
 * beezlib.image.imagemagickgetSize('xxx.png', function(err, sizes) {
 *   console.log(size)
 * });
 * >> {width: xxx, heigth: xxx}
 *
 */
functions.getSize = function (file, callback) {
    return this.identify(['-format','%w,%h', file], function (err, res) {
        if (err) {
            return callback && callback(err, res);
        }

        var s = res.replace('\n', '').split(',');
        callback && callback(err, {
            width: s[0],
            height: s[1]
        });
    });
};

/**
 * Get the estimated quality
 *
 * @name getDefaultQuality
 * @memberof imagemagick
 * @method
 * @param {String} file target file path
 * @param {function} callback
 * @return {Number} quality
 * @example
 * beezlib.image.getDefaultQuality('xxx.jpg', function(err, quality) {
 *   console.log(quality)
 * });
 *
 */
functions.getDefaultQuality = function (file, callback) {
    return this.identify(['-verbose', '-format', '%Q', file], function (err, quality) {
        if (err) {
            return callback && callback(err);
        }

        callback && callback(null, parseInt(quality, 10) / 100);
    });
};

/**
 * Convert file format beez-ratio
 *
 * @name makeRatioFileNameSync
 * @memberof imagemagick
 * @method
 * @param {String} name src file name
 * @param {String} separator Separator ratio and file name
 * @param {Float} ratio example) 2.0
 * @return {String} file name
 * @example
 * var out = beezlib.image.imagemagick.makeRatioFileName('hoge.png', '-', 1.3);
 * console.log(out)
 * >> hoge_13.png
 */
functions.makeRatioFileNameSync = function (name, separator, ratio) {
    separator = separator || DEFAULT_SEPARATOR;
    var extname = path.extname(name);
    var basename = path.basename(name, extname);

    return basename + separator + (ratio * 10) + extname;
};

/**
 * File name is whether ratio format
 *
 * @name isRatioFileNameSync
 * @memberof imagemagick
 * @method
 * @param {String} filename target file name
 * @param {String} separator Separator ratio and file name
 * @return {boolean}
 * @example
 * var out = beezlib.image.imagemagickisRatioFileName('hoge-20.png', '-');
 * console.log(out)
 * >> true
 */
functions.isRatioFileNameSync = function (filename, separator) {
    separator = separator || DEFAULT_SEPARATOR;
    var extname = path.extname(filename);
    var basename = path.basename(filename, extname);
    var idx = basename.lastIndexOf(separator);
    var ratio = basename.substring(idx+1);

    if (ratio && parseInt(ratio, 10)) {
        return true;
    }
    return false;
};

/**
 * I converted to a ratio name to a different ratio format file name.
 *
 * @name changeRatioFileNameSync
 * @memberof imagemagick
 * @method
 * @param {String} filename target file name
 * @param {String} separator Separator ratio and file name
 * @param {Float} change ratio example) 2.0
 * @return {String} file name
 * @example
 * var out = beezlib.image.imagemagick.changeRatioFileName('hoge-20.png', null, 1.3);
 * console.log(out)
 * >> 'hoge-13.png'
 */
functions.changeRatioFileNameSync = function (filename, separator, change) {
    separator = separator || DEFAULT_SEPARATOR;
    var extname = path.extname(filename);
    var basename = path.basename(filename, extname);
    var idx = basename.lastIndexOf(separator);

    return basename.substring(0, idx) + separator + (change * 10) + extname;
};

/**
 * Resize ratio to the specified size, the image file. The file name format ratio
 *
 * @name ratioResize
 * @memberof imagemagick
 * @method
 * @param {Object} options imagemagick resize options.
 * @param {Float} baseRatio Base ratio of the original image resizing
 * @param {Array} outRatios Ratio you want to resize
 * @param {function} callback
 * @example
 * var baseRatio = 2.0;
 * var outRatios = [3.0, 2.0, 1.3, 1.0];
 * var options = {
 *   quality: 0.8,
 *   srcPath: './test/image/logo.png',
 *   dstPath: './test/image',
 *   //width: null,
 *   //height: null,
 *   strip: true,
 *   sharpening: 0.3
 * };
 *
 * var out = beezlib.image.imagemagick.ratioResize(options, baseRatio, outRatios, function (err, res) {});
 */
functions.ratioResize = function (options, baseRatio, outRatios, callback) {
    var self = this;
    var srcPath = options.srcPath;

    new Bucks()
    .waterfall([
        function (err, res, next) {
            self.getSize(srcPath, next);
        },
        function (err, res, next) {
            // do not get the default quality if quality option is set
            if (!!options.quality) {
                return next(null);
            }
            self.getDefaultQuality(srcPath, next);
        }
    ])
    .end(function (err, ress) {
        if (err) {
            return callback && callback(err);
        }

        var width = ress[0].width;
        var height = ress[0].height;
        var quality = ress[1];

        // parallel load
        var tasks = _.map(outRatios, function make(ratio) {
            var filename = path.basename(options.srcPath);

            return function task(err, res, next) {
                var dst = self.makeRatioFileNameSync(filename, options.separator, ratio);
                var dstPath = path.join(options.dstPath, dst);

                var rw = width * ratio / baseRatio;
                var rh = height * ratio / baseRatio;
                var resizeOptions = {
                    quality: options.quality || quality,
                    srcPath: options.srcPath,
                    dstPath: dstPath,
                    width: rw,
                    height: rh,
                    strip: options.strip || true,
                    sharpening: options.sharpening || 0.3
                };
                logger.debug('resize:', resizeOptions.srcPath, '->' , dstPath);
                self.resize(resizeOptions, function(err, res) {
                    next(err, dstPath);
                });
            };
        });

        new Bucks().parallel(tasks).then(function(result) {
            for (var i = 0; i < result.err.length; i++) {
                if (result.err[i]) {
                    return callback && callback(result.err[i]);
                }
            }
            callback && callback(null, result.res);
        }).end();
    });
};

module.exports = functions;