mirror of
https://gitee.com/doramart/DoraCMS.git
synced 2024-12-06 14:17:58 +08:00
301 lines
6.6 KiB
JavaScript
301 lines
6.6 KiB
JavaScript
|
/**
|
||
|
* Extend proto.
|
||
|
*/
|
||
|
|
||
|
module.exports = function (gm) {
|
||
|
|
||
|
var proto = gm.prototype;
|
||
|
|
||
|
/**
|
||
|
* `identify` states
|
||
|
*/
|
||
|
|
||
|
const IDENTIFYING = 1;
|
||
|
const IDENTIFIED = 2;
|
||
|
|
||
|
/**
|
||
|
* Map getter functions to output names.
|
||
|
*
|
||
|
* - format: specifying the -format argument (see man gm)
|
||
|
* - verbose: use -verbose instead of -format (only if necessary b/c its slow)
|
||
|
* - helper: use the conversion helper
|
||
|
*/
|
||
|
|
||
|
var map = {
|
||
|
'format': { key: 'format', format: '%m ', helper: 'Format' }
|
||
|
, 'depth': { key: 'depth', format: '%q' }
|
||
|
, 'filesize': { key: 'Filesize', format: '%b' }
|
||
|
, 'size': { key: 'size', format: '%wx%h ', helper: 'Geometry' }
|
||
|
, 'color': { key: 'color', format: '%k', helper: 'Colors' }
|
||
|
, 'orientation': { key: 'Orientation', verbose: true }
|
||
|
, 'res': { key: 'Resolution', verbose: true }
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Getter functions
|
||
|
*/
|
||
|
|
||
|
Object.keys(map).forEach(function (getter) {
|
||
|
proto[getter] = function (opts, callback) {
|
||
|
if (!callback) callback = opts, opts = {};
|
||
|
if (!callback) return this;
|
||
|
|
||
|
var val = map[getter]
|
||
|
, key = val.key
|
||
|
, self = this;
|
||
|
|
||
|
if (self.data[key]) {
|
||
|
callback.call(self, null, self.data[key]);
|
||
|
return self;
|
||
|
}
|
||
|
|
||
|
self.on(getter, callback);
|
||
|
|
||
|
self.bufferStream = !!opts.bufferStream;
|
||
|
|
||
|
if (val.verbose) {
|
||
|
self.identify(opts, function (err, stdout, stderr, cmd) {
|
||
|
if (err) {
|
||
|
self.emit(getter, err, self.data[key], stdout, stderr, cmd);
|
||
|
} else {
|
||
|
self.emit(getter, err, self.data[key]);
|
||
|
}
|
||
|
});
|
||
|
return self;
|
||
|
}
|
||
|
|
||
|
var args = makeArgs(self, val);
|
||
|
self._exec(args, function (err, stdout, stderr, cmd) {
|
||
|
if (err) {
|
||
|
self.emit(getter, err, self.data[key], stdout, stderr, cmd);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var result = (stdout||'').trim();
|
||
|
|
||
|
if (val.helper in helper) {
|
||
|
helper[val.helper](self.data, result);
|
||
|
} else {
|
||
|
self.data[key] = result;
|
||
|
}
|
||
|
|
||
|
self.emit(getter, err, self.data[key]);
|
||
|
});
|
||
|
|
||
|
return self;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
/**
|
||
|
* identify command
|
||
|
*
|
||
|
* Overwrites all internal data with the parsed output
|
||
|
* which is more accurate than the fast shortcut
|
||
|
* getters.
|
||
|
*/
|
||
|
|
||
|
proto.identify = function identify (opts, callback) {
|
||
|
// identify with pattern
|
||
|
if (typeof(opts) === 'string') {
|
||
|
opts = {
|
||
|
format: opts
|
||
|
}
|
||
|
}
|
||
|
if (!callback) callback = opts, opts = {};
|
||
|
if (!callback) return this;
|
||
|
if (opts && opts.format) return identifyPattern.call(this, opts, callback);
|
||
|
|
||
|
var self = this;
|
||
|
|
||
|
if (IDENTIFIED === self._identifyState) {
|
||
|
callback.call(self, null, self.data);
|
||
|
return self;
|
||
|
}
|
||
|
|
||
|
self.on('identify', callback);
|
||
|
|
||
|
if (IDENTIFYING === self._identifyState) {
|
||
|
return self;
|
||
|
}
|
||
|
|
||
|
self._identifyState = IDENTIFYING;
|
||
|
|
||
|
self.bufferStream = !!opts.bufferStream;
|
||
|
|
||
|
var args = makeArgs(self, { verbose: true });
|
||
|
|
||
|
self._exec(args, function (err, stdout, stderr, cmd) {
|
||
|
if (err) {
|
||
|
self.emit('identify', err, self.data, stdout, stderr, cmd);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
err = parse(stdout, self);
|
||
|
|
||
|
if (err) {
|
||
|
self.emit('identify', err, self.data, stdout, stderr, cmd);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
self.data.path = self.source;
|
||
|
|
||
|
self.emit('identify', null, self.data);
|
||
|
self._identifyState = IDENTIFIED;
|
||
|
});
|
||
|
|
||
|
return self;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* identify with pattern
|
||
|
*
|
||
|
* Execute `identify -format` with custom pattern
|
||
|
*/
|
||
|
|
||
|
function identifyPattern (opts, callback) {
|
||
|
var self = this;
|
||
|
|
||
|
self.bufferStream = !!opts.bufferStream;
|
||
|
|
||
|
var args = makeArgs(self, opts);
|
||
|
self._exec(args, function (err, stdout, stderr, cmd) {
|
||
|
if (err) {
|
||
|
return callback.call(self, err, undefined, stdout, stderr, cmd);
|
||
|
}
|
||
|
|
||
|
callback.call(self, err, (stdout||'').trim());
|
||
|
});
|
||
|
|
||
|
return self;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Parses `identify` responses.
|
||
|
*
|
||
|
* @param {String} stdout
|
||
|
* @param {Gm} self
|
||
|
* @return {Error} [optionally]
|
||
|
*/
|
||
|
|
||
|
function parse (stdout, self) {
|
||
|
// normalize
|
||
|
var parts = (stdout||"").trim().replace(/\r\n|\r/g, "\n").split("\n");
|
||
|
|
||
|
// skip the first line (its just the filename)
|
||
|
parts.shift();
|
||
|
|
||
|
try {
|
||
|
var len = parts.length
|
||
|
, rgx1 = /^( *)(.+?): (.*)$/ // key: val
|
||
|
, rgx2 = /^( *)(.+?):$/ // key: begin nested object
|
||
|
, out = { indent: {} }
|
||
|
, level = null
|
||
|
, lastkey
|
||
|
, i = 0
|
||
|
, res
|
||
|
, o
|
||
|
|
||
|
for (; i < len; ++i) {
|
||
|
res = rgx1.exec(parts[i]) || rgx2.exec(parts[i]);
|
||
|
if (!res) continue;
|
||
|
|
||
|
var indent = res[1].length
|
||
|
, key = res[2] ? res[2].trim() : '';
|
||
|
|
||
|
if ('Image' == key || 'Warning' == key) continue;
|
||
|
|
||
|
var val = res[3] ? res[3].trim() : null;
|
||
|
|
||
|
// first iteration?
|
||
|
if (null === level) {
|
||
|
level = indent;
|
||
|
o = out.root = out.indent[level] = self.data;
|
||
|
} else if (indent < level) {
|
||
|
// outdent
|
||
|
if (!(indent in out.indent)) {
|
||
|
continue;
|
||
|
}
|
||
|
o = out.indent[indent];
|
||
|
} else if (indent > level) {
|
||
|
// dropping into a nested object
|
||
|
out.indent[level] = o;
|
||
|
// wierd format, key/val pair with nested children. discard the val
|
||
|
o = o[lastkey] = {};
|
||
|
}
|
||
|
|
||
|
level = indent;
|
||
|
|
||
|
if (val) {
|
||
|
o[key] = val;
|
||
|
|
||
|
if (key in helper) {
|
||
|
helper[key](o, val);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
lastkey = key;
|
||
|
}
|
||
|
|
||
|
} catch (err) {
|
||
|
err.message = err.message + "\n\n Identify stdout:\n " + stdout;
|
||
|
return err;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create an argument array for the identify command.
|
||
|
*
|
||
|
* @param {gm} self
|
||
|
* @param {Object} val
|
||
|
* @return {Array}
|
||
|
*/
|
||
|
|
||
|
function makeArgs (self, val) {
|
||
|
var args = [
|
||
|
'identify'
|
||
|
, '-ping'
|
||
|
];
|
||
|
|
||
|
if (val.format) {
|
||
|
args.push('-format', val.format);
|
||
|
}
|
||
|
|
||
|
if (val.verbose) {
|
||
|
args.push('-verbose');
|
||
|
}
|
||
|
|
||
|
args = args.concat(self.src());
|
||
|
return args;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* identify -verbose helpers
|
||
|
*/
|
||
|
|
||
|
var helper = gm.identifyHelpers = {};
|
||
|
|
||
|
helper.Geometry = function Geometry (o, val) {
|
||
|
// We only want the size of the first frame.
|
||
|
// Each frame is separated by a space.
|
||
|
var split = val.split(" ").shift().split("x");
|
||
|
o.size = {
|
||
|
width: parseInt(split[0], 10)
|
||
|
, height: parseInt(split[1], 10)
|
||
|
}
|
||
|
};
|
||
|
|
||
|
helper.Format = function Format (o, val) {
|
||
|
o.format = val.split(" ")[0];
|
||
|
};
|
||
|
|
||
|
helper.Depth = function Depth (o, val) {
|
||
|
o.depth = parseInt(val, 10);
|
||
|
};
|
||
|
|
||
|
helper.Colors = function Colors (o, val) {
|
||
|
o.color = parseInt(val, 10);
|
||
|
};
|
||
|
}
|